Shared
A macro for safely sharing data between application and interrupt context on cortex-m systems
NOTE: This code is still an early work in progress!
```rust use nrf52832pac::Interrupt; use baremetal; use cortex_m;
// Tuples are of the format: // (VARIABLENAME, VARIABLETYPE, CORRESPONDINGINTERRUPT), shared!( (RADIOPKTS, usize, Interrupt::RADIO), (WALL_CLOCK, usize, Interrupt::RTC0), );
fn main() {
// Using a shared
data item in non-interrupt context
// requires a token. This is a singleton, sort of like
// the peripherals from a peripheral access crate
let mut token = RADIOPKTS::setinitial(27).unwrap();
// You access the data from within a closure. The interrupt
// this data is shared with is disabled for the duration of
// the closure. Other interrupts may still occur.
token.modify_app_context(|y| {
*y -= 1;
y
}).unwrap();
}
fn RADIO() {
// Within an interrupt, access is only granted if it matches
// the declared interrupt. Inside the RADIO
interrupt here,
// only RADIO_PKTS
is accessible.
//
// Access from within an interrupt doesn't require a token.
RADIOPKTS::modifyint_context(|x| {
*x += 1;
x
}).unwrap();
}
fn RTC0() {
// If set_initial
was never called, then all attempts to
// access will return an Err
. This code would panic at
// runtime!
BAZ::modifyintcontext(|x| {
*x += 1;
x
}).unwrap();
}
```
The following is the desired end goal of this project. We're not there yet.
```rust use nrf52832_pac::Interrupt; use something::Queue;
// Done at global scope, only specify types cmim!( Interrupt::RADIO: bool, Interrupt::TIMER0: u128, Interrupt::UARTE0_UARTE: Queue::Producer, );
fn main() { let (prod, cons) = Queue::new().split();
// Sets the value described in `cmim!()`.
// This is a "move" operation.
// If the interrupt is currently active, an Error is returned.
cmim_set!(
Interrupt::UARTE0_UARTE,
prod
).unwrap();
NVIC::enable(Interrupt::UARTE0_UARTE);
loop {
let _ = cons.pop();
// ..
}
}
fn UARTE0UARTE() {
// Gets a mutable reference to the value described in cmim!()
// This is a "borrow" operation.
// This checks the currently active interrupt. If Interrupt::UARTE0UARTE is not active, an error is returned
// There is no other mutex.
let data: &mut Producer = cmimget!(Interrupt::UARTE0UARTE).unwrap();
data.push(0x00);
}
```
```rust use nrf52832_pac::Interrupt;
// Done at global scope, only specify types cmim!( Interrupt::RADIO: bool, Interrupt::TIMER0: u128, Interrupt::UARTE0_UARTE: bbqueue::Producer, );
fn main() { // Same as above for setting, Interrupt must be disabled cmim_set!( Interrupt::RADIO, false ).unwrap();
NVIC::enable(Interrupt::RADIO);
loop {
// Access the data in a critical section. Radio is disabled during the closure
// This can only be called from non-interrupt context. If ANY interrupt is active,
// an error is returned. This prevents higher prio interrupts messing with the data
let data_copy = cmim_borrow!(
Interrupt::RADIO,
|data: &mut bool| {
// trigger some flag
*data = true;
}
).unwrap();
}
}
fn RADIO() {
// Gets a mutable reference to the value described in cmim!()
// This is a "borrow" operation.
// This checks the currently active interrupt. If Interrupt::RADIO is not active, an error is returned
// There is no other mutex.
let data: &mut bool = cmim_get!(Interrupt::RADIO).unwrap();
if *data {
// ...
}
} ```
I have no idea how to do this without the possibility of deadlock. Maybe specify multiple interrupts in cmim!()
, and critical section all of them at once? Maybe do priority elevation like RTFM?