Советы и хитрости
Обобщенное программирование (Generics)
Ресурсы, совместно используемые двумя или более задачами, реализуют трейт Mutex
во всех контекстах, даже в тех, где для доступа к данным не требуются
критические секции. Это позволяет легко писать обобщенный код оперирующий
ресурсами, который можно вызывать из различных задач. Вот такой пример:
#![allow(unused)] 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::{app, Mutex}; #[app(device = lm3s6965)] const APP: () = { static mut SHARED: u32 = 0; #[init] fn init() { rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART1); } #[interrupt(resources = [SHARED])] fn UART0() { static mut STATE: u32 = 0; hprintln!("UART0(STATE = {})", *STATE).unwrap(); advance(STATE, resources.SHARED); rtfm::pend(Interrupt::UART1); debug::exit(debug::EXIT_SUCCESS); } #[interrupt(priority = 2, resources = [SHARED])] fn UART1() { static mut STATE: u32 = 0; hprintln!("UART1(STATE = {})", *STATE).unwrap(); // just to show that `SHARED` can be accessed directly and .. *resources.SHARED += 0; // .. also through a (no-op) `lock` resources.SHARED.lock(|shared| *shared += 0); advance(STATE, 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` в будущем релизе.
[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100
В примере ниже показано как разместить высокоприоритетную задачу `bar` в ОЗУ.
``` rust
//! examples/ramfunc.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [bar])]
fn init() {
spawn.bar().unwrap();
}
#[inline(never)]
#[task]
fn foo() {
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() {
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`).
``` console
$ cargo nm --example ramfunc --release | grep ' foo::'
20000100 B foo::FREE_QUEUE::ujkptet2nfdw5t20
200000dc B foo::INPUTS::thvubs85b91dg365
000002c6 T foo::sidaht420cg1mcm8```
``` console
$ 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` и другие служебные типы будут размещены в модуле,
названном в соответствии с названием функции, а не прерывания / исключения.
Давайте посмотрим пример:
``` rust
//! 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;
use rtfm::app;
// `examples/interrupt.rs` rewritten to use `binds`
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init() {
rtfm::pend(Interrupt::UART0);
hprintln!("init").unwrap();
}
#[idle]
fn idle() -> ! {
hprintln!("idle").unwrap();
rtfm::pend(Interrupt::UART0);
debug::exit(debug::EXIT_SUCCESS);
loop {}
}
#[interrupt(binds = UART0)]
fn foo() {
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```