Советы и хитрости
Обобщенное программирование (Generics)
Ресурсы, совместно используемые двумя или более задачами, реализуют трейт Mutex
во всех контекстах, даже в тех, где для доступа к данным не требуются
критические секции. Это позволяет легко писать обобщенный код оперирующий
ресурсами, который можно вызывать из различных задач. Вот такой пример:
# #![allow(unused_variables)] #fn main() { //! examples/generics.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; use lm3s6965::Interrupt; use rtfm::Mutex; #[rtfm::app(device = lm3s6965)] const APP: () = { static mut SHARED: u32 = 0; #[init] fn init(_: init::Context) { rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART1); } #[interrupt(resources = [SHARED])] fn UART0(c: UART0::Context) { static mut STATE: u32 = 0; hprintln!("UART0(STATE = {})", *STATE).unwrap(); advance(STATE, c.resources.SHARED); rtfm::pend(Interrupt::UART1); debug::exit(debug::EXIT_SUCCESS); } #[interrupt(priority = 2, resources = [SHARED])] fn UART1(mut c: UART1::Context) { static mut STATE: u32 = 0; hprintln!("UART1(STATE = {})", *STATE).unwrap(); // just to show that `SHARED` can be accessed directly and .. *c.resources.SHARED += 0; // .. also through a (no-op) `lock` c.resources.SHARED.lock(|shared| *shared += 0); advance(STATE, c.resources.SHARED); } }; fn advance(state: &mut u32, mut shared: impl Mutex<T = u32>) { *state += 1; let (old, new) = shared.lock(|shared| { let old = *shared; *shared += *state; (old, *shared) }); hprintln!("SHARED: {} -> {}", old, new).unwrap(); } #}
$ cargo run --example generics
UART1(STATE = 0)
SHARED: 0 -> 1
UART0(STATE = 0)
SHARED: 1 -> 2
UART1(STATE = 1)
SHARED: 2 -> 4
Это также позволяет Вам изменять статические приоритеты задач без
переписывания кода. Если Вы единообразно используете lock
-и для доступа
к данным в разделяемых ресурсах, тогда Ваш код продолжит компилироваться,
когда Вы измените приоритет задач.
Запуск задач из ОЗУ
Главной целью переноса описания программы на RTFM в атрибуты в
RTFM v0.4.x была возможность взаимодействия с другими атрибутами.
Напримерe, атрибут link_section
можно применять к задачам, чтобы разместить
их в ОЗУ; это может улучшить производительность в некоторых случаях.
ВАЖНО: Обычно атрибуты
link_section
,export_name
иno_mangle
очень мощные, но их легко использовать неправильно. Неверное использование любого из этих атрибутов может вызвать неопределенное поведение; Вам следует всегда предпочитать использование безопасных, высокоуровневых атрибутов вокруг них, таких как атрибутыinterrupt
иexception
изcortex-m-rt
.В особых случаях функций RAM нет безопасной абстракции в
cortex-m-rt
v0.6.5 но создано RFC для добавления атрибутаramfunc
в будущем релизе.
В примере ниже показано как разместить высокоприоритетную задачу bar
в ОЗУ.
# #![allow(unused_variables)] #fn main() { //! examples/ramfunc.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; #[rtfm::app(device = lm3s6965)] const APP: () = { #[init(spawn = [bar])] fn init(c: init::Context) { c.spawn.bar().unwrap(); } #[inline(never)] #[task] fn foo(_: foo::Context) { hprintln!("foo").unwrap(); debug::exit(debug::EXIT_SUCCESS); } // run this task from RAM #[inline(never)] #[link_section = ".data.bar"] #[task(priority = 2, spawn = [foo])] fn bar(c: bar::Context) { c.spawn.foo().unwrap(); } extern "C" { fn UART0(); // run the task dispatcher from RAM #[link_section = ".data.UART1"] fn UART1(); } }; #}
Запуск этой программы произведет ожидаемый вывод.
$ cargo run --example ramfunc
foo
Можно посмотреть на вывод cargo-nm
, чтобы убедиться, что bar
расположен в ОЗУ
(0x2000_0000
), тогда как foo
расположен во Flash (0x0000_0000
).
$ cargo nm --example ramfunc --release | grep ' foo::'
20000100 B foo::FREE_QUEUE::ujkptet2nfdw5t20
200000dc B foo::INPUTS::thvubs85b91dg365
000002c6 T foo::sidaht420cg1mcm8
$ cargo nm --example ramfunc --release | grep ' bar::'
20000100 B bar::FREE_QUEUE::lk14244m263eivix
200000dc B bar::INPUTS::mi89534s44r1mnj1
20000000 T bar::ns9009yhw2dc2y25
binds
ПРИМЕЧАНИЕ: Требуется RTFM не ниже 0.4.2
Вы можете давать аппаратным задачам имена похожие на имена обычных задач.
Для этого нужно использовать аргумент binds
: Вы называете функцию
по своему желанию и назначаете ей прерывание / исключение
через аргумент binds
. Spawn
и другие служебные типы будут размещены в модуле,
названном в соответствии с названием функции, а не прерывания / исключения.
Давайте посмотрим пример:
# #![allow(unused_variables)] #fn main() { //! examples/binds.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; use lm3s6965::Interrupt; // `examples/interrupt.rs` rewritten to use `binds` #[rtfm::app(device = lm3s6965)] const APP: () = { #[init] fn init(_: init::Context) { rtfm::pend(Interrupt::UART0); hprintln!("init").unwrap(); } #[idle] fn idle(_: idle::Context) -> ! { hprintln!("idle").unwrap(); rtfm::pend(Interrupt::UART0); debug::exit(debug::EXIT_SUCCESS); loop {} } #[interrupt(binds = UART0)] fn foo(_: foo::Context) { static mut TIMES: u32 = 0; *TIMES += 1; hprintln!( "foo called {} time{}", *TIMES, if *TIMES > 1 { "s" } else { "" } ) .unwrap(); } }; #}
$ cargo run --example binds
init
foo called 1 time
idle
foo called 2 times