ESP32高分辨率定时器(esp_timer)应用指南
一、为什么需要esp_timer?
FreeRTOS软件定时器存在两大限制:
-
「最大分辨率受限于RTOS节拍周期」(通常1ms) -
「回调函数在低优先级任务中调度」
ESP32的硬件定时器虽然不受此限制,但直接操作复杂。esp_timer API通过以下方式解决了这些问题:
-
✅ 使用64位硬件定时器(CONFIG_ESP_TIMER_IMPL) -
✅ 提供微秒级分辨率(1μs) -
✅ 支持单次和周期定时 -
✅ 自动管理待处理事件队列
二、核心特性与限制
关键优势
-
「高优先级回调」:从专用 esp_timer任务分派(优先级高于普通应用任务) -
「64位时间戳」: esp_timer_get_time()返回微秒级单调时间
使用限制
|
类型 |
最小周期 |
原因 |
|---|---|---|
|
单次定时 |
≥20μs |
调度开销 |
|
周期定时 |
≥50μs |
避免CPU过载 |
❝
「重要提示」:回调函数应保持简短,避免阻塞。耗时操作需通过队列/信号量移交其他任务。
❞
三、API 工作流程
graph TD
A[esp_timer_init] --> B[esp_timer_create]
B --> C[esp_timer_start_once/start_periodic]
C --> D[回调函数执行]
D --> E[esp_timer_stop]
E --> F[esp_timer_delete]
F --> G[esp_timer_deinit]
四、实战代码示例
单次定时器实现
// 定义定时器句柄及参数
esp_timer_handle_t once_timer;
esp_timer_create_args_t once_args = {
.callback = timer_once_callback,
.arg = NULL,
.name = "SingleShotTimer"
};
// 回调函数
void timer_once_callback(void* arg) {
int64_t elapsed = esp_timer_get_time();
printf("[%.6fs] 单次定时器触发\n", elapsed / 1e6f);
// 删除定时器(必需!)
esp_timer_delete(once_timer);
}
void app_main() {
esp_timer_init();
// 创建并启动定时器(10秒后触发)
ESP_ERROR_CHECK(esp_timer_create(&once_args, &once_timer));
ESP_ERROR_CHECK(esp_timer_start_once(once_timer, 10 * 1000000));
}
周期定时器实现
esp_timer_handle_t periodic_timer;
esp_timer_create_args_t periodic_args = {
.callback = timer_periodic_callback,
.name = "PeriodicTimer"
};
void timer_periodic_callback(void* arg) {
static int count = 0;
int64_t time = esp_timer_get_time();
printf("[#%d] 周期定时触发: %.3fms\n", ++count, time / 1000.0f);
// 运行100秒后停止
if (time > 100 * 1000000) {
ESP_ERROR_CHECK(esp_timer_stop(periodic_timer));
ESP_ERROR_CHECK(esp_timer_delete(periodic_timer));
printf("定时器已停止并删除\n");
}
}
void app_main() {
esp_timer_init();
// 创建并启动1秒周期定时器
ESP_ERROR_CHECK(esp_timer_create(&periodic_args, &periodic_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 1000000));
}
五、关键注意事项
-
「资源释放」:必须调用 esp_timer_delete()释放定时器资源 -
「重启机制」:重启运行中的定时器需先 stop再start -
「Flash操作延迟」:SPI Flash操作期间回调可能被延迟 -
「回调设计原则」: // 正确做法:快速传递数据
void good_callback(void* arg) {
xQueueSend(data_queue, &sensor_data, 0);
}
// 错误做法:在回调中执行长操作
void bad_callback(void* arg) {
vTaskDelay(100); // 绝对禁止!
process_image(); // 耗时操作
}
六、性能对比
|
特性 |
esp_timer |
FreeRTOS定时器 |
|---|---|---|
|
分辨率 |
1μs |
≥1ms |
|
回调执行上下文 |
高优先级任务 |
低优先级任务 |
|
最小单次触发 |
20μs |
1个tick |
|
最小周期 |
50μs |
1个tick |
❝
需要更高实时性?考虑使用硬件外设(如GPTimer)或DMA传输!
❞
完整API文档见ESP-IDF编程指南