三、【ESP32开发全栈指南:ESP32高分辨率定时器接口使用】

ESP32高分辨率定时器(esp_timer)应用指南

一、为什么需要esp_timer?

FreeRTOS软件定时器存在两大限制:

  1. 「最大分辨率受限于RTOS节拍周期」(通常1ms)

  2. 「回调函数在低优先级任务中调度」

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));
}

五、关键注意事项

  1. 「资源释放」:必须调用esp_timer_delete()释放定时器资源

  2. 「重启机制」:重启运行中的定时器需先stop再start
  3. 「Flash操作延迟」:SPI Flash操作期间回调可能被延迟

  4. 「回调设计原则」:

    // 正确做法:快速传递数据
    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编程指南


发表评论