1. 简介
ESP32内置4个64-bit通用定时器。每个定时器包含一个16-bit预分频器和一个64-bit可自动重新加载向上/向下计数器。ESP32的定时器分为2组,每组2个。定时器具有闹钟功能,闹钟事件会引发重新加载和触发中断。
硬件定时器的时钟是由APB时钟提供的,APB时钟默认是80MHz;定时器时钟经过预分频器的分频后,才是定时器计数的频率。
2. API
2.1 创建定时器
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer);
- config:定时器配置;
typedef struct {
gptimer_clock_source_t clk_src; /*!< GPTimer clock source */
gptimer_count_direction_t direction; /*!< Count direction */
uint32_t resolution_hz; /*!< Counter resolution (working frequency) in Hz,
hence, the step size of each count tick equals to (1 / resolution_hz) seconds */
int intr_priority; /*!< GPTimer interrupt priority,
if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */
struct {
uint32_t intr_shared: 1; /*!< Set true, the timer interrupt number can be shared with other peripherals */
uint32_t backup_before_sleep: 1; /*!< If set, the driver will backup/restore the GPTimer registers before/after entering/exist sleep mode.
By this approach, the system can power off GPTimer's power domain.
This can save power, but at the expense of more RAM being consumed */
} flags; /*!< GPTimer config flags*/
} gptimer_config_t;
clk_src:定时器时钟源;direction:定时器计数方向,向上或向下;resolution_hz:分辨率,即计数的频率;intr_priority:中断优先级,可选0-3,0为默认优先级。
- ret_timer:定时器句柄。
2.2 删除定时器
esp_err_t gptimer_del_timer(gptimer_handle_t timer);
- timer:定时器句柄。
2.3 注册中断回调
esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data);
- timer:定时器句柄;
- cbs:中断回调;
- user_data:传入回调函数的用户数据。
2.4 初始化闹钟
esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config);
- timer:定时器句柄;
- config:配置项。
typedef struct {
uint64_t alarm_count; /*!< Alarm target count value */
uint64_t reload_count; /*!< Alarm reload count value, effect only when `auto_reload_on_alarm` is set to true */
struct {
uint32_t auto_reload_on_alarm: 1; /*!< Reload the count value by hardware, immediately at the alarm event */
} flags; /*!< Alarm config flags*/
} gptimer_alarm_config_t;
alarm_count:闹钟触发值,触发频率 = 定时器频率 * 闹钟触发值;reload_count:重装载次数,为0表示一直重装载;auto_reload_on_alarm:使能闹钟重装载。
2.5 使能定时器
esp_err_t gptimer_enable(gptimer_handle_t timer);
- timer:定时器句柄。
2.6 启动定时器
esp_err_t gptimer_start(gptimer_handle_t timer);
- timer:定时器句柄。
3. 例程
例程中实现一个定时器闹钟应用,间隔为1秒,每次闹钟触发就打印一条log。
#include "driver/gptimer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include <string.h>
#define TAG "app"
gptimer_handle_t gptimer = NULL;
SemaphoreHandle_t sem = NULL;
bool timer_timeout_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
BaseType_t higherTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(sem, &higherTaskWoken);
return higherTaskWoken == pdTRUE;
}
void app_main()
{
/* 初始化信号量 */
sem = xSemaphoreCreateBinary();
/* 初始化定时器 */
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT, // 默认时钟,APB时钟
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000, // 1MHz
.intr_priority = 0, // 默认中断优先级
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
/* 注册回调 */
gptimer_event_callbacks_t cbs = {
.on_alarm = timer_timeout_cb,
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
/* 初始化闹钟 */
gptimer_alarm_config_t alarm_config = {
.reload_count = 0, // 不断重载
.alarm_count = 1000000,
.flags.auto_reload_on_alarm = true,
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
/* 使能定时器和闹钟 */
ESP_ERROR_CHECK(gptimer_enable(gptimer));
ESP_ERROR_CHECK(gptimer_start(gptimer));
while (1) {
if (pdTRUE == xSemaphoreTake(sem, portMAX_DELAY)) {
ESP_LOGI(TAG, "Timer timeout");
}
}
}
初始化定时器参数,resolution_hz设置1000000,即计数频率为1MHz;向上计数。下面注册定时器中断回调函数,结构体里面只有一个on_alarm的回调;回调函数的第二个参数内容如下。
typedef struct {
uint64_t count_value; /*!< Current count value */
uint64_t alarm_value; /*!< Current alarm value */
} gptimer_alarm_event_data_t;
主要就是当前定时器的寄存器值和闹钟寄存器的值,基本不会用到。回调函数返回是否需要切换操作系统上下文,如果在回调函数里面有信号量、互斥量这种操作就要注意;如果没有用到,返回假就行了。
初始化闹钟,设置无限次重装载;触发值为1000000,即1秒触发一次。
最后是一个死循环,在里面程序会不断等待信号量的到来,获取到信号量表示闹钟触发了一次,然后打印一句log。