ESP32 High Resolution Timer |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||
FreeRTOS предоставляет программные таймеры [5], однако у них есть ограничения: • Максимальная разрешающая способность равна периоду тика RTOS (по умолчанию 10 мс, определяется опцией CONFIG_FREERTOS_HZ). Аппаратные таймеры свободны от обоих этих ограничений, однако они часто не настолько удобны в использовании. Например, компоненту приложения может понадобиться события таймера, которые запускаются в определенное назначенное время, однако аппаратный таймер имеет только одно значение сравнения (compare), используемое для генерации прерывания. Это значит, что поверх аппаратуры требуется какая-то программная надстройка для обработки списка ожидающих обработки событий, которая перенаправляет эти события в соответствующие callback-функции при возникновении определенных аппаратных прерываний таймера. Уровень прерывания обработчика зависит от опции CONFIG_ESP_TIMER_INTERRUPT_LEVEL. Это позволит установить уровень 1, 2 или 3 (по умолчанию используется 1). Повышением уровня можно уменьшить задержку обработки таймера. Библиотека esp_timer предоставляет набор API-функций для создания однократных (one-shot) и периодических (periodic) таймеров, с разрешающей способностью в микросекунд, и 64-битным диапазоном значений. Внутри себя esp_timer использует 64-разрядный аппаратный таймер, реализация зависит от target компиляции (используемого кристалла). Для ESP32 используется таймер LAC. Callback-функции таймера могут быть обработаны двумя методами: • ESP_TIMER_TASK. ESP_TIMER_TASK. Callback-функции таймера диспетчеризуются из задачи esp_timer, работающей с высоким приоритетом. Из-за того, что эти callback-и обрабатываются из одной и той же задачи, то рекомендуется в каждом callback реализовать как можно меньше действий, и передавать событие для обработки в низкоприоритетную задачу, используя очередь. Если работают другие задачи с более высоким приоритетом, чем приоритет задачи esp_timer, то обработка вызова callback будет откладываться до момента, когда задаче esp_timer появится шанс запуститься. Например, это произойдет, если выполняется операция с памятью SPI Flash. ESP_TIMER_ISR. Callback-и таймера обрабатываются непосредственно из тела ISR таймера. Этот метод полезен для некоторых простых callback-ов, которые должны работать с минимальной латентностью. Создание и запуск таймера, и диспетчеризация callback-а занимает некоторое время. Таким образом, существует минимальный предел для значения длительности one-shot таймера esp_timer. Если esp_timer_start_once() вызывается со значением таймаута меньше, чем 20 мкс, то callback-функция будет обработана только после приблизительно 20 мкс. Периодический esp_timer также накладывает ограничение в 50 мкс на минимальный период таймера. Периодические таймеры с периодом меньше 50 мкс непрактичны для использования, поскольку они будут потреблять слишком много процессорного времени ядра CPU. Рассмотрите использование выделенные аппаратные периферийные устройства, или функции DMA, если требуется таймер с периодом меньше 50 мкс. [Использование API-функций esp_timer] Один таймер представлен типом esp_timer_handle_t. У таймера есть связанная с ним callback-функция. Эта функция вызывается из задачи esp_timer каждый раз, когда истекает задержка таймера. Для создания таймера вызовите esp_timer_create(). Для удаления таймера, когда он больше не нужен, вызовите esp_timer_delete(). Таймер может быть запущен в режиме однократного запуска (one-shot mode), или в режиме периодического запуска (periodic mode). Для запуска таймера в однократном режиме вызовите esp_timer_start_once(), и передайте в эту функцию значение интервала времени, после которого должен быть вызван callback. Когда callback вызван, однократный таймер считается остановленным. Для запуска таймера в периодическом режиме вызовите esp_timer_start_periodic(), и передайте в неё длительность периода, с которым должен вызываться callback. В этом случае таймер продолжает работу до тех пор, пока не будет вызвана функция esp_timer_stop(). Обратите внимание, что таймер не должен работать, когда вызывается esp_timer_start_once() или esp_timer_start_periodic(). Для перезапуска таймера вызовите сначала esp_timer_stop(), затем вызовите функцию запуска. Callback-функции. Функции обратного вызова (callback) таймера обрабатываемые методом ESP_TIMER_ISR, не должны вызывать функцию portYIELD_FROM_ISR(), которая приводит к переключению контекста, вместо этого следует использовать функцию esp_timer_isr_dispatch_need_yield(). Переключение контекста произойдет после того, как будут обработаны все ISR таймеров, если это требуется системой. Важное замечание: делайте код callback-функций как можно более коротким, иначе это повлияет на все таймеры. esp_timer во время light sleep. Во время light sleep счетчик esp_timer остановится, и callback-функции вызываться не будут. Вместо этого реальное время будет отсчитывать RTC, его счетчик продолжит работать. После пробуждения система вычислит разницу между этими счетчиками, и вызовет функцию, которая продвинет вперед на эту разницу значение счетчика esp_timer. Поскольку счетчик esp_timer был увеличен, система начнет вызывать callback-и, которые не были вызваны во время сна. Количество callback-ов зависит от длительности сна и от периода таймеров. Это может привести к переполнению некоторых очередей. Это относится только к периодическим таймерам, обработчики one-shot таймеров будут вызваны однократно. Это поведение может быть отменено вызовом esp_timer_stop() перед входом в sleep. В некоторых случаях этот способ неудобен, и вместо функции остановки можно использовать опцию skip_unhandled_events при создании таймера esp_timer_create(). Когда skip_unhandled_events == true, если периодический таймер истечет больше одного раза во время light sleep, то при пробуждении будет вызван все равно только один callback. Использование опции skip_unhandled_events с автоматическим light sleep (см. Power Management API [2]) помогает уменьшить энергопотребление системы, когда она находится в light sleep. Длительность light sleep также определяется is also determined by таймерами esp_timer. Таймеры с опцией skip_unhandled_events не будут пробуждать систему. Обработка callback-функций. Библиотека esp_timer была разработана для получения таймеров высокого разрешения, с малой латентностью, и возможностью обработки отложенных по времени событий. Если обработка таймера запаздывает, то его callback будет вызван как можно быстрее, это событие не будет потеряно. Для самого плохого случая, когда таймер не был обработан для больше чем одного периода (для периодических таймеров), вызовы его callback-ов произойдут сразу друг за другом, без ожидания установленного периода. Для некоторых приложений это может быть плохо (в частности, может переполниться очередь вызова callback-ов таймера), и была введена опция для пропуска не обработанных событий skip_unhandled_events, устраняющая такое поведение. Если установлена опция skip_unhandled_events, то периодический таймер, который истек несколько раз, не имея возможности вызывать свой callback, все равно вызовет его при появлении такой возможности, но только один раз. Отсчет текущего времени. Библиотека esp_timer также предоставляет удобную функцию для получения текущего времени, которое прошло с момента запуска системы (start-up), с микросекундной точностью: esp_timer_get_time(). Эта функция вернет количество микросекунд с момента, когда esp_timer был инициализирован, что обычно происходит перед вызовом функции app_main приложения. В отличие от функции gettimeofday, функция esp_timer_get_time() возвратит значения: • Начиная с 0 от момента пробуждения чипа из deep sleep. Пример приложения, использующее esp_timer API, см. в папке examples/system/esp_timer среди других примеров среды разработки ESP-IDF [3]. [Справочник по esp_timer API] Заголовочный файл: components/esp_timer/include/esp_timer.h.
Примечания: (1) Эта функция вызывается из кода startup, приложениям не нужно вызывать её перед использованием других API-вызовов esp_timer. Подробное описание типов данных, перечислений и функций см. в документации [1]. [Ссылки] 1. ESP32 High Resolution Timer site:docs.espressif.com. |