| 
 Чип 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: оцифровка звука.  |