Выбрать главу

Чтобы проиллюстрировать, рассмотрим следующий пример:

#![allow(unused)]

fn main() {

#[rtic::app(device = ..)]

mod app {

#[task(priority = 3, spawn = [baz])]

fn foo(c: foo::Context) {

// ..

}

#[task(priority = 2, schedule = [foo, baz])]

fn bar(c: bar::Context) {

// ..

}

#[task(priority = 1)]

fn baz(c: baz::Context) {

// ..

}

}

}

Анализ приоритетов происходил бы вот так:

   • foo (prio = 3) и baz (prio = 1) планируемые задачи, поэтому SysTick должен работать на максимальном из этих двух приоритетов, т.е. 3.

   • foo::Spawn (prio = 3) и bar::Schedule (prio = 2) соперничают за конечный потребитель baz_FQ; это приводит к максимальному приоритету 3.

   • bar::Schedule (prio = 2) имеет экслюзивный доступ к конечному потребителю foo_FQ; поэтому максимальный приоритет foo_FQ фактически 2.

   • SysTick (prio = 3) и bar::Schedule (prio = 2) соперничают за очередь таймера TQ; это приводит к максимальному приоритету 3.

   • SysTick (prio = 3) и foo::Spawn (prio = 3) оба имеют неблокируемый доступ к очереди готовности RQ3, что хранит записи foo; поэтому максимальный приоритет RQ3 фактически 3.

   • SysTick имеет эксклюзивный доступ к очереди готовности RQ1, которая хранит записи baz; поэтому максимальный приоритет RQ1 фактически 3.

Когда интерфейс schedule используется, реализация spawn немного изменяется, чтобы отслеживать baseline задач. Как можете видеть в реализации schedule есть буферы INSTANTS, используемые, чтобы хранить время, в которое задача была запланирована навыполнение; этот Instant читается диспетчером задач и передается в пользовательский код, как часть контекста задачи.

#![allow(unused)]

fn main() {

mod app {

// ..

#[no_mangle]

unsafe UART1() {

const PRIORITY: u8 = 1;

let snapshot = basepri::read();

while let Some(ready) = RQ1.split().1.dequeue() {

match ready.task {

Task::baz => {

let input = baz_INPUTS[ready.index as usize].read();

// ADDED

let instant = baz_INSTANTS[ready.index as usize].read();

baz_FQ.split().0.enqueue_unchecked(ready.index);

let priority = Celclass="underline" :new(PRIORITY);

// ИЗМЕНЕНО instant передан как часть контекста задачи

baz(baz::Context::new(&priority, instant), input)

}

Task::bar => {

// выглядит также как ветка для `baz`

}

}

}

// инвариант BASEPRI

basepri::write(snapshot);

}

}

}

И наоборот, реализации spawn нужно писать значение в буфер INSTANTS. Записанное значение располагается в структуре Spawn и это либо время start аппаратной задачи, либо время scheduled программной задачи.

#![allow(unused)]

fn main() {

mod foo {

// ..

pub struct Spawn<'a> {

priority: &'a Cell<u8>,

// ADDED

instant: Instant,

}

impl<'a> Spawn<'a> {

pub unsafe fn priority(&self) -> &Cell<u8> {

&self.priority

}

// ADDED

pub unsafe fn instant(&self) -> Instant {

self.instant

}

}

}

mod app {

impl<'a> foo::Spawn<'a> {

/// Spawns the `baz` task

pub fn baz(&self, message: u64) -> Result<(), u64> {

unsafe {

match lock(self.priority(), baz_FQ_CEILING, || {

baz_FQ.split().1.dequeue()

}) {

Some(index) => {

baz_INPUTS[index as usize].write(message);

// ADDED

baz_INSTANTS[index as usize].write(self.instant());

lock(self.priority(), RQ1_CEILING, || {

RQ1.split().1.enqueue_unchecked(Ready {

task: Task::foo,

index,

});

});

rtic::pend(Interrupt::UART0);

}

None => {

// достигнута максимальная вместительность; неудачный вызов