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.