Читаем Real-Time Interrupt-driven Concurrency полностью

До сих пор все программы на RTIC, которые мы видели, не отличались от программ, которые можно написать, используя лишь крейт cortex-m-rt. С этого момента мы начинаем представлять возможности, уникальные для RTIC.

<p id="Приоритеты"><strong><a l:href="#Приоритеты">Приоритеты</a></strong></p>

Статический приоритет каждого обработчика можно оределить в атрибуте task, используя аргумент priority. Задачи могут иметь приоритет в диапазоне 1..=(1 << NVIC_PRIO_BITS), где NVIC_PRIO_BITS - это константа, определенная в крейте устройства. Когда аргумент priority не указан, предполагается, что приоритет равен 1. Задача idle имеет ненастраиваемый приоритет 0, наименьший из возможных.

Более высокое значение означает более высокий приоритет в RTIC, что противоположно тому, что указано в периферии NVIC Cortex-M. Точнее, это значит, что число 10 обозначает приоритет выше, чем число 9.

Когда несколько задач готовы к запуску, задача с самым большим статическим приоритетом будет запущена первой. Приоритезацию задач можно рассматривать по такому сценарию: сигнал прерывания приходит во время выполнения задачи с низким приоритетом; сигнал переключает задачу с высоким приоритетом в режим ожидания. Разница в приоритетах приводи к тому, что задача с высоким приоритетом вытесняет задачу с низким: выполнение задачи с низким приоритетом замораживается и задача с высоким приоритетом выполняется, пока не будет завершена. Как только задача с высоким приоритетом будет остановлена, продолжится выполнение задачи с низким приоритетом.

Следующий пример демонстрирует диспетчеризацию на основе приоритетов задач.

#![allow(unused)]

fn main() {

//! examples/preempt.rs

#![no_main]

#![no_std]

use panic_semihosting as _;

use rtic::app;

#[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) {

rtic::pend(Interrupt::GPIOA);

(Shared {}, Local {}, init::Monotonics())

}

#[task(binds = GPIOA, priority = 1)]

fn gpioa(_: gpioa::Context) {

hprintln!("GPIOA - start").unwrap();

rtic::pend(Interrupt::GPIOC);

hprintln!("GPIOA - end").unwrap();

debug::exit(debug::EXIT_SUCCESS);

}

#[task(binds = GPIOB, priority = 2)]

fn gpiob(_: gpiob::Context) {

hprintln!(" GPIOB").unwrap();

}

#[task(binds = GPIOC, priority = 2)]

fn gpioc(_: gpioc::Context) {

hprintln!(" GPIOC - start").unwrap();

rtic::pend(Interrupt::GPIOB);

hprintln!(" GPIOC - end").unwrap();

}

}

}

$ cargo run --example preempt

GPIOA - start

GPIOC - start

GPIOC - end

GPIOB

GPIOA - end

Заметьте, что задача gpiob не вытесняет задачу gpioc, потому что ее приоритет такой же, как и у gpioc. Однако, как только gpioc возвращает результат, выполненяется задача gpiob, как более приоритетная по сравнению с gpioa. Выполнение gpioa возобновляется только после выхода из gpiob.

Еще одно замечание по поводу приоритетов: выбор приоритета большего, чем поддерживает устройство (а именно 1 << NVIC_PRIO_BITS) приведет к ошибке компиляции. Из-за ограничений языка, сообщение об ошибке далеко от понимания: вам скажут что-то похожее на "evaluation of constant value failed", а указатель на ошибку не покажет на проблемное значение прерывания -- мы извиняемся за это!

<p id="Ресурсы"><strong><a l:href="#Ресурсы">Ресурсы</a></strong></p>

Фреймворк предоставляет абстракцию для разделения данных между любыми контекстами, с которыми мы встречались в предыдущей главе (задачами-обработчиками, init и idle): ресурсы.

Ресурсы - это данные, видимые только функциями, определенными внутри модуля #[app]. Фреймворк дает пользователю полный контроль за тем, какой контекст может получить доступ к какому ресурсу.

Все ресурсы определены в одной структуре внутри модуля #[app]. Каждое поле структуры соответствует отдельному ресурсу. struct-ура должна быть аннотирована следующим атрибутом: #[resources].

Похожие книги

Книги не найдены