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.