The app attribute
Это наименьшая возможная программа на RTFM:
# #![allow(unused_variables)] #fn main() { //! examples/smallest.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] // panic-handler crate extern crate panic_semihosting; use rtfm::app; #[app(device = lm3s6965)] const APP: () = { #[init] fn init(_: init::Context) {} }; #}
Все программы на RTFM используют атрибут app (#[app(..)]). Этот атрибут
нужно применять к const-элементам, содержащим элементы. Атрибут app имеет
обязательный аргумент device, в качестве значения которому передается путь.
Этот путь должен указывать на библиотеку устройства, сгенерированную с помощью
svd2rust v0.14.x. Атрибут app развернется в удобную точку входа,
поэтому нет необходимости использовать атрибут cortex_m_rt::entry.
ОТСТУПЛЕНИЕ: Некоторые из вас удивятся, почему мы используем ключевое слово
constкак модуль, а не правильноеmod. Причина в том, что использование атрибутов на модулях требует feature gate, который требует ночную сборку. Чтобы заставить RTFM работать на стабильной сборке, мы используем вместо него словоconst. Когда большая часть макросов 1.2 стабилизируются, мы прейдем отconstкmodи в конце концов в атрибуту уровне приложения (#![app]).
init
Внутри псевдо-модуля атрибут app ожидает найти функцию инициализации, обозначенную
атрибутом init. Эта функция должна иметь сигнатуру [unsafe] fn().
Эта функция инициализации будет первой частью запускаемого приложения.
Функция init запустится с отключенными прерываниями и будет иметь эксклюзивный
доступ к периферии Cortex-M и специфичной для устройства периферии через переменные
core and device, которые внедряются в область видимости init атрибутом app.
Не вся периферия Cortex-M доступна в core, потому что рантайм RTFM принимает владение
частью из неё -- более подробно см. структуру rtfm::Peripherals.
Переменные static mut, определённые в начале init будут преобразованы
в ссылки &'static mut с безопасным доступом.
Пример ниже показывает типы переменных core и device и
демонстрирует безопасный доступ к переменной static mut.
# #![allow(unused_variables)] #fn main() { //! examples/init.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; #[rtfm::app(device = lm3s6965)] const APP: () = { #[init] fn init(c: init::Context) { static mut X: u32 = 0; // Cortex-M peripherals let _core: rtfm::Peripherals = c.core; // Device specific peripherals let _device: lm3s6965::Peripherals = c.device; // Safe access to local `static mut` variable let _x: &'static mut u32 = X; hprintln!("init").unwrap(); debug::exit(debug::EXIT_SUCCESS); } }; #}
Запуск примера напечатает init в консоли и завершит процесс QEMU.
$ cargo run --example init
init
idle
Функция, помеченная атрибутом idle может присутствовать в псевдо-модуле
опционально. Эта функция используется как специальная задача ожидания и должна иметь
сигнатуру [unsafe] fn() - > !.
Когда она присутствует, рантайм запустит задачу idle после init. В отличие от
init, idle запустится с включенными прерываниями и не может завершиться,
поэтому будет работать бесконечно.
Когда функция idle определена, рантайм устанавливает бит SLEEPONEXIT, после чего
отправляет микроконтроллер в состояние сна после выполнения init.
Как и в init, переменные static mutбудут преобразованы в ссылки &'static mut
с безопасным доступом.
В примере ниже показан запуск idle после init.
# #![allow(unused_variables)] #fn main() { //! examples/idle.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; #[rtfm::app(device = lm3s6965)] const APP: () = { #[init] fn init(_: init::Context) { hprintln!("init").unwrap(); } #[idle] fn idle(_: idle::Context) -> ! { static mut X: u32 = 0; // Safe access to local `static mut` variable let _x: &'static mut u32 = X; hprintln!("idle").unwrap(); debug::exit(debug::EXIT_SUCCESS); loop {} } }; #}
$ cargo run --example idle
init
idle
interrupt / exception
Как Вы бы сделали с помощью библиотеки cortex-m-rt, Вы можете использовать атрибуты
interrupt и exception внутри псевдо-модуля app, чтобы определить обработчики
прерываний и исключений. В RTFM, мы называем обработчики прерываний и исключений
аппаратными задачами.
# #![allow(unused_variables)] #fn main() { //! examples/interrupt.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; use lm3s6965::Interrupt; #[rtfm::app(device = lm3s6965)] const APP: () = { #[init] fn init(_: init::Context) { // Pends the UART0 interrupt but its handler won't run until *after* // `init` returns because interrupts are disabled rtfm::pend(Interrupt::UART0); hprintln!("init").unwrap(); } #[idle] fn idle(_: idle::Context) -> ! { // interrupts are enabled again; the `UART0` handler runs at this point hprintln!("idle").unwrap(); rtfm::pend(Interrupt::UART0); debug::exit(debug::EXIT_SUCCESS); loop {} } #[interrupt] fn UART0(_: UART0::Context) { static mut TIMES: u32 = 0; // Safe access to local `static mut` variable *TIMES += 1; hprintln!( "UART0 called {} time{}", *TIMES, if *TIMES > 1 { "s" } else { "" } ) .unwrap(); } }; #}
$ cargo run --example interrupt
init
UART0 called 1 time
idle
UART0 called 2 times
До сих пор программы RTFM, которые мы видели не отличались от программ, которые
можно написать, используя только библиотеку cortex-m-rt. В следующем разделе
мы начнем знакомиться с функционалом, присущим только RTFM.