[−][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 |