Чип ESP32 содержит 2 группы аппаратных таймеров. У каждой группы есть два 64-битного таймера общего назначения. У каждого таймера есть 16-битный прескалер и 64-битные счетчики, которые могут считать вверх и вниз, с возможностью автозагрузки значения в счетчик.
В следующих секциях этого документа (перевод документации [1]) описываются типовые шаги, необходимые для конфигурирования и работы таймера:
• Инициализация таймера - какие параметры должны быть установлены для того, чтобы таймер заработал, а также варианты функциональности, предоставляемые в зависимости от конфигурации таймера. • Управление таймером - как читать значение таймера, приостанавливать или запускать, и изменять его поведение. • События (Alarms) - показано, как устанавливать и использовать alarm-ы. • Прерывания - как использовать callback-и прерывания.
[Инициализация таймера]
В двух группах таймеров ESP32, по 2 таймера в каждой, предоставляется для общего использования 4 индивидуальных таймера. Группа таймеров ESP32 идентифицируется с использованием типа timer_group_t. Отдельный таймер в группе идентифицируется значением типа timer_idx_t.
Сначала таймер должен быть инициализирован вызовом функции timer_init() с передачей ей структуры timer_config_t, параметры в которой определяют, как должен работать таймер. В частности, могут быть установлены параметры:
Clock Source: выбирается источник тактирования, который вместе с параметром Divider определяет разрешающую способность таймера. По умолчанию источником тактов установлен APB_CLK (обычно 80 МГц). Divider: устанавливает деление тактовой частоты для таймера, определяет как быстро происходят "тики" таймера. Mode: устанавливает, должен ли счетчик инкрементироваться или декрементироваться. Это определяется значением поля counter_dir, которое может быть установлено в одно из двух значений из timer_count_dir_t. Counter Enable: если счетчик разрешен, то он начнет инкрементироваться / декрементироваться сразу после вызова timer_init(). Вы можете поменять это поведение установкой поля counter_en, которое может быть одним из значений timer_start_t. Alarm Enable: можно установить с помощью поля alarm_en. Auto Reload: устанавливает, должен ли счетчик автоматически загружать начальное значение при событии alarm, либо продолжать инкремент или декремент.
Чтобы получить текущие значения настройки таймера, используйте функцию timer_get_config().
[Управление таймером]
Как только таймер был разрешен, его счетчик начинает изменяться с заданной тактовой частотой. Для разрешения таймера вызовите timer_init() с counter_en, установленным в true, либо вызовите timer_start(). Вы можете указать начальное значение счетчика таймера вызовом timer_set_counter_value(). Для проверки текущего значения счетчика таймера вызовите timer_get_counter_value() или timer_get_counter_time_sec().
Для приостановки таймера в любой момент вызовите timer_pause(). Для возобновления работы таймера вызовите timer_start(). Для переконфигурирования таймера можно вызвать timer_init(). Также можно использовать выделенные функции для изменения отдельных настроек:
Таблица 1. Описание функций для изменения отдельных параметров таймера.
Настройка |
Функция |
Описание |
Divider |
timer_set_divider() |
Меняет частоту тиков таймера. Чтобы избежать непредсказуемых результатов, таймер должен быть приостановлен перед изменением делителя. Если таймер работает, то timer_set_divider() приостанавливает его, меняет настройку делителя, и затем снова запускает таймер. |
Mode |
timer_set_counter_mode() |
Устанавливает направление счета таймера - инкремент или декремент. |
Auto Reload |
timer_set_auto_reload() |
Если установлено, то начальное значение таймера должно быть перезагружено при событии alarm. |
[События (Alarms)]
Для установки alarm вызовите timer_set_alarm_value(), и затем разрешите alarm вызовом timer_set_alarm(). Работу alarm можно также разрешить на стадии инициализации таймера, когда вызывается функция timer_init().
После того, как alarm разрешена, и таймер достигает значения alarm, могут быть предприняты два действия в зависимости от конфигурации:
• Сработает прерывание, если оно было ранее разрешено. См. далее раздел "Прерывания", где описано, как они конфигурируются. • Когда разрешена автозагрузка счетчика (auto_reload), счетчик таймера будет автоматически перезагружаться для начала счета от ранее сконфигурированного значения. Это значение должно быть установлено заранее вызовом timer_set_counter_value().
Замечания: если установлено значение alarm, и таймер уже достиг этого значения, то alarm сработает немедленно. Когда alarm сработала, она автоматически запрещается, и должна быть заново разрешена, чтобы она снова могла сработать.
Чтобы проверить указанное значение alarm, вызовите timer_get_alarm_value().
[Прерывания]
Регистрация callback-функции прерывания для определенного таймера делается вызовом timer_isr_callback_add() с передачей ей group ID, timer ID, обработчика callback и данных пользователя. Callback будет вызван в контексте ISR, поэтому в нем нельзя вставлять блокирующие вызовы API-функций и функции смены контекста. Достоинство использования callback-а вместо реализации ISR с нуля в том, что программисту не нужно иметь дело с проверкой статуса прерывания и связанными с этим очистками, эти действия выполняются в коде драйвера ISR по умолчанию, до вызова callback-а.
Для дополнительной информации по использованию прерываний см. проект peripherals/timer_group, который находится в папке examples среди других примеров кода, поставляемых вместе со средой разработки ESP-IDF [2].
[Справочник по API]
Файл заголовка API-функций таймера: driver/include/driver/timer.h. Описание типов: hal/include/hal/timer_types.h.
Функция |
Описание |
timer_get_counter_value |
Считывает значение счетчика аппаратного таймера. |
timer_get_counter_time_sec |
Считывает значение счетчика аппаратного таймера в заданных единицах времени. |
timer_set_counter_value |
Установит значение счетчика аппаратного таймера. |
timer_start |
Запустит счетчик аппаратного таймера. |
timer_pause |
Приостановит счет аппаратного таймера. |
timer_set_counter_mode |
Установит направление счета аппаратного таймера. |
timer_set_auto_reload |
Разрешит или запретит загрузку значения счетчика, когда происходит событие alarm. |
timer_set_divider |
Установит коэффициент деления тактовой частоты таймера. Частота групп таймеров получается делением частоты тактов шины APB. |
timer_set_alarm_value |
Установит значение alarm таймера. |
timer_get_alarm_value |
Возвратит значение alarm таймера. |
timer_set_alarm |
Разрешает или запрещает генерацию событий alarm таймера. |
timer_isr_callback_add |
Добавит callback-обработчик прерывания для соответствующего таймера(1). Этот callback должен вернуть значение bool, которое определяет, нужно ли уступить контекст (YIELD) по окончанию ISR. Замечание: этот обработчик будет вызван из тела ISR, но в обработчике не надо обрабатывать статус прерывания, и его код должен быть настолько коротким, насколько это возможно. Если нужно реализовать какие-то специальные приложения, или переписать ISR полностью, то можете вызвать timer_isr_register() для регистрации ISR. |
timer_isr_callback_remove |
Удалит callback-обработчик для соответствующего таймера. |
timer_isr_register |
Регистрирует обработчик прерывания таймера (ISR). Этот обработчик будет подключен к тому же ядру CPU, на котором был вызов timer_isr_register(1). Для конфигурирования таймеров внутри ISR используйте прямой доступ к регистрам таймера. Кроме того, необходимо полностью реализовать весь код обработчика ISR таймера, т. е. нужно вызвать timer_spinlock_take() перед кодом обработчика и вызвать timer_spinlock_give() после. |
timer_init |
Инициализирует и конфигурирует таймер. |
timer_deinit |
Деинициализирует таймер. |
timer_get_config |
Считывает параметры конфигурации таймера. |
timer_group_intr_enable |
По маске разрешает прерывание группы таймеров. |
timer_group_intr_disable |
По маске запрещает прерывание группы таймеров. |
timer_enable_intr |
Разрешает прерывание таймера. |
timer_disable_intr |
Запрещает прерывание таймера. |
timer_group_intr_clr_in_isr |
Очищает статус прерывания таймера(2). |
timer_group_clr_intr_status_in_isr |
timer_group_enable_alarm_in_isr |
Разрешает прерывание alarm(2). |
timer_group_get_counter_value_in_isr |
Вернет текущее значение счетчика(2). |
timer_group_set_alarm_value_in_isr |
Установит порог alarm(2). |
timer_group_set_counter_enable_in_isr |
Разрешит или запретит счетчик таймера(2). |
timer_group_intr_get_in_isr |
Вернет маскированный статус прерывания(2). |
timer_group_get_intr_status_in_isr |
Вернет статус прерывания(2). |
timer_group_clr_intr_sta_in_isr |
Очистит маскированный статус прерывания(2). |
timer_group_get_auto_reload_in_isr |
Вернет статус разрешения автоматической загрузки(2). |
timer_spinlock_take |
Получает spinlock таймера для входа в защищенный критический регион. |
timer_spinlock_give |
Выдает spinlock таймера для выхода из защищенного критического региона. |
Примечания:
(1) Если в параметре intr_alloc_flags установлено ESP_INTR_FLAG_IRAM, то обработчик должен быть декларирован с атрибутом IRAM_ATTR, и может вызывать только функции из IRAM или ROM. Он не может вызывать другие API-функции таймера. (2) Эта функция используется только в теле ISR.
Подробное описание макросов, перечислений, определений типа и функций см. в документации [1].
[Ссылки]
1. ESP32 General Purpose Timer site:docs.espressif.com. 2. Установка среды разработки ESP-IDF для ESP32. 3. ESP32 High Resolution Timer. 4. ESP32: оцифровка звука. |