一、SYSTIMER
1、SYSTIMER
SYSTIMER(System Timer,系统定时器)是ESP32-S3内置的高精度、全局硬件定时器,属于芯片底层基础定时外设。
SYSTIMER专为系统计时、高精度延时、时间戳统计、周期性中断设计,具备52位超长计数、微秒级高精度、独立时钟域、不掉电计时等特性,是ESP-IDF系统时钟、任务延时、软件定时器的底层硬件依托。
SYSTIMER核心作用是为整个芯片系统提供统一的全局时间基准,所有上层延时函数、时间统计、RTOS节拍、软件定时任务均基于该硬件定时器实现,是嵌入式系统时序运行的核心基石。
2、特性
• 由两个 52 位计数器和三个 52 位比较器组成。
• 软件操作使用 APB_CLK 时钟。
• 计数采用 CNT_CLK 时钟,两次计数周期的平均频率为 16 MHz。
• CNT_CLK 的时钟源为 XTAL_CLK (40 MHz)。
• 支持 52 位报警值 (t) 和 26 位报警周期 (δt)。
• 支持两种报警模式:
单次报警模式:根据设定的目标报警值 (t),生成一次性报警。
周期报警模式:根据设定的报警周期 (δt),生成周期性报警。
• 三个比较器可根据设置的报警值 (t) 或报警周期 (δt) 生成三个独立中断。
• 支持软件配置基准计数值。例如,支持从 Light-sleep 唤醒之后,系统定时器通过软件加载 RTC 定时器记录的睡眠时间,并进行补偿。
• CPU 处于停止状态或处于在线调试状态时,系统定时器可选择停止运行或继续运行。
3、组成结构
系统定时器内置两个计数器(UNIT0 和 UNIT1)以及三个比较器(COMP 0、COMP1 和 COMP2)。
比较器用于监控计数器的计数值是否达到报警值。

二、结构原理
1、时钟源
计数器 / 比较器时钟域:XTAL_CLK(外部晶振),驱动计数器、Alarm 比较器,决定计时精度。
寄存器访问时钟域:APB_CLK(外设总线时钟),驱动软件读写寄存器,不影响计时精度。

2、系统定时器生成报警的过程
XTAL_CLK → DIV分频器 → Timer Counter计数器 → Comparator比较器 → 中断矩阵。

