Перейти к содержанию

Бестактовый режим (tickless idle)

Сон в хуке простоя упирается в системный тик: каждые 10 мс прерывание будит процессор, и на вход-выход из глубокого сна уходит больше энергии, чем он экономит. Бестактовый режим убирает эту помеху — тик останавливается, пока ядру нечего делать.

Обновлено Редакция GAW

Бестактовый режим (tickless idle) — режим FreeRTOS, в котором ядро останавливает периодическое прерывание системного тика на время простоя, то есть пока ни одна прикладная задача не готова к выполнению, а после пробуждения корректирует счётчик тиков на проспанное время. Микроконтроллер может оставаться в глубоком режиме пониженного потребления до внешнего прерывания или до момента, когда ядру пора перевести какую-нибудь задачу в состояние Ready.

Зачем останавливать тик

Простой способ экономить — уводить процессор в сон из хука задачи простоя. Но тик будит его каждый период, и чем выше частота тика, тем больше энергии и времени уходит на вход и выход из сна; по документации FreeRTOS, при высокой частоте тика эти затраты превышают выигрыш для всех режимов, кроме самых лёгких. При типичных 100 Гц из шаблона конфигурации это пробуждение каждые 10 мс (наш расчёт), а глубоким режимам с долгим выходом столько не хватает.

Как это работает

Режим включается в FreeRTOSConfig.h: configUSE_TICKLESS_IDLE = 1 использует встроенную реализацию порта (есть у портов GCC, IAR и Keil для ARM Cortex-M), = 2 — пользовательскую, которую можно написать для любого порта. По умолчанию и в шаблоне значение 0: не все порты поддерживают режим.

Ядро вызывает макрос portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime), когда выполняются оба условия:

  1. задача простоя — единственная готовая задача;
  2. до ближайшей разблокировки задачи пройдёт не меньше configEXPECTED_IDLE_TIME_BEFORE_SLEEP полных тиков (по умолчанию 2).

Параметр макроса — число тиков до момента, когда задачу нужно перевести в Ready, то есть длительность безопасного сна. Внутри реализация останавливает таймер тика, входит в короткую критическую секцию и спрашивает ядро eTaskConfirmSleepModeStatus():

ОтветЧто делать
eAbortSleepзадача успела разблокироваться — перезапустить тик и не спать
eNoTasksWaitingTimeoutникто не ждёт по времени — спать без будильника
иноезавести пробуждение через xExpectedIdleTime тиков и уснуть

Ответ «спать без будильника» возможен только если в системе нет программных таймеров и все задачи либо приостановлены, либо заблокированы с portMAX_DELAY. После пробуждения счётчик тиков корректируют vTaskStepTick() на реально проспанное время, измеренное источником, который работает во сне. Планировщик приостановлен на всё время выполнения макроса, так что прикладные задачи не вклинятся между выходом из сна и коррекцией времени. Простая реализация из документации даёт расхождение с календарным временем; официальные порты сводят его к минимуму.

В ESP-IDF

Автоматический light sleep на ESP32 построен именно на Tickless Idle: ядро само уводит чип в сон между тиками и будит его по таймеру. Поэтому для esp_pm_configure() с автоматическим light sleep опция CONFIG_FREERTOS_USE_TICKLESS_IDLE обязательна — без неё функция вернёт ESP_ERR_NOT_SUPPORTED. Источник пробуждения по таймеру занят этим механизмом, и настраивать его вручную нельзя; в самом light sleep периферия отключена от тактирования и прерывания от GPIO не формируются. С deep sleep этот механизм не связан: в глубокий сон входят вручную вызовом esp_deep_sleep_start(), и в нём выключаются процессорные ядра и большая часть ОЗУ.

Частые вопросы

Когда ядро FreeRTOS решает остановить тик?

Когда выполняются два условия сразу: задача простоя — единственная готовая к выполнению (все прикладные задачи заблокированы или приостановлены), и до ближайшей разблокировки какой-либо задачи пройдёт не меньше configEXPECTED_IDLE_TIME_BEFORE_SLEEP полных тиков — по умолчанию 2. Тогда ядро вызывает portSUPPRESS_TICKS_AND_SLEEP() и передаёт ей число тиков, на которое можно безопасно уснуть.

