#interrupt #shared-data #context #applications #sharing #cortex-m #systems

shared

用于在 cortex-m 系统上安全地在应用程序和中断上下文之间共享数据的宏

3 个版本

0.1.2 2019 年 4 月 2 日
0.1.1 2019 年 4 月 2 日
0.1.0 2019 年 4 月 2 日

#1968嵌入式开发

MIT/Apache

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