The app
attribute
Это наименьшая возможная программа на RTFM:
#![allow(unused)] 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() {} }; }
Все программы на 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)] fn main() { //! examples/init.rs #![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; use rtfm::app; #[app(device = lm3s6965)] const APP: () = { #[init] fn init() { static mut X: u32 = 0; // Cortex-M peripherals let _core: rtfm::Peripherals = core; // Device specific peripherals let _device: lm3s6965::Peripherals = 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`.
[SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
Как и в `init`, переменные `static mut`будут преобразованы в ссылки `&'static mut`
с безопасным доступом.
В примере ниже показан запуск `idle` после `init`.
``` rust
//! examples/idle.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init() {
hprintln!("init").unwrap();
}
#[idle]
fn idle() -> ! {
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, мы называем обработчики прерываний и исключений
*аппаратными* задачами.
``` rust
//! 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;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init() {
// 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() -> ! {
// 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() {
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.