Программирование ARM ESP32 High Resolution Timer Fri, April 19 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

ESP32 High Resolution Timer Печать
Добавил(а) microsin   

FreeRTOS предоставляет программные таймеры [5], однако у них есть ограничения:

• Максимальная разрешающая способность равна периоду тика RTOS (по умолчанию 10 мс, определяется опцией CONFIG_FREERTOS_HZ).
• Callback-функции программных таймеров обрабатываются из задачи с низким приоритетом.

Аппаратные таймеры свободны от обоих этих ограничений, однако они часто не настолько удобны в использовании. Например, компоненту приложения может понадобиться события таймера, которые запускаются в определенное назначенное время, однако аппаратный таймер имеет только одно значение сравнения (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_ISR. Этот вариант доступен только если разрешена опция CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD (по умолчанию эта опция запрещена).

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.
• Нет зоны времени (timezone) или подстроек DST.

Пример приложения, использующее esp_timer API, см. в папке examples/system/esp_timer среди других примеров среды разработки ESP-IDF [3].

[Справочник по esp_timer API]

Заголовочный файл: components/esp_timer/include/esp_timer.h.

Функция Описание
esp_timer_early_init Минимальная инициализация esp_timer(1). Эта функция может быть вызвана на самой ранней стадии процесса startup, и только после этого может вызываться функция esp_timer_get_time.
esp_timer_init Инициализирует библиотеку esp_timer(1). Перед вызовом этой функции из кода startup также должна быть вызвана функция esp_timer_early_init.
esp_timer_deinit Деинициализирует библиотеку esp_timer. Обычно эта функция не должна вызываться из приложений.
esp_timer_create Создает экземпляр таймера esp_timer. Когда использование этого экземпляра завершено, вызовите esp_timer_delete.
esp_timer_start_once Создает однократный таймер(2).
esp_timer_start_periodic Запуск периодического таймера(2). Обработчик этого таймера будет вызываться с установленным интервалом в единицах микросекунд.
esp_timer_stop Остановит таймер, который был ранее запущен функцией esp_timer_start_once или esp_timer_start_periodic.
esp_timer_delete Удалит созданный ранее экземпляр таймера. Перед удалением таймер должен быть остановлен. Однократный таймер, у которого истекла задержка, останавливать не нужно.
esp_timer_get_time Возвратит абсолютное время в микросекундах, прошедшее с момента загрузки системы (boot).
esp_timer_get_next_alarm Возвратит метку времени, когда должен произойти следующий ожидаемый таймаут.
esp_timer_get_next_alarm_for_wake_up Возвратит метку времени, когда должен произойти следующий ожидаемый таймаут, с пропуском тех таймаутов, у которых был установлен флаг skip_unhandled_events.
esp_timer_get_period Эта функция считывает период таймаута таймера. Период таймаут это интервал времени, через который таймер снова запустится при истечении его таймаута. Для однократных таймеров этот период всегда 0, потому что для них не определен период запуска.
esp_timer_get_expiry_time Эта функция считывает время истечения однократного таймера. Будет возвращено достоверное врем только для однократного таймера. Будет возвращена ошибка, если в эту функцию был передан дескриптор периодического таймера.
esp_timer_dump Выводит список таймеров. Если разрешена опция CONFIG_ESP_TIMER_PROFILING, то даст вывод списка всех существующих таймеров. Иначе будет выведен список только активных таймеров. Формат вывода: name - имя таймера, если определена опция CONFIG_ESP_TIMER_PROFILING, или указатель на таймер. period - период таймера в мкс, или 0 для однократного таймера. alarm - время до следующего срабатывания в мкс от момента загрузки или 0 если таймер не запущен. Если определена опция CONFIG_ESP_TIMER_PROFILING, то дополнительно будет выведено следующее: times_armed — сколько раз таймер был взведен вызовомesp_timer_start_X. times_triggered - количество вызовов callback-а. total_callback_run_time - общее время, которое заняло выполнение callback-а суммарно по всем вызовам.
esp_timer_isr_dispatch_need_yield Запрашивает переключение контекста из callback-функции таймера. Это работает только для таймера, у которого применяется метод обработки через ISR. Переключение контекста произойдет после того, как будут обработаны все ISR таймеров.
esp_timer_is_active Вернет статус таймера - активен он или нет.

Примечания:

(1) Эта функция вызывается из кода startup, приложениям не нужно вызывать её перед использованием других API-вызовов esp_timer.
(2) Во время вызова этой функции соответствующий таймер работать не должен.

Подробное описание типов данных, перечислений и функций см. в документации [1].

[Ссылки]

1. ESP32 High Resolution Timer site:docs.espressif.com.
2. ESP32: управление энергопотреблением.
3. Установка среды разработки ESP-IDF для ESP32.
4ESP32 General Purpose Timer.
5FreeRTOS: программные таймеры.

 

Добавить комментарий


Защитный код
Обновить

Top of Page