Этот раздел описывает как обновиться с версии v0.5.x на v0.6.0 фреймворка RTIC.
Cargo.toml
- увеличьте версию
Измените версию cortex-m-rtic на "0.6.0".
mod
вместо const
С поддержкой атрибутов над модулями трюк с const APP теперь не нужен.
Измените
#![allow(unused)]
fn main() {
#[rtic::app(/* .. */)]
const APP: () = {
[код здесь]
};
}
на
#![allow(unused)]
fn main() {
#[rtic::app(/* .. */)]
mod app {
[код здесь]
}
}
Так как теперь используется обычный модуль Rust, это значит, что можно использовать обычный пользовательский код в этом модуле. Также жто значит, что use-выражения для ресурсов (и т.п.) могут понадобиться.
extern "C"
в аргументы app.
Измените
#![allow(unused)]
fn main() {
#[rtic::app(/* .. */)]
const APP: () = {
[код здесь]
// RTIC требует, чтобы неиспользуемые прерывания были задекларированы в блоке extern, когда
// используются программные задачи; эти свободные прерывания будут использованы для управления
// программными задачами.
extern "C" {
fn SSI0();
fn QEI0();
}
};
}
на
#![allow(unused)]
fn main() {
#[rtic::app(/* .. */, dispatchers = [SSI0, QEI0])]
mod app {
[код здесь]
}
}
Это работает и для ОЗУ-функций, см. examples/ramfunc.rs
С целью сделать API более симметричным задача #[init] всегда возвращает поздние ресурсы.
С этого:
#![allow(unused)]
fn main() {
#[rtic::app(device = lm3s6965)]
mod app {
#[init]
fn init(_: init::Context) {
rtic::pend(Interrupt::UART0);
}
// [еще код]
}
}
на это:
#![allow(unused)]
fn main() {
#[rtic::app(device = lm3s6965)]
mod app {
#[init]
fn init(_: init::Context) -> init::LateResources {
rtic::pend(Interrupt::UART0);
init::LateResources {}
}
// [еще код]
}
}
#[resources]
Ранее ресурсы RTIC должны были располагаться в структуре с именем "Resources":
#![allow(unused)]
fn main() {
struct Resources {
// Ресурсы определены здесь
}
}
В RTIC v0.6.0 структура ресурсов аннотируется также, как и #[task], #[init], #[idle]: атрибутом #[resources]
#![allow(unused)]
fn main() {
#[resources]
struct Resources {
// Ресурсы определены здесь
}
}
На самом деле, имя структуры предоставлено на усмотрение разработчика:
#![allow(unused)]
fn main() {
#[resources]
struct Whateveryouwant {
// Ресурсы определены здесь
}
}
будет работать так же хороршо.
С этой новой возвожностью, старый код, такой как:
#![allow(unused)]
fn main() {
#[task(spawn = [bar])]
fn foo(cx: foo::Context) {
cx.spawn.bar().unwrap();
}
#[task(schedule = [bar])]
fn bar(cx: bar::Context) {
cx.schedule.foo(/* ... */).unwrap();
}
}
Теперь будет выглядеть так:
#![allow(unused)]
fn main() {
#[task]
fn foo(_c: foo::Context) {
bar::spawn().unwrap();
}
#[task]
fn bar(_c: bar::Context) {
foo::schedule(/* ... */).unwrap();
}
}
Заметьте, что атрибуты spawn и schedule больше не нужны.
Теперь RTIC использует симметричные блокировки, это значит, что метод lock нужно использовать для всех доступов к ресурсам. Поскольку высокоприоритетные задачи имеют эксклюзивный доступ к ресурсу, в старом коде можно было следующее:
#![allow(unused)]
fn main() {
#[task(priority = 2, resources = [r])]
fn foo(cx: foo::Context) {
cx.resources.r = /* ... */;
}
#[task(resources = [r])]
fn bar(cx: bar::Context) {
cx.resources.r.lock(|r| r = /* ... */);
}
}
С симметричными блокировками нужно вызывать lock для обоих задач:
#![allow(unused)]
fn main() {
#[task(priority = 2, resources = [r])]
fn foo(cx: foo::Context) {
cx.resources.r.lock(|r| r = /* ... */);
}
#[task(resources = [r])]
fn bar(cx: bar::Context) {
cx.resources.r.lock(|r| r = /* ... */);
}
}
Заметьте, что скорость работы не изменяется благодаря оптимизациям LLVM, которые убирают ненужные блокировки.
Как программные, так и аппаратные задачи теперь можно определять вне модуля mod app. Ранее это было возможно только путем реализации обертки, вызывающей реализацию задачи.
Смотреть примеры examples/extern_binds.rs и examples/extern_spawn.rs.