8.2 KiB
Советы и хитрости
Полные примеры для RTIC смотрите в репозитарии rtic-examples.
Обобщенное программирование (Generics)
Все объекты, предоставляющие ресурысы реализуют трейт rtic::Mutex
.
Если ресурс не реализует его, можно обернуть его в новый тип rtic::Exclusive
,
который реализует трейт Mutex
. С помощью этого нового типа
можно написать обобщенную функцию, которая работает с обобщенным ресурсом и
вызывать его из различных задач, чтобы производить однотипные операции над
похожим множеством ресурсов.
Вот один такой пример:
{{#include ../../../../examples/generics.rs}}
$ cargo run --example generics
{{#include ../../../../ci/expected/generics.run}}
Условная компиляция
Вы можете использовать условную компиляцию (#[cfg]
) на ресурсах (полях структуры
#[resources] struct Resources
) и задачах (элементах fn
).
Эффект использования атрибутов #[cfg]
в том, что ресурс/ задача
будут не доступны в соответствующих структурах Context
если условие не выполняется.
В примере ниже выводится сообщение каждый раз, когда вызывается задача foo
, но только
если программы скомпилирова с профилем dev
.
{{#include ../../../../examples/cfg.rs}}
$ cargo run --example cfg --release
$ cargo run --example cfg
{{#include ../../../../ci/expected/cfg.run}}
Запуск задач из ОЗУ
Главной целью переноса описания программы на RTIC в атрибуты в
RTIC v0.4.x была возможность взаимодействия с другими атрибутами.
Напримерe, атрибут link_section
можно применять к задачам, чтобы разместить
их в ОЗУ; это может улучшить производительность в некоторых случаях.
ВАЖНО: Обычно атрибуты
link_section
,export_name
иno_mangle
очень мощные, но их легко использовать неправильно. Неверное использование любого из этих атрибутов может вызвать неопределенное поведение; Вам следует всегда предпочитать использование безопасных, высокоуровневых атрибутов вместо них, таких как атрибутыinterrupt
иexception
изcortex-m-rt
.В особых функций, размещаемых в ОЗУ нет безопасной абстракции в
cortex-m-rt
v0.6.5 но создано RFC для добавления атрибутаramfunc
в будущем релизе.
В примере ниже показано как разместить высокоприоритетную задачу bar
в ОЗУ.
{{#include ../../../../examples/ramfunc.rs}}
Запуск этой программы создаст ожидаемый вывод.
$ cargo run --example ramfunc
{{#include ../../../../ci/expected/ramfunc.run}}
Можно посмотреть на вывод cargo-nm
, чтобы убедиться, что bar
расположен в ОЗУ
(0x2000_0000
), тогда как foo
расположен во Flash (0x0000_0000
).
$ cargo nm --example ramfunc --release | grep ' foo::'
{{#include ../../../../ci/expected/ramfunc.run.grep.foo}}
$ cargo nm --example ramfunc --release | grep ' bar::'
{{#include ../../../../ci/expected/ramfunc.run.grep.bar}}
Обходной путь для быстрой передачи сообщений
Передача сообщений всегда вызывает копирование от отправителя в
статическую переменную, а затем из статической переменной получателю.
Таким образом, при передаче большого буфера, например [u8; 128]
, передача сообщения
вызывает два дорогих вызова memcpy
. Чтобы минимизировать накладные расходы на передачу
сообщения, можно использовать обходной путь: вместо передачи буфера по значению,
можно передавать владеющий указатель на буфер.
Можно использовать глобальный аллокатор, чтобы реализовать данный трюк (alloc::Box
,
alloc::Rc
, и т.п.), либо использовать статически аллоцируемый пул памяти, например heapless::Pool
.
Здесь приведен пример использования heapless::Pool
для "упаковки" буфера из 128 байт.
{{#include ../../../../examples/pool.rs}}
$ cargo run --example pool
{{#include ../../../../ci/expected/pool.run}}
Инспектирование раскрываемого кода
#[rtic::app]
- это процедурный макрос, который создает код.
Если по какой-то причине вам нужно увидеть код, сгенерированный этим макросом,
у вас есть два пути:
Вы можете изучить файл rtic-expansion.rs
внутри папки target
. Этот файл
содержит элемент #[rtic::app]
в раскрытом виде (не всю вашу программу!)
из последней сборки (с помощью cargo build
или cargo check
) RTIC программы.
Раскрытый код не отформатирован по-умолчанию, но вы можете запустить rustfmt
на нем перед тем, как читать.
$ cargo build --example foo
$ rustfmt target/rtic-expansion.rs
$ tail target/rtic-expansion.rs
#[doc = r" Implementation details"]
mod app {
#[doc = r" Always include the device crate which contains the vector table"]
use lm3s6965 as _;
#[no_mangle]
unsafe extern "C" fn main() -> ! {
rtic::export::interrupt::disable();
let mut core: rtic::export::Peripherals = core::mem::transmute(());
core.SCB.scr.modify(|r| r | 1 << 1);
rtic::export::interrupt::enable();
loop {
rtic::export::wfi()
}
}
}
Или, вы можете использовать подкоманду cargo-expand
. Она раскроет
все макросы, включая атрибут #[rtic::app]
, и модули в вашем крейте и
напечатает вывод в консоль.
$ # создаст такой же вывод, как выше
$ cargo expand --example smallest | tail
Деструктуризация ресурса
Если задача требует нескольких ресурсов, разбиение структуры ресурсов может улучшить читабельность. Вот два примера того, как это можно сделать:
{{#include ../../../../examples/destructure.rs}}