三、核心功能
1、两种工作模式
一次性定时器:到期执行一次回调后自动停止。
周期性定时器:循环触发,需手动esp_timer_stop停止。
2、两种回调分发模式(二选一)
ESP_TIMER_TASK(默认):高优先级专用任务执行回调。
回调可 printf、调用阻塞 API;
多定时器串行执行,存在调度延迟。
ESP_TIMER_ISR(中断分发):直接在硬件中断执行回调。
延迟极低,硬实时;
中断上下文限制(禁止 printf、阻塞函数),需开启 Kconfig ONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH。
3、全局时间戳接口 esp_timer_get_time()
返回开机至今微秒数,快速无锁,任务 / 中断均可调用,适合耗时统计。
4、睡眠适配
LightSleep:定时器暂停,唤醒后自动补偿休眠时长,可配置skip_unhandled_events跳过堆积回调。
DeepSleep:定时器完全复位,唤醒从零计时。
5、调试工具:
esp_timer_dump() 打印所有定时器运行统计(触发次数、跳过次数、回调耗时)。
四、SYSTIMER API
1、头文件/组件
头文件:
#include “esp_timer.h”
组件:
PRIV_REQUIRES esp_timer
2、创建 / 销毁
esp_err_t esp_timer_create(const esp_timer_create_args_t *args, esp_timer_handle_t *out_handle);
esp_err_t esp_timer_delete(esp_timer_handle_t timer);
3、启停控制
// 一次性启动
esp_err_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us);
// 周期性启动
esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us);
// 停止运行
esp_err_t esp_timer_stop(esp_timer_handle_t timer);
// 重启(运行中定时器更新延时)
esp_err_t esp_timer_restart(esp_timer_handle_t timer, uint64_t new_timeout_us);
4、时间获取
// 获取开机微秒时间戳
int64_t esp_timer_get_time(void);
// 获取下一次定时唤醒时间
int64_t esp_timer_get_next_alarm(void);
5、调试
// 打印全部定时器统计信息
esp_err_t esp_timer_dump(FILE *stream);
6、关键结构体
typedef struct {
esp_timer_cb_t callback; // 定时到期回调函数
void *arg; // 回调入参
esp_timer_dispatch_t dispatch_method; // TASK/ISR
const char *name; // 定时器名称(调试dump用)
bool skip_unhandled_events; // 浅睡眠跳过堆积回调
} esp_timer_create_args_t;
7、标准开发流程
1.头文件与组件依赖
#include “esp_timer.h”
main/CMakeLists.txt 必须添加:
PRIV_REQUIRES esp_timer
2.定义定时器配置结构体
esp_timer_create_args_t(绑定回调、分发模式、名称)。
3.创建定时器句柄
esp_timer_create() 。
5.启动定时器
单次:esp_timer_start_once(句柄, 延时us)。
周期:esp_timer_start_periodic(句柄, 周期us)。
6.生命周期管理
暂停:esp_timer_stop()。
重启:esp_timer_restart()。
销毁:esp_timer_delete()(必须先 stop)。
五、示例代码
1、timestamp(时间戳 & 耗时测量)
1.示例代码
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_log.h"
static const char *TAG = "ESP_TIMER_TS";
void app_main(void)
{
ESP_LOGI(TAG, "===== esp_timer 时间戳 & 耗时测量 =====");
// 1. 获取开机时间戳(单位:微秒 us)
int64_t t1 = esp_timer_get_time();
ESP_LOGI(TAG, "开机时间戳 t1 = %lld us", t1);
// 模拟代码执行
vTaskDelay(pdMS_TO_TICKS(100));
int64_t t2 = esp_timer_get_time();
ESP_LOGI(TAG, "开机时间戳 t2 = %lld us", t2);
// 2. 计算耗时
int64_t diff = t2 - t1;
ESP_LOGI(TAG, "vTaskDelay(100ms) 实际耗时 = %lld us = %.3f ms",
diff, diff / 1000.0f);
// 3. 多项耗时对比测试
ESP_LOGI(TAG, "----- 各项操作耗时 -----");
// 空循环 10000 次
{
volatile uint32_t dummy = 0;
int64_t s = esp_timer_get_time();
for (uint32_t i = 0; i < 10000; i++) dummy++;
int64_t e = esp_timer_get_time();
ESP_LOGI(TAG, "空循环10000次 : %lld us", e - s);
}
// memcpy 1KB
{
uint8_t src[1024], dst[1024];
memset(src, 0xAA, sizeof(src));
int64_t s = esp_timer_get_time();
memcpy(dst, src, 1024);
int64_t e = esp_timer_get_time();
ESP_LOGI(TAG, "memcpy 1KB : %lld us", e - s);
}
// printf 一次
{
int64_t s = esp_timer_get_time();
printf("benchmark test\n");
int64_t e = esp_timer_get_time();
ESP_LOGI(TAG, "一次printf : %lld us", e - s);
}
ESP_LOGI(TAG, "===== 测量完成 =====");
// 循环打印当前时间
uint32_t cnt = 0;
while (1)
{
ESP_LOGI(TAG, "[%lu] now = %lld us",
(unsigned long)cnt++, esp_timer_get_time());
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
2.执行结果

2、oneshot(一次性定时器,TASK 模式)
1.示例代码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_log.h"
static const char *TAG = "ESP_TIMER_ONESHOT";
#define DELAY_US (500 * 1000) // 500ms 后触发
static esp_timer_handle_t s_oneshot_timer = NULL;
/* ===== 回调函数(任务上下文,可直接 printf/ESP_LOG) ===== */
static void oneshot_cb(void *arg)
{
(void)arg;
int64_t now = esp_timer_get_time();
ESP_LOGI(TAG, "一次性定时器触发! time = %lld us", now);
// 用完销毁
esp_timer_delete(s_oneshot_timer);
s_oneshot_timer = NULL;
ESP_LOGI(TAG, "定时器已销毁");
}
void app_main(void)
{
ESP_LOGI(TAG, "===== esp_timer 一次性定时器 =====");
ESP_LOGI(TAG, "延时 %d us (%d ms) 后触发", DELAY_US, DELAY_US / 1000);
// 1. 配置定时器参数
const esp_timer_create_args_t timer_args = {
.callback = oneshot_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_TASK, // 任务上下文(默认)
.name = "oneshot_500ms",
.skip_unhandled_events = false,
};
// 2. 创建定时器
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &s_oneshot_timer));
// 3. 启动一次性定时
ESP_ERROR_CHECK(esp_timer_start_once(s_oneshot_timer, DELAY_US));
ESP_LOGI(TAG, "一次性定时器已启动");
// 打印当前时间
while (1)
{
ESP_LOGI(TAG, "main loop running... now = %lld us",
esp_timer_get_time());
vTaskDelay(pdMS_TO_TICKS(200));
}
}
2.执行结果

