rtic/book/ru/src/by-example/timer-queue.md

91 lines
5.8 KiB
Markdown
Raw Normal View History

2019-02-08 21:18:51 +01:00
# Очередь таймера
Когда включена опция `timer-queue`, фреймворк RTFM включает
*глобальную очередь таймера*, которую приложения могут использовать, чтобы
*планировать* программные задачи на запуск через некоторое время в будущем.
Чтобы была возможность планировать программную задачу, имя задачи должно
присутствовать в аргументе `schedule` контекста атрибута. Когда задача
планируется, момент ([`Instant`]), в который задачу нужно запустить, нужно передать
как первый аргумент вызова `schedule`.
[`Instant`]: ../../api/rtfm/struct.Instant.html
Рантайм RTFM включает монотонный, растущий только вверх, 32-битный таймер,
значение которого можно запросить конструктором `Instant::now`. Время ([`Duration`])
можно передать в `Instant::now()`, чтобы получить `Instant` в будущем. Монотонный
таймер отключен пока запущен `init`, поэтому `Instant::now()` всегда возвращает
значение `Instant(0 /* циклов тактовой частоты */)`; таймер включается сразу перед
включением прерываний и запуском `idle`.
[`Duration`]: ../../api/rtfm/struct.Duration.html
В примере ниже две задачи планируются из `init`: `foo` и `bar`. `foo` -
запланирована на запуск через 8 миллионов тактов в будущем. Кроме того, `bar`
запланирован на запуск через 4 миллиона тактов в будущем. `bar` запустится раньше
`foo`, т.к. он запланирован на запуск первым.
> **ВАЖНО**: Примеры, использующие API `schedule` или абстракцию `Instant`
> **не** будут правильно работать на QEMU, потому что функциональность счетчика
> тактов Cortex-M не реализована в `qemu-system-arm`.
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/schedule.rs}}
2019-02-08 21:18:51 +01:00
```
Запуск программы на реальном оборудовании производит следующий вывод в консоли:
``` text
2019-02-11 21:40:53 +01:00
{{#include ../../../../ci/expected/schedule.run}}
2019-02-08 21:18:51 +01:00
```
## Периодические задачи
Программные задачи имеют доступ к `Instant` в момент, когда были запланированы
на запуск через переменную `scheduled`. Эта информация и API `schedule` могут
быть использованы для реализации периодических задач, как показано в примере ниже.
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/periodic.rs}}
2019-02-08 21:18:51 +01:00
```
Это вывод, произведенный примером. Заметьте, что есть смещение / колебание нуля
даже если `schedule.foo` была вызвана в *конце* `foo`. Использование
`Instant::now` вместо `scheduled` имело бы влияние на смещение / колебание.
``` text
2019-02-11 21:40:53 +01:00
{{#include ../../../../ci/expected/periodic.run}}
2019-02-08 21:18:51 +01:00
```
## Базовое время
Для задач, планируемых из `init` мы имеем точную информацию о их планируемом
(`scheduled`) времени. Для аппаратных задач нет `scheduled` времени, потому
что эти задачи асинхронны по природе. Для аппаратных задач рантайм предоставляет
время старта (`start`), которе отражает время, в которое обработчик прерывания
был запущен.
Заметьте, что `start` **не** равен времени возникновения события, вызвавшего
задачу. В зависимости от приоритета задачи и загрузки системы время
`start` может быть сильно отдалено от времени возникновения события.
Какое по Вашему мнению будет значение `scheduled` для программных задач которые
*вызываются*, вместо того чтобы планироваться? Ответ в том, что вызываемые
задачи наследуют *базовое* время контекста, в котором вызваны. Бызовым для
аппаратных задач является `start`, базовым для программных задач - `scheduled`
и базовым для `init` - `start = Instant(0)`. `idle` на сомом деле не имеет
базового времени но задачи, вызванные из него будут использовать `Instant::now()`
как их базовое время.
Пример ниже демонстрирует разное значение *базового времени*.
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/baseline.rs}}
2019-02-08 21:18:51 +01:00
```
Запуск программы на реальном оборудовании произведет следующий вывод в консоли:
``` text
2019-02-11 21:40:53 +01:00
{{#include ../../../../ci/expected/baseline.run}}
2019-02-08 21:18:51 +01:00
```