ESP32 高分辨率定时器(ESP Timer)使用指南
目录
- [1. ESP Timer 简介](#1. ESP Timer 简介)
- [2. 头文件与依赖](#2. 头文件与依赖)
- [3. 定时器回调函数](#3. 定时器回调函数)
- [4. 定时器初始化与配置](#4. 定时器初始化与配置)
- [5. 启动定时器](#5. 启动定时器)
- [6. 完整使用示例](#6. 完整使用示例)
- [7. 常用 API 参考](#7. 常用 API 参考)
- [8. 注意事项](#8. 注意事项)
1. ESP Timer 简介
ESP Timer 是 ESP-IDF 提供的高分辨率软件定时器 API。它具有以下特点:
- 高精度:定时精度可达微秒级(μs)
- 基于系统时钟:使用 64 位硬件计数器,不受 APB 频率变化影响
- 灵活的回调机制:支持从任务或 ISR 上下文进入回调
- 单次/周期模式:支持单次定时和周期性定时两种模式
适用场景
- 精确延时控制
- 周期性任务执行(如 LED 闪烁、传感器采样)
- 超时检测
- 需要微秒级精度的定时操作
2. 头文件与依赖
使用 ESP Timer 需要包含以下头文件:
c
#include "esp_timer.h" // ESP Timer API(核心)
#include "esp_err.h" // 错误检查宏(可选,用于 ESP_ERROR_CHECK)
在自己的定时器模块头文件中声明:
c
#ifndef __ESPTIMER_H
#define __ESPTIMER_H
#include "esp_timer.h"
/* 函数声明 */
void esptimer_init(uint64_t tps); /* 初始化高分辨率定时器 */
#endif
注意:使用 ESP Timer 之前,通常需要初始化 NVS(非易失性存储),因为 ESP-IDF 系统组件可能依赖 NVS。
3. 定时器回调函数
定时器回调函数在定时时间到达时被自动调用。编写回调函数需遵循以下规范:
函数原型
c
void esptimer_callback(void *arg);
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
arg |
void * |
创建定时器时传入的用户参数,不需要时可设为 NULL |
代码示例
c
/**
* @brief 定时器回调函数
* @param arg: 不携带参数
* @retval 无
*/
void esptimer_callback(void *arg)
{
LED0_TOGGLE(); // 每到达定时周期,翻转一次 LED 状态
}
回调函数注意事项
- 回调函数应尽量简短,避免长时间阻塞
- 不要在回调中使用会阻塞的 API(如等待信号量超过很短时间)
- 回调执行时间应远小于定时周期
4. 定时器初始化与配置
4.1 配置结构体 esp_timer_create_args_t
创建定时器前,需要先填充 esp_timer_create_args_t 结构体来配置定时器参数:
| 成员 | 类型 | 说明 |
|---|---|---|
.callback |
esp_timer_cb_t |
定时到达时执行的回调函数指针 |
.arg |
void * |
传递给回调函数的参数 |
.dispatch_method |
esp_timer_dispatch_t |
回调执行方式 |
.name |
const char * |
定时器名称(用于调试和日志) |
.skip_unhandled_events |
bool |
是否跳过未处理的事件(可选) |
4.2 dispatch_method 取值
| 值 | 说明 |
|---|---|
ESP_TIMER_TASK |
回调由专用定时器任务执行(推荐,适合大多数场景) |
ESP_TIMER_ISR |
回调在中断上下文中执行(延迟最低,但限制较多) |
4.3 初始化函数实现
c
/**
* @brief 初始化高分辨率定时器(ESP_TIMER)
* @param tps: 定时器周期,以微秒为单位(μs)
* 若以一秒为定时器周期来执行一次定时器中断,那此处 tps = 1s = 1000000μs
* @retval 无
*/
void esptimer_init(uint64_t tps)
{
esp_timer_handle_t esp_tim_handle; /* 定义定时器句柄 */
/* 定义一个定时器结构体设置定时器配置参数 */
esp_timer_create_args_t timer_arg = {
.callback = &esptimer_callback, /* 计时时间到达时执行的回调函数 */
.arg = NULL, /* 传递给回调函数的参数 */
.dispatch_method = ESP_TIMER_TASK, /* 进入回调方式,从定时器任务进入 */
.name = "Timer", /* 定时器名称 */
};
ESP_ERROR_CHECK(esp_timer_create(&timer_arg, &esp_tim_handle)); /* 创建定时器 */
ESP_ERROR_CHECK(esp_timer_start_periodic(esp_tim_handle, tps)); /* 启动周期性定时器 */
}
4.4 参数说明
| 参数 | 说明 |
|---|---|
tps |
定时器触发周期,单位微秒(μs) 。例如 1000000 表示 1 秒 |
5. 启动定时器
ESP Timer 提供两种启动方式:
5.1 启动周期性定时器
定时器以固定周期反复触发回调:
c
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us);
| 参数 | 说明 |
|---|---|
timer |
定时器句柄 |
period_us |
定时周期,单位微秒(μs) |
| 返回值 | ESP_OK 成功;其他值表示失败 |
5.2 启动单次定时器
定时器仅触发一次回调后自动停止:
c
esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
| 参数 | 说明 |
|---|---|
timer |
定时器句柄 |
timeout_us |
超时时间,单位微秒(μs) |
| 返回值 | ESP_OK 成功;其他值表示失败 |
5.3 停止与删除定时器
c
esp_err_t esp_timer_stop(esp_timer_handle_t timer); /* 停止定时器 */
esp_err_t esp_timer_delete(esp_timer_handle_t timer); /* 删除定时器,释放资源 */
6. 完整使用示例
以下是一个完整的 ESP Timer 使用示例,实现 LED 每秒闪烁一次:
6.1 工程结构
05_esptimer/
├── CMakeLists.txt
├── main/
│ ├── CMakeLists.txt
│ └── main.c
├── components/
│ └── BSP/
│ ├── LED/
│ │ ├── led.h
│ │ └── led.c
│ └── ESPTIMER/
│ ├── esptimer.h
│ └── esptimer.c
└── README.md
6.2 main.c
c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include <stdio.h>
#include "led.h"
#include "esptimer.h"
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
led_init(); /* 初始化LED */
esptimer_init(1000000); /* 初始化ESP_TIMER,周期1秒(1000000μs)*/
while(1)
{
vTaskDelay(pdMS_TO_TICKS(10)); /* 延时10ms */
}
}
6.3 实验现象
程序运行后,LED0 每隔 1 秒 翻转一次电平状态,即 LED 以 2 秒为周期闪烁。
7. 常用 API 参考
7.1 定时器生命周期 API
| API | 功能 |
|---|---|
esp_timer_create() |
创建定时器 |
esp_timer_start_once() |
启动单次定时器 |
esp_timer_start_periodic() |
启动周期性定时器 |
esp_timer_restart() |
重新启动已启动的定时器(更新超时时间) |
esp_timer_stop() |
停止定时器 |
esp_timer_delete() |
删除定时器 |
7.2 时间获取 API
| API | 功能 |
|---|---|
esp_timer_get_time() |
获取自启动以来的时间(微秒,64位) |
esp_timer_dump() |
打印所有定时器的状态信息(调试用) |
7.3 系统初始化 API
| API | 功能 |
|---|---|
esp_timer_init() |
初始化 ESP Timer 子系统(在调用 esp_timer_create 前自动调用) |
esp_timer_deinit() |
反初始化 ESP Timer 子系统 |
8. 注意事项
8.1 回调上下文选择
| 上下文 | 优点 | 限制 |
|---|---|---|
ESP_TIMER_TASK |
可调用大部分 FreeRTOS API,实现灵活 | 延迟稍高(取决于任务优先级) |
ESP_TIMER_ISR |
延迟极低 | 不能调用阻塞 API,不能使用 printf 打印浮点数等 |
一般应用场景推荐使用
ESP_TIMER_TASK。
8.2 精度与周期
- 最小定时周期取决于系统负载和回调执行时间,理论上可达微秒级
- 定时周期不宜过短(如几微秒),否则回调可能来不及执行
- 1 秒 = 1,000,000 微秒(μs)
8.3 资源管理
- 不再使用的定时器应及时调用
esp_timer_delete()删除,释放内存 - 一个定时器同一时刻只能启动一次,如需更改周期,先停止再重新启动
- 使用
ESP_ERROR_CHECK宏检查 API 返回值,便于调试
8.4 NVS 初始化
ESP Timer 的某些依赖(如日志、系统时间等)需要 NVS 支持,建议在 app_main 中首先初始化 NVS:
c
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
8.5 与 FreeRTOS 软件定时器的区别
| 特性 | ESP Timer | FreeRTOS 软件定时器 |
|---|---|---|
| 精度 | 微秒级 | 毫秒级(受 tick 频率限制) |
| 基于 | 硬件 64 位计数器 | FreeRTOS tick 计数 |
| 使用复杂度 | 简单,API 直观 | 需要配置定时器任务 |
| 适用场景 | 高精度定时 | 低精度周期性任务 |