Skip to content
This repository has been archived by the owner on Sep 5, 2022. It is now read-only.

Problems using irq in a real-world use case. #7

Closed
tstellanova opened this issue Apr 10, 2020 · 6 comments
Closed

Problems using irq in a real-world use case. #7

tstellanova opened this issue Apr 10, 2020 · 6 comments

Comments

@tstellanova
Copy link

I'm trying to figure out how to use this crate with interrupts, as you suggested it was useful for this case. Specifically, I'm trying to read and clear nrf52's GPIOTE interrupts. Following your example, I have:

// Hook into GPIOTE interrupt using the `#[interrupt]` attribute imported above.
scoped_interrupts! {
    enum Interrupt {
        GPIOTE,
    }

    use #[interrupt];
}

later in my main fn I do:

    let mut thingo = dp.GPIOTE;
    handler!(gpiote_int = || {
        let set_exti_pins = thingo.events_in[0].read().bits();
        thingo.events_in[0].reset();
        rprintln!("GPIOTE: {:?}", set_exti_pins);
    });

    scope(|scope| {
        scope.register(Interrupt::GPIOTE, gpiote_int);

I get a build error:

error[E0277]: `*const ()` cannot be shared between threads safely
   --> src/main.rs:233:9
    |
233 | /         handler!(gpiote_int = || {
234 | |             let set_exti_pins = thingo.GPIOTE.events_in[0].read().bits();
235 | |             thingo.GPIOTE.events_in[0].reset();
236 | |             rprintln!("GPIOTE: {:?}", set_exti_pins);
237 | |         });
    | |___________^ `*const ()` cannot be shared between threads safely

I think I'm missing something here. Do I need to enable:
#![feature(const_fn)] ? I'm currently using stable.

Thanks for any help.

@jonas-schievink
Copy link
Owner

The data captured by the interrupt handler has to be thread-safe, which is expressed with the Send bound on Handler::new.

Is that the entire error message you got? It should normally tell you what contains the *const ().

@tstellanova
Copy link
Author

The full error is this:


error[E0277]: `*const ()` cannot be shared between threads safely
   --> src/main.rs:229:5
    |
229 | /     handler!(gpiote_int = || {
230 | |         let set_exti_pins = thingo.events_in[0].read().bits();
231 | |         thingo.events_in[0].reset();
232 | |         rprintln!("GPIOTE: {:?}", set_exti_pins);
233 | |     });
    | |_______^ `*const ()` cannot be shared between threads safely
    | 
   ::: /Users/todd/.cargo/registry/src/github.com-1ecc6299db9ec823/irq-0.2.3/src/lib.rs:304:22
    |
304 |           F: FnMut() + Send + 'a,
    |                        ---- required by this bound in `irq::Handler::<'a>::new`
    |
    = help: within `nrf52832_pac::GPIOTE`, the trait `core::marker::Sync` is not implemented for `*const ()`
    = note: required because it appears within the type `core::marker::PhantomData<*const ()>`
    = note: required because it appears within the type `nrf52832_pac::GPIOTE`
    = note: required because of the requirements on the impl of `core::marker::Send` for `&nrf52832_pac::GPIOTE`
    = note: required because it appears within the type `[closure@src/main.rs:229:27: 233:6 thingo:&nrf52832_pac::GPIOTE]`
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

I'll try a couple more variations on using the device peripherals (let mut dp = pac::Peripherals::take().unwrap();) from within that scope

@jonas-schievink
Copy link
Owner

Try a move closure

@tstellanova
Copy link
Author

That builds, but the run result is not what I would expect:

    let mut ext_pin_bits: u32 = 0;
    let mut thingo = dp.GPIOTE;
    handler!(gpiote_int = move || {
        ext_pin_bits = thingo.events_in[0].read().bits();
        thingo.events_in[0].reset();
        rprintln!("GPIOTE: {:?}", ext_pin_bits);
    });

    scope(|scope| {
        scope.register(Interrupt::GPIOTE, gpiote_int);
        let mut idle_count = 0;
        loop {
            rprintln!("ext_pin_bits {} ", ext_pin_bits);
            cortex_m::asm::wfi();

The output is something like this:

00> GPIOTE: 1
00> AWAKE!
00> ext_pin_bits 0 

In other words, ext_pin_bits modification is not visible in the scope.
What I'm trying to do is share state between the interrupt handler and the main event loop-- eg which GPIOTE external interrupt pin triggered the event handler.

@jonas-schievink
Copy link
Owner

Ah, now I see what you want to do.

The normal rules of Rust closures apply here: You can either capture something by value, by mutable reference, or by immutable reference. Since registered handlers can interrupt each other, as well as the main loop in the scope (encoded by the Send requirement on the closures), to safely share data between contexts you need to use a mutex, atomics, or other datastructures that support thread-safe interior mutability (like those provided by the heapless crate).

Registering an interrupt handler with irq is basically the same thing as spawning a thread in libstd land (except that irq provides a scoped-interrupts API, so handlers don't have to be 'static). That means that sharing data between them has basically the same constraints and solutions.

@tstellanova
Copy link
Author

Thanks for the explanation-- that makes sense!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants