2019-02-08 21:18:51 +01:00
|
|
|
|
# Программные задачи
|
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
В дополнение к аппаратным задачам, вызываемым в ответ на аппаратные события,
|
|
|
|
|
RTIC также поддерживает *программные* задачи, которые могут порождаться
|
|
|
|
|
приложением из любого контекста выполнения.
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
Программным задачам можно также назначать приоритет и, под капотом, они
|
|
|
|
|
диспетчеризуются обработчиками прерываний. RTIC требует, чтобы свободные
|
|
|
|
|
прерывания, были указаны в аргументе `dispatchers` модуля `app`, если используются
|
|
|
|
|
программные задачи; часть из этих свободных прерываний будут использованы для
|
|
|
|
|
управления программными задачами. Преимущество программных задач над аппаратными
|
|
|
|
|
в том, что множество задач можно назначить на один обработчик прерывания.
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
Программные задачи также определяются атрибутом `task`, но аргумент `binds` опускается.
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
Пример ниже демонстрирует три программные задачи, запускаемых 2-х разных приоритетах.
|
|
|
|
|
Три программные задачи привязаны к 2-м обработчикам прерываний.
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
|
|
|
|
``` rust
|
2019-02-11 21:40:53 +01:00
|
|
|
|
{{#include ../../../../examples/task.rs}}
|
2019-02-08 21:18:51 +01:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
``` console
|
|
|
|
|
$ cargo run --example task
|
2021-04-04 07:15:13 +02:00
|
|
|
|
{{#include ../../../../ci/expected/task.run}}
|
|
|
|
|
```
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
|
|
|
|
## Передача сообщений
|
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
Другое преимущество программной задачи в том, что задачам можно передать сообщения
|
|
|
|
|
в момент их запуска. Тип передаваемого сообщения должен быть определен в сигнатуре
|
|
|
|
|
задачи-обработчика.
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
Пример ниже демонстрирует три задачи, две из которых ожидают сообщение.
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
|
|
|
|
``` rust
|
2019-02-11 21:40:53 +01:00
|
|
|
|
{{#include ../../../../examples/message.rs}}
|
2019-02-08 21:18:51 +01:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
``` console
|
|
|
|
|
$ cargo run --example message
|
2021-04-04 07:15:13 +02:00
|
|
|
|
{{#include ../../../../ci/expected/message.run}}
|
|
|
|
|
```
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
## Вместимость
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
RTIC *не* производит никакого рода аллокаций памяти в куче.
|
|
|
|
|
Память, необходимая для размещения сообщения резервируется статически.
|
|
|
|
|
По-умолчанию фреймворк минимизирует выделение памяти программой таким образом,
|
|
|
|
|
что каждая задача имеет "вместимость" для сообщения равную 1:
|
|
|
|
|
это значит, что не более одного сообщения можно передать задаче перед тем, как
|
|
|
|
|
у нее появится возможность к запуску. Это значение по-умолчанию можно
|
|
|
|
|
изменить для каждой задачи, используя аргумент `capacity`.
|
|
|
|
|
Этот аргумент принимает положительное целое, которое определяет как много
|
|
|
|
|
сообщений буфер сообщений задачи может хранить.
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
2021-04-04 07:15:13 +02:00
|
|
|
|
Пример ниже устанавливает вместимость программной задачи `foo` равной 4.
|
|
|
|
|
Если вместимость не установить, второй вызов `spawn.foo` в `UART0` приведет к ошибке (панике).
|
2019-02-08 21:18:51 +01:00
|
|
|
|
|
|
|
|
|
``` rust
|
2019-02-11 21:40:53 +01:00
|
|
|
|
{{#include ../../../../examples/capacity.rs}}
|
2019-02-08 21:18:51 +01:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
``` console
|
|
|
|
|
$ cargo run --example capacity
|
2021-04-04 07:15:13 +02:00
|
|
|
|
{{#include ../../../../ci/expected/capacity.run}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Обработка ошибок
|
|
|
|
|
|
|
|
|
|
Интерфейс `spawn` возвращает вариант `Err`, если для размещения сообщения нет места.
|
|
|
|
|
В большинстве сценариев возникающие ошибки обрабатываются одним из двух способов:
|
|
|
|
|
|
|
|
|
|
- Паника, с помощью `unwrap`, `expect`, и т.п. Этот метод используется, чтобы обнаружить
|
|
|
|
|
ошибку программиста (например bug) выбора вместительности, которая оказалась недостаточна.
|
|
|
|
|
Когда эта паника встречается во время тестирования, выбирается большая вместительность,
|
|
|
|
|
и перекомпиляция программы может решить проблему, но иногда достаточно окунуться глубже
|
|
|
|
|
и провести анализ времени выполнения программы, чтобы выяснить, может ли платформа
|
|
|
|
|
обрабатывать пиковые нагрузки, или процессор необходимо заменить на более быстрый.
|
|
|
|
|
|
|
|
|
|
- Игнорирование результата. В программах реального времени, как и в обычных, может быть
|
|
|
|
|
нормальным иногда терять данные, или не получать ответ на некоторые события в пиковых ситуациях.
|
|
|
|
|
В таких сценариях может быть допустимо игнорирование ошибки вызова `spawn`.
|
|
|
|
|
|
|
|
|
|
Следует отметить, что повторная попытка вызова `spawn` обычно неверный подход, поскольку
|
|
|
|
|
такая операция на практике вероятно никогда не завершится успешно.
|
|
|
|
|
Так как у нас есть только переключения контекста на задачи с *более высоким* приоритетом,
|
|
|
|
|
повторение вызова `spawn` на задаче с низким приоритом никогда не позволит планировщику
|
|
|
|
|
вызвать задачу, что значит, что буфер никогда не будет очищен. Такая ситуация отражена в
|
|
|
|
|
следующем наброске:
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
#[rtic::app(..)]
|
|
|
|
|
mod app {
|
|
|
|
|
#[init(spawn = [foo, bar])]
|
|
|
|
|
fn init(cx: init::Context) {
|
|
|
|
|
cx.spawn.foo().unwrap();
|
|
|
|
|
cx.spawn.bar().unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[task(priority = 2, spawn = [bar])]
|
|
|
|
|
fn foo(cx: foo::Context) {
|
|
|
|
|
// ..
|
|
|
|
|
|
|
|
|
|
// программа зависнет здесь
|
|
|
|
|
while cx.spawn.bar(payload).is_err() {
|
|
|
|
|
// повтор попытки вызова spawn, если произошла ошибка
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[task(priority = 1)]
|
|
|
|
|
fn bar(cx: bar::Context, payload: i32) {
|
|
|
|
|
// ..
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|