Может ли микроконтроллер в бестактовом режиме спать бесконечно?

Да, если eTaskConfirmSleepModeStatus() вернула eNoTasksWaitingTimeout. Это случается только когда в системе не используются программные таймеры и все задачи либо приостановлены, либо заблокированы без таймаута (portMAX_DELAY). Тогда ядру незачем просыпаться по времени, и разбудить его может только внешнее прерывание. Любой активный программный таймер или задача с конечным таймаутом задают крайний срок пробуждения.

Как бестактовый режим связан с автоматическим light sleep в ESP-IDF?

Автоматический light sleep ESP-IDF построен на FreeRTOS Tickless Idle: ядро само уводит чип в light sleep, когда задач нет, и будит его по таймеру к нужному тику. Поэтому опция CONFIG_FREERTOS_USE_TICKLESS_IDLE должна быть включена в menuconfig, иначе esp_pm_configure() вернёт ESP_ERR_NOT_SUPPORTED. Источник пробуждения по таймеру занят этим механизмом и вручную не настраивается.

Источники

  1. FreeRTOS. FreeRTOS Kernel documentation: Low power support — tickless idle mode (Kernel features), 2026 — разд. Power Saving Introduction: обычный способ — уводить МК в режим пониженного потребления из хука простоя, но экономия ограничена необходимостью выходить из сна на каждый тик; при высокой частоте тика затраты на вход и выход превышают выигрыш для всех режимов, кроме самых лёгких; бестактовый режим останавливает периодическое прерывание тика на время простоя (когда ни одна прикладная задача не готова) и корректирует счётчик тиков при перезапуске; МК остаётся в глубоком сне до прерывания или до момента, когда ядру пора перевести задачу в Ready; разд. portSUPPRESS_TICKS_AND_SLEEP: встроенная реализация включается configUSE_TICKLESS_IDLE = 1 (для портов, где она есть), пользовательская — значением 2 для любого порта; ядро вызывает portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ), когда одновременно: задача простоя — единственная готовая и до ближайшей разблокировки задачи пройдёт не меньше n полных тиков, n задаёт configEXPECTED_IDLE_TIME_BEFORE_SLEEP; параметр — число тиков до перевода задачи в Ready, то есть время безопасного сна; eTaskConfirmSleepModeStatus() возвращает eNoTasksWaitingTimeout — спать можно неограниченно — только если не используются программные таймеры и все задачи приостановлены или заблокированы с portMAX_DELAY; eAbortSleep — сон отменить; планировщик приостанавливается до вызова макроса и возобновляется после; между остановкой тика и входом в сон нужна короткая критическая секция, из неё вызывают eTaskConfirmSleepModeStatus(); порты GCC, IAR и Keil для ARM Cortex-M содержат готовую реализацию; в примере пользовательской реализации счётчик корректирует vTaskStepTick(), простая реализация даёт расхождение с календарным временем, официальные порты его сводят к минимуму
  2. FreeRTOS. FreeRTOS-Kernel V11.3.3: examples/template_configuration/FreeRTOSConfig.h и include/FreeRTOS.h (GitHub, MIT) — шаблон: configUSE_TICKLESS_IDLE 0 — не все порты поддерживают бестактовый режим, по умолчанию 0; configTICK_RATE_HZ 100; FreeRTOS.h: configEXPECTED_IDLE_TIME_BEFORE_SLEEP равно 2, если не определено, configUSE_TICKLESS_IDLE равно 0, если не определено
  3. Espressif. ESP-IDF Programming Guide v6.1, ESP32: Power Management; Sleep Modes — автоматический light sleep основан на механизме FreeRTOS Tickless Idle; если он запрошен, а опция CONFIG_FREERTOS_USE_TICKLESS_IDLE не включена в menuconfig, esp_pm_configure() возвращает ESP_ERR_NOT_SUPPORTED; в light sleep периферия отключена от тактирования, прерывания от GPIO и внутренней периферии не формируются; автоматический light sleep реализован через пробуждение по таймеру, поэтому источник пробуждения по таймеру нельзя настраивать вручную; стр. Sleep Modes: в Deep-sleep выключены процессоры, большая часть ОЗУ и цифровая периферия на APB_CLK, вход — esp_deep_sleep_start()
Страница помогла?

Нашли ошибку? ·