[][src]Crate cmim

Cortex-M Interrupt Move

It's the next best thing to moving to interrupt context.

Examples

Check out the full examples in the app-examples folder.

The goal

The goal here is to replace usage of a mutex which may require an entire critical section, and instead model "Moving" of data to an interrupt context.

This means that we don't need a critical section to access it, we just need to be in the interrupt we moved the data to.

Here's how it works:

use nrf52832_hal::nrf52832_pac::{self, interrupt, Interrupt, NVIC};
use cortex_m::asm::wfi;
use cortex_m_rt::entry;
use cmim::{Move};

// Define your global variables, and what
// interrupt they are allowed to be used from

// These variables are initialized at runtime
static FOO: Move<Foo, Interrupt> = Move::new_uninitialized(Interrupt::UARTE0_UART0);
static BAR: Move<Bar, Interrupt> = Move::new_uninitialized(Interrupt::UARTE0_UART0);

// These variables are const initialized. This probably isn't super useful vs just
// having a static variable inside the function, but would allow you to later send
// new data to the interrupt.
static BAZ: Move<u64, Interrupt> = Move::new(123u64, Interrupt::TIMER0);

#[entry]
fn main() -> ! {
    let periphs = nrf52832_pac::CorePeripherals::take().unwrap();

    let mut nvic = periphs.NVIC;

    // Data *MUST* be initialized from non-interrupt context. A critical
    // section will be used when initializing the data.
    //
    // Since this data has never been initialized before, these will return Ok(None).
    assert!(FOO.try_move(Foo::default()).unwrap().is_none());
    assert!(BAR.try_move(Bar::with_settings(123, 456)).unwrap().is_none());

    // Since this data WAS initialized, we will get the old data back as Ok(Some(T))
    assert_eq!(BAZ.try_move(456u64), Ok(Some(123u64)));

    // Now you can enable the interrupts
    unsafe {
        NVIC::unmask(Interrupt::UARTE0_UART0);
        NVIC::unmask(Interrupt::TIMER0);
    }

    loop {
        wfi();
    }
}

#[interrupt]
fn UARTE0_UART0() {
    // You're allowed to access any data you've
    // "given" to the interrupt. You'll get a
    // mutable reference to your data inside of
    // a closure.
    //
    // You can either stack closures like this, or
    // just use a single struct containing all data.
    //
    // You will only get an error if:
    //
    // 1. You try to lock the same data multiple times
    // 2. You try to lock the data from the wrong interrupt
    // 3. You never initialized the data
    //
    // If you avoid these three things, it should always be
    // safe to unwrap the return values
    FOO.try_lock(|foo| {
        BAR.try_lock(|bar| {
            uart0_inner(foo, bar);
        }).unwrap();
    }).unwrap();
}

fn uart0_inner(foo: &mut Foo, bar: &mut Bar) {
    foo.some_foo_method();
    bar.some_bar_method();
}

#[interrupt]
fn TIMER0() {
    BAZ.try_lock(|baz| {
        // Do something with baz...
    }).unwrap();

    // This doesn't work, and will panic at
    // runtime because it is the wrong interrupt
    //
    // FOO.try_lock(|foo| {
    //     // Do something with foo...
    // }).unwrap();
}

fn not_an_interrupt() {
    // This doesn't work, and will panic at
    // runtime because we're not in an interrupt
    //
    // FOO.try_lock(|foo| {
    //     // Do something with foo...
    // }).unwrap();
}

License

Licensed under either of

  • Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)

  • MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Structs

Move

Move is a structure that is intended to be stored as a static variable, and represents a metaphorical "move" to an interrupt context. Data is moved to the interrupt context by calling try_move from thread (non-interrupt) context, and the data can be retrived within a selected interrupt using the try_lock method.