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

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 直观 需要配置定时器任务
适用场景 高精度定时 低精度周期性任务