Это короткий фрагмент для ознакомления с книгой.
Real-Time Interrupt-driven Concurrency
Конкурентный фреймворк для создания систем реального времениВведение
Эта книга содержит документацию пользовательского уровня о фреймворке Real-Time Interrupt-driven Concurrency (RTIC). Справочник по API можно найти здесь. Также известен как Real-Time For the Masses. Это документация по RTIC версии v0.6.x; за документацией по другим версиям: • v0.5.x сюда. • v0.4.x сюда.Возможности
• Задачи как единица конкуренции 1. Задачи могут запускаться от событий (срабатывать в ответ на асинхронные воздействия) или вызываться по запросу программы. • Передача сообщений между задачами. Если точнее, сообщения можно передавать программным задачам в момент вызова. • Очередь таймера 2. Программные задачи можно планировать на запуск в определенный момент в будущем. Эту возможность можно использовать для создания периодических задач. • Поддержка приоритета задач, и, как результат, вытесняющей многозадачности. • Эффективное, избавленное от гонок данных, разделение ресурсов благодаря легкому разбиению на основанные на приоритетах критические секции 1. • Выполнение без Deadlock, гарантируемое на этапе компиляции. Данная гарантия строже, чем та, что предоставляется стандартный абтракцией Mutex. • Минимальные расходы на диспетчеризацию. Диспетчер задач иммет минимальную программную базу; основная работа по диспетчеризации происходит аппаратно. • Высокоэффективное использование памяти: Все задачи разделяют единый стек вызовов и отсутствует ресурсоемкая зависисмость от динамического аллокатора. • Все Cortex-M устройства полностью поддерживаются. • К такой модели задач можно применять так называемый анализ WCET (Наихудшего времени выполнения), а также техники анализа диспетчеризации. (Хотя мы еще не разработали дружественный к Rust'у инструментарий для этого.) • Приложения должны быть написаны в редакции 2018.Документация пользователя Справочник по API Чат
Присоединяйтесь к нам, чтобы говорить о RTIC в Matrix-комнате. Записи еженедельных собраний можно найти в HackMDВнести вклад
Новые возможности и большие изменения следует проводить через процесс RFC в соответствующем RFC-репозитории.Благодарности
Этот крейт основан на языке Real-Time For the Masses, созданном Embedded Systems group в Техническом Университете Luleå, под руководством Prof. Per Lindgren.Ссылки
1 Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P. (2013, June). Real-time for the masses, step 1: Programming API and static priority SRP kernel primitives. In Industrial Embedded Systems (SIES), 2013 8th IEEE International Symposium on (pp. 110-113). IEEE. 2 Lindgren, P., Fresk, E., Lindner, M., Lindner, A., Pereira, D., & Pinho, L. M. (2016). Abstract timers and their implementation onto the arm cortex-m family of mcus. ACM SIGBED Review, 13(1), 48-53.Лицензия
Все исходные тексты (включая примеры кода) лицензированы под одной из лицензий: • Apache License, Version 2.0 (LICENSE-APACHE или https://www.apache.org/licenses/LICENSE-2.0) • MIT license (LICENSE-MIT или https://opensource.org/licenses/MIT) на ваш выбор. Текст книги лицензирован по условиям лицензий Creative Commons CC-BY-SA v4.0 (LICENSE-CC-BY-SA или https://creativecommons.org/licenses/by-sa/4.0/legalcode).Условия участия
Если вы не укажете этого отдельно, любой вклад, который вы предоставите в эту работу, как указано в тексте лицензии Apache-2.0, будет лицензирован по условиям, указанным выше, без каких-либо дополнительных условий.RTIC в примерах
В этой части книги фреймворк Real-Time Interrupt-driven Concurrency (RTIC) представляется новым пользователям путем прохода по примерам от простых к более сложным. Все примеры в этой части книги можно найти в репозитарии проекта. Большинство из них можно пройти, запустив их на эмуляторе QEMU без специального оборудования. Для запуска примеров на вашем ПК, вам понадобится программа qemu-system-arm. В the embedded Rust book есть инструкции по настройке среды для эмбеддед разработке, в том числе QEMU.Примеры из реальной жизни
Ниже представлены примеры использования RTIC (RTFM) в реальных проектах.RTFM V0.4.2
• etrombly/sandbox. Аппаратный дзэн-сад, рисующий картинки на песке. Картинки передаются по последовательному порту с помощью G-кода.
Атрибут app
Это простейшая из возможных программ на RTIC:
#![allow(unused)]
fn main() {
//! examples/smallest.rs
#![no_main]
#![no_std]
use panic_semihosting as _; // panic handler
use rtic::app;
#[app(device = lm3s6965)]
mod app {
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
(Shared {}, Local {}, init::Monotonics())
}
}
}
Все программы на RTIC используют атрибут app (#[app(..)]). Этот атрибут должен применяться к элементу mod. Атрибут app имеет обязательный аргумент device, который принимает путь как значение. Это должен быть полный путь, указывающий на крейт доступа к периферии (PAC), сгенерированный с помощью svd2rust версии v0.14.x или новее. Более подробно в разделе Создание нового проекта.
Атрибут app будет раскрыт в подходящую точку входа программы, поэтому атрибут cortex_m_rt::entry не нужен.
init
Внутри модуля app атрибут ожидает найти функцию инициализации, помеченную атрибутом init. Эта функция должна иметь сигнатуру fn(init::Context) [-> init::LateResources] (возвращаемый тип нужен не всегда).
Эта функция инициализации будет первой частью программы, выполняемой при запуске. Функция init будет запущена с отключенными прерываниями и будет иметь эксклюзивный доступ к Cortex-M, в котором токен bare_metal::CriticalSection доступен как cs. Опционально, устройство-специфичные периферия доступна через поля core и device структуры init::Context.
static mut переменные, определенные в начале init будут преобразованы в &'static mut ссылки, безопасные для доступа. Обратите внимание, данная возможность может быть удалена в следующем релизе, см. task_local ресурсы.
Пример ниже показывает типы полей core, device и cs, и демонстрирует безопасный доступ к static mut переменной. Поле device доступно только когда аргумент peripherals установлен в true (по умолчанию). В редких случаях, когда вы захотите создать приложение с минимальным потреблением ресурсов, можно явно установить peripherals в false.
#![allow(unused)]
fn main() {
//! examples/init.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_semihosting as _;
#[rtic::app(device = lm3s6965, peripherals = true)]
mod app {
use cortex_m_semihosting::{debug, hprintln};
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init(local = [x: u32 = 0])]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
// Cortex-M peripherals
let _core: cortex_m::Peripherals = cx.core;
// Device specific peripherals
let _device: lm3s6965::Peripherals = cx.device;
// Locals in `init` have 'static lifetime
let _x: &'static mut u32 = cx.local.x;
// Access to the critical section token,
// to indicate that this is a critical seciton
let _cs_token: bare_metal::CriticalSection = cx.cs;
hprintln!("init").unwrap();
debug::exit(debug::EXIT_SUCCESS);
(Shared {}, Local {}, init::Monotonics())
}
}
}
Запуск примера напечатате init в консоли, а затем завершит процесс QEMU.
$ cargo run --example init
init
idle
Функцию, помеченную атрибутом idle может опционально добавить в модуль. Эта функция используется как специальная задача ожидания и должна иметь сигнатуру fn(idle::Context) - > !.
Если она присутствует, задача idle будет запущена после init. В отличие от init, idle будет запущена с включенными прерываниями и она не может вернуть результат, а значит должна работать вечно.
Если функция idle не определена, среда вполнения устанавливает бит SLEEPONEXIT, а затем отправляет микроконтроллер в сон после запуска init.
Как и в init, static mut переменные будут трансформированы в &'static mut ссылки, безопасные для доступа. Обратите внимание, данная возможность может быть удалена в следующем релизе, см. task_local ресурсы.
Пример ниже показывает, что idle запускается после init.
Примечание: Цикл loop {} в функци ожидания не может быть пустым, так как это сломает микроконтроллер, из-за того, что LLVM компилирует пустые циклы в инструкцию UDF в release mode. Чтобы избежать неопределенного поведения, цикл должен включать "side-effect" путем вставки ассемблерной инструкции (например, WFI) или ключевого слова continue.
#![allow(unused)]
fn main() {
//! examples/idle.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_semihosting as _;
#[rtic::app(device = lm3s6965)]
mod app {
use cortex_m_semihosting::{debug, hprintln};
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
hprintln!("init").unwrap();
(Shared {}, Local {}, init::Monotonics())
}
#[idle(local = [x: u32 = 0])]
fn idle(cx: idle::Context) -> ! {
// Locals in idle have lifetime 'static
let _x: &'static mut u32 = cx.local.x;
hprintln!("idle").unwrap();
debug::exit(debug::EXIT_SUCCESS);
loop {
cortex_m::asm::nop();
}
}
}
}
$ cargo run --example idle
init
idle
Аппаратные задачи
Чтобы объявить обработчик прерывания, фреймворк предоставляет атрибут #[task], который можно применять к функциям. Этот атрибут берет аргумент binds, чье значение - это имя прерывания, которому будет назначен обработчик; функция, декорированная этим атрибутом становится обработчиком прерывания. В фреймворке такие типы задач именуются аппаратными, потому что они начинают выполняться в ответ на аппаратное событие. Пример ниже демонстрирует использование атрибута #[task], чтобы объявить обработчик прерывания. Как и в случае с #[init] и #[idle] локальные static mut переменные безопасны для использования с аппаратной задачей.#![allow(unused)]
fn main() {
//! examples/hardware.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_semihosting as _;
#[rtic::app(device = lm3s6965)]
mod app {
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
// Pends the UART0 interrupt but its handler won't run until *after*
// `init` returns because interrupts are disabled
rtic::pend(Interrupt::UART0); // equivalent to NVIC::pend
hprintln!("init").unwrap();
(Shared {}, Local {}, init::Monotonics())
}
#[idle]
fn idle(_: idle::Context) -> ! {
// interrupts are enabled again; the `UART0` handler runs at this point
hprintln!("idle").unwrap();
rtic::pend(Interrupt::UART0);
debug::exit(debug::EXIT_SUCCESS);
loop {
cortex_m::asm::nop();
}
}
#[task(binds = UART0, local = [times: u32 = 0])]
fn uart0(cx: uart0::Context) {
// Safe access to local `static mut` variable
*cx.local.times += 1;
hprintln!(
"UART0 called {} time{}",
*cx.local.times,
if *cx.local.times > 1 { "s" } else { "" }
)
.unwrap();
}
}
}
$ cargo run --example hardware
init
UART0 called 1 time
idle
UART0 called 2 times
До сих пор все программы на RTIC, которые мы видели, не отличались от программ, которые можно написать, используя лишь крейт cortex-m-rt. С этого момента мы начинаем представлять возможности, уникальные для RTIC.
Приоритеты
Статический приоритет каждого обработчика можно оределить в атрибуте task, используя аргумент priority. Задачи могут иметь приоритет в диапазоне 1..=(1 << NVIC_PRIO_BITS), где NVIC_PRIO_BITS - это константа, определенная в крейте устройства. Когда аргумент priority не указан, предполагается, что приоритет равен 1. Задача idle имеет ненастраиваемый приоритет 0, наименьший из возможных. ...Все права на текст принадлежат автору: Коллектив авторов.
Это короткий фрагмент для ознакомления с книгой.