3 个版本
0.1.2 | 2019 年 4 月 2 日 |
---|---|
0.1.1 | 2019 年 4 月 2 日 |
0.1.0 | 2019 年 4 月 2 日 |
#1968 在 嵌入式开发
16KB
110 行
Shared
用于在 cortex-m 系统上安全地在应用程序和中断上下文之间共享数据的宏
注意:此代码仍处于早期开发阶段!
目前看起来是什么样子
use nrf52832_pac::Interrupt;
use bare_metal;
use cortex_m;
// Tuples are of the format:
// (VARIABLE_NAME, VARIABLE_TYPE, CORRESPONDING_INTERRUPT),
shared!(
(RADIO_PKTS, usize, Interrupt::RADIO),
(WALL_CLOCK, usize, Interrupt::RTC0),
);
#[entry]
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 = RADIO_PKTS::set_initial(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();
}
#[interrupt]
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.
RADIO_PKTS::modify_int_context(|x| {
*x += 1;
x
}).unwrap();
}
#[interrupt]
fn RTC0() {
// If `set_initial` was never called, then all attempts to
// access will return an `Err`. This code would panic at
// runtime!
BAZ::modify_int_context(|x| {
*x += 1;
x
}).unwrap();
}
原始想法
以下是这个项目的预期最终目标。我们还没有达到那里。
阶段 1 - 将数据移动到中断
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();
// ..
}
}
#[interrupt]
fn UARTE0_UARTE() {
// Gets a mutable reference to the value described in `cmim!()`
// This is a "borrow" operation.
// This checks the currently active interrupt. If Interrupt::UARTE0_UARTE is not active, an error is returned
// There is no other mutex.
let data: &mut Producer = cmim_get!(Interrupt::UARTE0_UARTE).unwrap();
data.push(0x00);
}
阶段 2 - 与单个中断共享数据
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();
}
}
#[interrupt]
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 {
// ...
}
}
阶段 3 - 在中断之间共享数据
我不知道如何在没有死锁可能性的情况下做这件事。也许在 cmim!()
中指定多个中断,并且同时所有它们的临界区?也许像 RTFM 一样提升优先级?
依赖关系
~1MB
~12K SLoC