ESP32 高分辨率定时器(ESP Timer)使用指南


目录


1. ESP Timer 简介

ESP Timer 是 ESP-IDF 提供的高分辨率软件定时器 API。它具有以下特点:

  • 高精度:定时精度可达微秒级(μs)
  • 基于系统时钟:使用 64 位硬件计数器,不受 APB 频率变化影响
  • 灵活的回调机制:支持从任务或 ISR 上下文进入回调
  • 单次/周期模式:支持单次定时和周期性定时两种模式

适用场景

  • 精确延时控制
  • 周期性任务执行(如 LED 闪烁、传感器采样)
  • 超时检测
  • 需要微秒级精度的定时操作

2. 头文件与依赖

使用 ESP Timer 需要包含以下头文件:

#include "esp_timer.h"      // ESP Timer API(核心)
#include "esp_err.h"        // 错误检查宏(可选,用于 ESP_ERROR_CHECK)

在自己的定时器模块头文件中声明:

#ifndef __ESPTIMER_H
#define __ESPTIMER_H

#include "esp_timer.h"

/* 函数声明 */
void esptimer_init(uint64_t tps);   /* 初始化高分辨率定时器 */

#endif

注意:使用 ESP Timer 之前,通常需要初始化 NVS(非易失性存储),因为 ESP-IDF 系统组件可能依赖 NVS。


3. 定时器回调函数

定时器回调函数在定时时间到达时被自动调用。编写回调函数需遵循以下规范:

函数原型

void esptimer_callback(void *arg);

参数说明

参数类型说明
argvoid *创建定时器时传入的用户参数,不需要时可设为 NULL

代码示例

/**
 * @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 结构体来配置定时器参数:

成员类型说明
.callbackesp_timer_cb_t定时到达时执行的回调函数指针
.argvoid *传递给回调函数的参数
.dispatch_methodesp_timer_dispatch_t回调执行方式
.nameconst char *定时器名称(用于调试和日志)
.skip_unhandled_eventsbool是否跳过未处理的事件(可选)

4.2 dispatch_method 取值

说明
ESP_TIMER_TASK回调由专用定时器任务执行(推荐,适合大多数场景)
ESP_TIMER_ISR回调在中断上下文中执行(延迟最低,但限制较多)

4.3 初始化函数实现

/**
 * @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 启动周期性定时器

定时器以固定周期反复触发回调:

esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us);
参数说明
timer定时器句柄
period_us定时周期,单位微秒(μs)
返回值ESP_OK 成功;其他值表示失败

5.2 启动单次定时器

定时器仅触发一次回调后自动停止:

esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
参数说明
timer定时器句柄
timeout_us超时时间,单位微秒(μs)
返回值ESP_OK 成功;其他值表示失败

5.3 停止与删除定时器

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

#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:

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 TimerFreeRTOS 软件定时器
精度微秒级毫秒级(受 tick 频率限制)
基于硬件 64 位计数器FreeRTOS tick 计数
使用复杂度简单,API 直观需要配置定时器任务
适用场景高精度定时低精度周期性任务

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