Для задач, вызываемых из init мы имеем точную информацию о их scheduled времени. Для аппаратных задач такого времени нет, поскольку они асинхронны по природе. Для аппаратных задач среда исполнения предоставляет время запуска (start), которое отражает время, в которое обработчик прерывания будет запущен.
Заметьте, что start не равно времени прихода события, которое вызывает задачу. В зависимости от приоритета задачи и загрузки системы, время start может сильно отдалиться от времени прихода события.
Какое по вашему мнению будет значение scheduled для программных задач, которые вызываются через spawn вместо планирования? Ответ в том, что вызываемые задачи наследуют
Пример ниже демонстрирует разные смыслы
#![allow(unused)]
fn main() {
{{#include ../../../../examples/baseline.rs}}
}
Запуск программы на реальном оборудовании приведет к следующему выводу в консоли:
init(baseline = Instant(0))
foo(baseline = Instant(0))
UART0(baseline = Instant(904))
foo(baseline = Instant(904))
Каждая функция в модуле app принимает структуру Context в качесте первого параметра. Все поля этих структур имеют предсказуемые, неанонимные типы, поэтому вы можете написать обычные функции, принимающие их как аргументы.
Справочник по API определяет как эти типы генерируются на основе входных данных. Вы можете также сгенерировать документацию к вашему крейту программы (cargo doc --bin
Пример ниже показывает различные типы, сгенерированные атрибутом app.
#![allow(unused)]
fn main() {
{{#include ../../../../examples/types.rs}}
}
Send - это маркерный трейт для "типов, которые можно передавать через границы потоков", как это определено в core. В контексте RTIC трейт Send необходим только там, где возможна передача значения между задачами, запускаемыми на
Атрибут app проверит, что Send реализован, где необходимо, поэтому вам не стоит волноваться об этом. В настоящий момент все передаваемые типы в RTIC должны быть Send, но это ограничение возможно будет ослаблено в будущем.
Аналогично, Sync - маркерный трейт для "типов, на которые можно безопасно разделять между потоками", как это определено в core. В контексте RTIC типаж Sync необходим только там, где возможно для двух или более задач, запускаемых на разных приоритетах получить разделяемую ссылку (&-) на ресурс. Это возникает только (&-) ресурсах с разделяемым доступом.
Атрибут app проверит, что Sync реализован, где необходимо, но важно знать, где ограничение Sync не требуется: в (&-) ресурсах с разделяемым доступом, за которые соперничают задачи с
В примере ниже показано, где можно использовать типы, не реализующие Sync.
#![allow(unused)]
fn main() {
#![deny(warnings)]
#![no_main]
#![no_std]
use core::marker::PhantomData;
use panic_semihosting as _;
pub struct NotSync {
_0: PhantomData<*const ()>,
}
unsafe impl Send for NotSync {}
#[rtic::app(device = lm3s6965, dispatchers = [SSI0])]
mod app {
use super::NotSync;
use core::marker::PhantomData;
use cortex_m_semihosting::debug;
#[shared]
struct Shared {
shared: NotSync,
}
#[local]
struct Local {}
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
debug::exit(debug::EXIT_SUCCESS);
(
Shared {
shared: NotSync { _0: PhantomData },
},
Local {},
init::Monotonics(),
)
}
#[task(shared = [&shared])]