# Очередь таймера В отличие от интерфейса `spawn`, который немедленно передает программную задачу планировщику для немедленного запуска, интерфейс `schedule` можно использовать для планирования задачи к запуске через какое-то время в будущем. Чтобы использовать интерфейс `schedule`, предварительно должен быть определен монотонный таймер с помощью аргумента `monotonic` атрибута `#[app]`. Этот аргумент принимает путь к типу, реализующему трейт [`Monotonic`]. Ассоциированный тип, `Instant`, этого трейта представляет метку времени в соответствущих единицах измерения и широко используется в интерфейсе `schedule` -- предлагается смоделировать этот тип позднее [один из таких есть в стандартной библиотеке][std-instant]. Хотя это не отражено в определении трейта (из-за ограничений системы типов / трейтов), разница двух `Instant`ов должна возвращать какой-то тип `Duration` (см. [`core::time::Duration`]) и этот `Duration` должен реализовывать трейт `TryInto`. Реализация этого трейта должна конвертировать значение `Duration`, которое использует какую-то определенную единицу измерения времени, в единицы измерения "тактов системного таймера (SYST)". Результат преобразований должен быть 32-битным целым. Если результат не соответствует 32-битному целому, тогда операция должна возвращать ошибку любого типа. [`Monotonic`]: ../../../api/rtic/trait.Monotonic.html [std-instant]: https://doc.rust-lang.org/std/time/struct.Instant.html [`core::time::Duration`]: https://doc.rust-lang.org/core/time/struct.Duration.html Для целевых платформ ARMv7+ крейт `rtic` предоставляет реализацию `Monotonic`, основанную на встроенном CYCle CouNTer (CYCCNT). Заметьте, что это 32-битный таймер, работающий на частоте центрального процессора, и поэтому не подходит для отслеживания интервалов времени в секундах. Когда планируется задача, (определенный пользователем) `Instant`, в который задача должна быть выполнена, должен передаваться в качестве первого аргумента вызова `schedule`. К тому же, выбранный `monotonic` таймер, необходимо сконфигурировать и инициализировать в фазе работы `#[init]`. Заметьте, что *также* касается случая использования `CYCCNT`, предоставляемого крейтом `cortex-m-rtic`. Пример ниже планирует к выполнению две задачи из `init`: `foo` и `bar`. `foo` запланирована к запуску через 8 миллионов циклов в будущем. Далее, `bar` запланировано запустить через 4 миллиона циклов в будущем. Таким образом, `bar` запустится до `foo`, так как и запланировано. > **DF:YJ**: Примеры, использующие интерфейс `schedule` или абстракцию `Instant` > **не будут** правильно работать на эмуляторе QEMU, поскольку счетчик циклов Cortex-M > функционально не был реализован в `qemu-system-arm`. ``` rust {{#include ../../../../examples/schedule.rs}} ``` Запусе программы на реальном оборудовании создает следующий вывод в консоли: ``` text {{#include ../../../../ci/expected/schedule.run}} ``` Когда интерфейс `schedule` используется, среда исполнения использует внутри обработчик прерываний `SysTick` и периферию системного таймера (`SYST`), поэтому ни тот ни другой нельзя использовать в программе. Это гарантируется изменением типа `init::Context.core` с `cortex_m::Peripherals` на `rtic::Peripherals`. Последняя структура содержит все поля из предыдущей кроме `SYST`. ## Периодические задачи Программные задачи имеют доступ к моменту времени `Instant`, в который они были запланированы на выполнение переменной `scheduled`. Эта информация и интерфейс `schedule` можно использовать, чтобы реализовать периодические задачи, как показано ниже. ``` rust {{#include ../../../../examples/periodic.rs}} ``` Это вывод, создаваемый примером. Заметьте, что здесь пристствует небольшой дрейф / колебания даже несмотря на то, что `schedule.foo` была вызвана в *конце* `foo`. Использование `Instant::now` вместо `scheduled` вызвало бы дрейф / колебания. ``` text {{#include ../../../../ci/expected/periodic.run}} ``` ## Базовое время Для задач, вызываемых из `init` мы имеем точную информацию о их `scheduled` времени. Для аппаратных задач такого времени нет, поскольку они асинхронны по природе. Для аппаратных задач среда исполнения предоставляет время запуска (`start`), которое отражает время, в которое обработчик прерывания будет запущен. Заметьте, что `start` **не** равно времени прихода события, которое вызывает задачу. В зависимости от приоритета задачи и загрузки системы, время `start` может сильно отдалиться от времени прихода события. Какое по вашему мнению будет значение `scheduled` для программных задач, которые вызываются через `spawn` вместо планирования? Ответ в том, что вызываемые задачи наследуют *базовое* время того контекста, который их вызывает. Базовое время аппаратных задач - это их время `start`, базовое время программных задач - их время `scheduled`, а базовое время `init` - время старта системы, или нулевое (`Instant::zero()`). `idle` на самом деле не имеет базового времени, но задачи вызываемые из нее, используют `Instant::now()` в качестве базового. Пример ниже демонстрирует разные смыслы *базового времени*. ``` rust {{#include ../../../../examples/baseline.rs}} ``` Запуск программы на реальном оборудовании приведет к следующему выводу в консоли: ``` text {{#include ../../../../ci/expected/baseline.run}} ```