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

#[rtic::app(device = lm3s6965, dispatchers = [SSI0])]

mod app {

use cortex_m_semihosting::{debug, hprintln};

#[shared]

struct Shared {}

#[local]

struct Local {}

#[init]

fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {

foo::spawn(/* no message */).unwrap();

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

}

#[task(local = [count: u32 = 0])]

fn foo(cx: foo::Context) {

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

bar::spawn(*cx.local.count).unwrap();

*cx.local.count += 1;

}

#[task]

fn bar(_: bar::Context, x: u32) {

hprintln!("bar({})", x).unwrap();

baz::spawn(x + 1, x + 2).unwrap();

}

#[task]

fn baz(_: baz::Context, x: u32, y: u32) {

hprintln!("baz({}, {})", x, y).unwrap();

if x + y > 4 {

debug::exit(debug::EXIT_SUCCESS);

}

foo::spawn().unwrap();

}

}

}

$ cargo run --example message

foo

bar(0)

baz(1, 2)

foo

bar(1)

baz(2, 3)

<p id="Вместимость"><strong><a l:href="#Вместимость">Вместимость</a></strong></p>

RTIC не производит никакого рода аллокаций памяти в куче. Память, необходимая для размещения сообщения резервируется статически. По-умолчанию фреймворк минимизирует выделение памяти программой таким образом, что каждая задача имеет "вместимость" для сообщения равную 1: это значит, что не более одного сообщения можно передать задаче перед тем, как у нее появится возможность к запуску. Это значение по-умолчанию можно изменить для каждой задачи, используя аргумент capacity. Этот аргумент принимает положительное целое, которое определяет как много сообщений буфер сообщений задачи может хранить.

Пример ниже устанавливает вместимость программной задачи foo равной 4. Если вместимость не установить, второй вызов spawn.foo в UART0 приведет к ошибке (панике).

#![allow(unused)]

fn main() {

//! examples/capacity.rs

#![deny(unsafe_code)]

#![deny(warnings)]

#![no_main]

#![no_std]

use panic_semihosting as _;

#[rtic::app(device = lm3s6965, dispatchers = [SSI0])]

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::UART0);

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

}

#[task(binds = UART0)]

fn uart0(_: uart0::Context) {

foo::spawn(0).unwrap();

foo::spawn(1).unwrap();

foo::spawn(2).unwrap();

foo::spawn(3).unwrap();

bar::spawn().unwrap();

}

#[task(capacity = 4)]

fn foo(_: foo::Context, x: u32) {

hprintln!("foo({})", x).unwrap();

}

#[task]

fn bar(_: bar::Context) {

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

debug::exit(debug::EXIT_SUCCESS);

}

}

}

$ cargo run --example capacity

foo(0)

foo(1)

foo(2)

foo(3)

bar

<p id="Обработка_ошибок"><strong><a l:href="#Обработка_ошибок">Обработка ошибок</a></strong></p>

Интерфейс spawn возвращает вариант Err, если для размещения сообщения нет места. В большинстве сценариев возникающие ошибки обрабатываются одним из двух способов:

   • Паника, с помощью unwrap, expect, и т.п. Этот метод используется, чтобы обнаружить ошибку программиста (например bug) выбора вместительности, которая оказалась недостаточна. Когда эта паника встречается во время тестирования, выбирается большая вместительность, и перекомпиляция программы может решить проблему, но иногда достаточно окунуться глубже и провести анализ времени выполнения программы, чтобы выяснить, может ли платформа обрабатывать пиковые нагрузки, или процессор необходимо заменить на более быстрый.

Перейти на страницу:

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