rtic/book/ru/src/by-example/tips.md

176 lines
8.2 KiB
Markdown
Raw Normal View History

2019-02-08 21:18:51 +01:00
# Советы и хитрости
2021-08-03 21:40:33 +02:00
Полные примеры для RTIC смотрите в репозитарии [rtic-examples][rtic-examples].
[rtic-examples]: https://github.com/rtic-rs/rtic-examples
2019-02-09 06:48:12 +01:00
## Обобщенное программирование (Generics)
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
Все объекты, предоставляющие ресурысы реализуют трейт `rtic::Mutex`.
Если ресурс не реализует его, можно обернуть его в новый тип [`rtic::Exclusive`],
который реализует трейт `Mutex`. С помощью этого нового типа
можно написать обобщенную функцию, которая работает с обобщенным ресурсом и
вызывать его из различных задач, чтобы производить однотипные операции над
похожим множеством ресурсов.
Вот один такой пример:
[`rtic::Exclusive`]: ../../../api/rtic/struct.Exclusive.html
2019-02-08 21:18:51 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/generics.rs}}
2019-02-08 21:18:51 +01:00
```
``` console
$ cargo run --example generics
2021-04-04 07:15:13 +02:00
{{#include ../../../../ci/expected/generics.run}}
```
## Условная компиляция
Вы можете использовать условную компиляцию (`#[cfg]`) на ресурсах (полях структуры
`#[resources] struct Resources`) и задачах (элементах `fn`).
Эффект использования атрибутов `#[cfg]` в том, что ресурс/ задача
будут *не* доступны в соответствующих структурах `Context` если условие не выполняется.
В примере ниже выводится сообщение каждый раз, когда вызывается задача `foo`, но только
если программы скомпилирова с профилем `dev`.
``` rust
{{#include ../../../../examples/cfg.rs}}
```
``` console
$ cargo run --example cfg --release
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
$ cargo run --example cfg
{{#include ../../../../ci/expected/cfg.run}}
```
2019-02-08 21:18:51 +01:00
2019-02-09 06:48:12 +01:00
## Запуск задач из ОЗУ
2019-02-08 21:18:51 +01:00
2020-06-11 19:18:29 +02:00
Главной целью переноса описания программы на RTIC в атрибуты в
RTIC v0.4.x была возможность взаимодействия с другими атрибутами.
2019-02-08 21:18:51 +01:00
Напримерe, атрибут `link_section` можно применять к задачам, чтобы разместить
2019-02-09 06:48:12 +01:00
их в ОЗУ; это может улучшить производительность в некоторых случаях.
2019-02-08 21:18:51 +01:00
> **ВАЖНО**: Обычно атрибуты `link_section`, `export_name` и `no_mangle`
> очень мощные, но их легко использовать неправильно. Неверное использование
> любого из этих атрибутов может вызвать неопределенное поведение;
> Вам следует всегда предпочитать использование безопасных, высокоуровневых
2021-04-04 07:15:13 +02:00
> атрибутов вместо них, таких как атрибуты `interrupt` и `exception`
2019-02-08 21:18:51 +01:00
> из `cortex-m-rt`.
>
2021-04-04 07:15:13 +02:00
> В особых функций, размещаемых в ОЗУ нет безопасной абстракции в `cortex-m-rt`
2019-02-08 21:18:51 +01:00
> v0.6.5 но создано [RFC] для добавления атрибута `ramfunc` в будущем релизе.
[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100
2019-02-09 06:48:12 +01:00
В примере ниже показано как разместить высокоприоритетную задачу `bar` в ОЗУ.
2019-02-08 21:18:51 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/ramfunc.rs}}
2019-02-08 21:18:51 +01:00
```
2021-04-04 07:15:13 +02:00
Запуск этой программы создаст ожидаемый вывод.
2019-02-08 21:18:51 +01:00
``` console
$ cargo run --example ramfunc
2021-04-04 07:15:13 +02:00
{{#include ../../../../ci/expected/ramfunc.run}}
```
2019-02-08 21:18:51 +01:00
2019-02-09 06:48:12 +01:00
Можно посмотреть на вывод `cargo-nm`, чтобы убедиться, что `bar` расположен в ОЗУ
2019-02-08 21:18:51 +01:00
(`0x2000_0000`), тогда как `foo` расположен во Flash (`0x0000_0000`).
``` console
$ cargo nm --example ramfunc --release | grep ' foo::'
2022-02-09 17:59:00 +01:00
{{#include ../../../../ci/expected/ramfunc.run.grep.foo}}
2021-04-04 07:15:13 +02:00
```
2019-02-08 21:18:51 +01:00
``` console
$ cargo nm --example ramfunc --release | grep ' bar::'
2022-02-09 17:59:00 +01:00
{{#include ../../../../ci/expected/ramfunc.run.grep.bar}}
2021-04-04 07:15:13 +02:00
```
## Обходной путь для быстрой передачи сообщений
2019-02-24 05:38:21 +01:00
2021-04-04 07:15:13 +02:00
Передача сообщений всегда вызывает копирование от отправителя в
статическую переменную, а затем из статической переменной получателю.
Таким образом, при передаче большого буфера, например `[u8; 128]`, передача сообщения
вызывает два дорогих вызова `memcpy`. Чтобы минимизировать накладные расходы на передачу
сообщения, можно использовать обходной путь: вместо передачи буфера по значению,
можно передавать владеющий указатель на буфер.
2019-02-24 05:38:21 +01:00
2021-04-04 07:15:13 +02:00
Можно использовать глобальный аллокатор, чтобы реализовать данный трюк (`alloc::Box`,
`alloc::Rc`, и т.п.), либо использовать статически аллоцируемый пул памяти, например [`heapless::Pool`].
2019-02-24 05:38:21 +01:00
2021-04-04 07:15:13 +02:00
[`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html
Здесь приведен пример использования `heapless::Pool` для "упаковки" буфера из 128 байт.
2019-02-24 05:38:21 +01:00
``` rust
2021-04-04 07:15:13 +02:00
{{#include ../../../../examples/pool.rs}}
2019-02-24 05:38:21 +01:00
```
2021-04-04 07:15:13 +02:00
2019-02-24 05:38:21 +01:00
``` console
2021-04-04 07:15:13 +02:00
$ cargo run --example pool
{{#include ../../../../ci/expected/pool.run}}
```
## Инспектирование раскрываемого кода
`#[rtic::app]` - это процедурный макрос, который создает код.
Если по какой-то причине вам нужно увидеть код, сгенерированный этим макросом,
у вас есть два пути:
Вы можете изучить файл `rtic-expansion.rs` внутри папки `target`. Этот файл
содержит элемент `#[rtic::app]` в раскрытом виде (не всю вашу программу!)
из *последней сборки* (с помощью `cargo build` или `cargo check`) RTIC программы.
Раскрытый код не отформатирован по-умолчанию, но вы можете запустить `rustfmt`
на нем перед тем, как читать.
``` console
$ cargo build --example foo
$ rustfmt target/rtic-expansion.rs
$ tail target/rtic-expansion.rs
```
``` rust
#[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`]: https://crates.io/crates/cargo-expand
``` console
$ # создаст такой же вывод, как выше
$ cargo expand --example smallest | tail
```
## Деструктуризация ресурса
Если задача требует нескольких ресурсов, разбиение структуры ресурсов
может улучшить читабельность. Вот два примера того, как это можно сделать:
``` rust
{{#include ../../../../examples/destructure.rs}}
```