Выбрать главу

Рассмотрим такую программу:

#![allow(unused)]

fn main() {

#[rtic::app(device = ..)]

mod app {

struct Resources {

#[init(0)]

x: u64,

#[init(0)]

y: u64,

}

#[init]

fn init() {

rtic::pend(Interrupt::UART0);

}

#[interrupt(binds = UART0, priority = 1, resources = [x, y])]

fn foo(c: foo::Context) {

let mut x = c.resources.x;

let mut y = c.resources.y;

y.lock(|y| {

*y += 1;

*x.lock(|x| {

x += 1;

});

*y += 1;

});

// середина

x.lock(|x| {

*x += 1;

y.lock(|y| {

*y += 1;

});

*x += 1;

})

}

#[interrupt(binds = UART1, priority = 2, resources = [x])]

fn bar(c: foo::Context) {

// ..

}

#[interrupt(binds = UART2, priority = 3, resources = [y])]

fn baz(c: foo::Context) {

// ..

}

// ..

}

}

Код, сгенерированный фреймворком, выглядит так:

#![allow(unused)]

fn main() {

// опущено: пользовательский код

pub mod resources {

pub struct x<'a> {

priority: &'a Cell<u8>,

}

impl<'a> x<'a> {

pub unsafe fn new(priority: &'a Cell<u8>) -> Self {

x { priority }

}

pub unsafe fn priority(&self) -> &Cell<u8> {

self.priority

}

}

// repeat for `y`

}

pub mod foo {

pub struct Context {

pub resources: Resources,

// ..

}

pub struct Resources<'a> {

pub x: resources::x<'a>,

pub y: resources::y<'a>,

}

}

mod app {

use cortex_m::register::basepri;

#[no_mangle]

unsafe fn UART1() {

// статический приоритет прерывания (определено пользователем)

const PRIORITY: u8 = 2;

// сделать снимок BASEPRI

let initial = basepri::read();

let priority = Celclass="underline" :new(PRIORITY);

bar(bar::Context {

resources: bar::Resources::new(&priority),

// ..

});

// вернуть BASEPRI значение из снимка, сделанного ранее

basepri::write(initial); // то же, что и `asm!` блок, виденный ранее

}

// так же для `UART0` / `foo` и `UART2` / `baz`

impl<'a> rtic::Mutex for resources::x<'a> {

type T = u64;

fn lock<R>(&mut self, f: impl FnOnce(&mut u64) -> R) -> R {

unsafe {

// определение максимального приоритет ресурса

const CEILING: u8 = 2;

let current = self.priority().get();

if current < CEILING {

// увеличить динамический приоритет

self.priority().set(CEILING);

basepri::write(logical2hw(CEILING));

let r = f(&mut y);

// восстановить динамический приоритет

basepri::write(logical2hw(current));

self.priority().set(current);

r

} else {

// динамический приоритет достаточно высок

f(&mut y)

}

}

}

}

// повторить для ресурса `y`

}

}

Наконец, компилятор оптимизирует функцию foo во что-то наподобие такого:

#![allow(unused)]

fn main() {

fn foo(c: foo::Context) {

// ПРИМЕЧАНИЕ: BASEPRI содержит значение `0` (значение сброса) в этот момент

// увеличить динамический приоритет до `3`

unsafe { basepri::write(160) }

// две операции над `y` объединены в одну

y += 2;

// BASEPRI не изменяется для доступа к `x`, потому что динамический приоритет достаточно высок

x += 1;

// уменьшить (восстановить) динамический приоритет до `1`

unsafe { basepri::write(224) }