3、periodic(周期性定时器,TASK 模式)
1.示例代码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_log.h"
static const char *TAG = "ESP_TIMER_PERIOD";
#define PERIOD_US (200 * 1000) // 周期 200ms
static esp_timer_handle_t s_period_timer = NULL;
static uint32_t s_trigger_cnt = 0;
/* ===== 回调函数(任务上下文,可直接打印日志) ===== */
static void period_cb(void *arg)
{
(void)arg;
s_trigger_cnt++;
int64_t now = esp_timer_get_time();
ESP_LOGI(TAG, "[%lu] 周期触发 time = %lld us",
(unsigned long)s_trigger_cnt, now);
}
void app_main(void)
{
ESP_LOGI(TAG, "===== esp_timer 周期性定时器 =====");
ESP_LOGI(TAG, "周期 = %d us (%d ms)", PERIOD_US, PERIOD_US / 1000);
// 1. 配置定时器参数
const esp_timer_create_args_t timer_args = {
.callback = period_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_TASK, // 任务上下文
.name = "period_200ms",
.skip_unhandled_events = false,
};
// 2. 创建定时器
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &s_period_timer));
// 3. 启动周期性定时
ESP_ERROR_CHECK(esp_timer_start_periodic(s_period_timer, PERIOD_US));
ESP_LOGI(TAG, "周期定时器已启动");
// 5秒后停止演示
vTaskDelay(pdMS_TO_TICKS(5000));
ESP_LOGI(TAG, "----- 停止定时器 -----");
ESP_ERROR_CHECK(esp_timer_stop(s_period_timer));
ESP_LOGI(TAG, "定时器已停止,总触发次数:%lu", (unsigned long)s_trigger_cnt);
// 2秒后重启
vTaskDelay(pdMS_TO_TICKS(2000));
ESP_LOGI(TAG, "----- 重启定时器 -----");
ESP_ERROR_CHECK(esp_timer_start_periodic(s_period_timer, PERIOD_US));
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
2.执行结果

4、isr_mode(ISR 中断分发模式)
meunconfig配置:
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
1.示例代码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_timer.h"
#include "esp_log.h"
static const char *TAG = "ESP_TIMER_ISR";
#define PERIOD_US (100 * 1000) // 100ms
static esp_timer_handle_t s_isr_timer = NULL;
static QueueHandle_t s_queue = NULL;
/* ===== 回调函数(中断上下文!禁止 printf/ESP_LOG) ===== */
static void isr_mode_cb(void *arg)
{
(void)arg;
BaseType_t yield = pdFALSE;
uint32_t sig = 1;
// 只能使用 FromISR 系列 API
xQueueSendFromISR(s_queue, &sig, &yield);
if (yield) {
portYIELD_FROM_ISR();
}
}
/* ===== 处理任务(任务上下文,允许打印) ===== */
static void process_task(void *arg)
{
(void)arg;
uint32_t sig;
uint32_t cnt = 0;
while (1)
{
if (xQueueReceive(s_queue, &sig, portMAX_DELAY))
{
cnt++;
int64_t now = esp_timer_get_time();
ESP_LOGI(TAG, "[%lu] ISR模式触发 time = %lld us",
(unsigned long)cnt, now);
}
}
}
void app_main(void)
{
ESP_LOGI(TAG, "===== esp_timer ISR 中断分发模式 =====");
ESP_LOGI(TAG, "周期 = %d us (%d ms)", PERIOD_US, PERIOD_US / 1000);
// 1. 创建队列
s_queue = xQueueCreate(4, sizeof(uint32_t));
// 2. 配置定时器(ISR 模式)
const esp_timer_create_args_t timer_args = {
.callback = isr_mode_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_ISR, // 中断分发模式
.name = "isr_100ms",
.skip_unhandled_events = false,
};
// 3. 创建定时器
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &s_isr_timer));
// 4. 启动
ESP_ERROR_CHECK(esp_timer_start_periodic(s_isr_timer, PERIOD_US));
// 5. 创建处理任务
xTaskCreate(process_task, "isr_proc", 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "ISR模式定时器已启动");
// 调试:打印所有定时器信息
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI(TAG, "----- 定时器调试信息 -----");
esp_timer_dump(stdout);
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
2.执行结果

转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/Black_Leopard/article/details/166595730



