文章目录
- 一、TIMG
-
- [ 1、TIMG](# 1、TIMG)
- [ 2、硬件架构](# 2、硬件架构)
- [ 3、定时器功能:](# 3、定时器功能:)
- 二、结构原理
-
- [ 1、结构框图](# 1、结构框图)
- [ 2、结构模块](# 2、结构模块)
-
- [ 1. 时钟选择模块(Clock 区域)](# 1. 时钟选择模块(Clock 区域))
- [ 2. Inc/Dec Counter 双向计数器(54 位)](# 2. Inc/Dec Counter 双向计数器(54 位))
- [ 3. Comparator 比较器(报警模块)](# 3. Comparator 比较器(报警模块))
- 三、GPTimer
-
- [ 1、GPTimer](# 1、GPTimer)
- [ 2、关键参数](# 2、关键参数)
- [四、GPTimer API](#四、GPTimer API)
-
- [ 1、头文件/组件](# 1、头文件/组件)
- [ 2、结构体](# 2、结构体)
-
- [ 1. gptimer_config_t 基础配置](# 1. gptimer_config_t 基础配置)
- [ 2. gptimer_alarm_config_t 报警配置](# 2. gptimer_alarm_config_t 报警配置)
- [ 3. 回调结构体](# 3. 回调结构体)
- [ 3、定时器生命周期管理 API](# 3、定时器生命周期管理 API)
-
- [ 1. gptimer_new_timer](# 1. gptimer_new_timer)
- [ 2. gptimer_del_timer](# 2. gptimer_del_timer)
- [ 3. gptimer_enable](# 3. gptimer_enable)
- [ 4. gptimer_disable](# 4. gptimer_disable)
- [ 5. gptimer_start](# 5. gptimer_start)
- [ 6. gptimer_stop](# 6. gptimer_stop)
- [ 4、报警 & 中断回调 API](# 4、报警 & 中断回调 API)
-
- [ 1. gptimer_set_alarm_action](# 1. gptimer_set_alarm_action)
- [ 2. gptimer_register_event_callbacks](# 2. gptimer_register_event_callbacks)
- [ 5、计数值读写 API](# 5、计数值读写 API)
-
- [ 1. gptimer_get_raw_count](# 1. gptimer_get_raw_count)
- [ 2. gptimer_set_raw_count](# 2. gptimer_set_raw_count)
- [ 6、辅助工具 API](# 6、辅助工具 API)
-
- [ 1. gptimer_get_resolution](# 1. gptimer_get_resolution)
- [ 2. gptimer_apply_alarm_config](# 2. gptimer_apply_alarm_config)
- 五、示例代码
-
- [ 1、periodic(周期性中断)](# 1、periodic(周期性中断))
-
- [ 1.示例代码](# 1.示例代码)
- [ 2.执行结果](# 2.执行结果)
- [ 2、oneshot(单次定时)](# 2、oneshot(单次定时))
-
- [ 1.示例代码](# 1.示例代码)
- [ 2.执行结果](# 2.执行结果)
- [ 3、benchmark(计数值读取 & 耗时测量)](# 3、benchmark(计数值读取 & 耗时测量))
-
- [ 1.示例代码](# 1.示例代码)
- [ 2.执行结果](# 2.执行结果)
- [ 4、dynamic(动态修改周期)](# 4、dynamic(动态修改周期))
-
- [ 1.示例代码](# 1.示例代码)
- [ 2.执行结果](# 2.执行结果)
- [ 5、capture(输入捕获模式)](# 5、capture(输入捕获模式))
-
- [ 1.示例代码](# 1.示例代码)
- [ 2.执行结果](# 2.执行结果)
一、TIMG
1、TIMG
TIMG(Timer Group,定时器组)是ESP32-S3内置的通用硬件定时器外设,属于芯片基础定时资源,主打通用周期性定时、超时计数、软件看门狗驱动。
2、硬件架构
ESP32-S3 包含两个定时器组,即定时器组 0 和定时器组 1。
每个定时器组有两个通用定时器和一个主系统看门狗定时器。
所有通用定时器均基于 16 位预分频器和 54 位可自动重新加载向上/向下计数器。

3、定时器功能:
16 位时钟预分频器,分频系数为 2 到 65536。
54 位时基计数器可配置成递增或递减。
可读取时基计数器的实时值。
暂停和恢复时基计数器。
可配置的报警产生机制。
计数器值重新加载(报警时自动重新加载或软件控制的即时重新加载)。
电平触发中断。
二、结构原理
1、结构框图

2、结构模块
1. 时钟选择模块(Clock 区域)
多路选择器控制位:TIMG_Tx_USE_XTAL
0:选择 APB_CLK(80MHz)。
1:选择 XTAL_CLK(32MHz)。
信号进入 16 位整数预分频器 Int Divider。
分频配置寄存器:TIMG_Tx_DIVIDER
分频公式:TB_CLK = 输入时钟 / DIVIDER。
输出:TB_CLK,作为计数器的工作时钟。
关键知识点:
所有寄存器读写、软件配置信号使用 APB_CLK。
计数器硬件计数动作使用独立 TB_CLK,两者时钟域分离。
2. Inc/Dec Counter 双向计数器(54 位)
控制信号:
TIMG_Tx_EN:定时器使能开关,1 = 启动计数,0 = 暂停。
TIMG_Tx_INCREASE:计数方向。
1:向上递增计数
0:向下递减计数
输入时钟:
TB_CLK,每来一个时钟沿计数值 TIMG_VALUE +1 / -1。
配套寄存器:
TIMG_Tx_LOAD_LO / LOAD_HI:初始加载值。
写入TIMG_TxLOAD_REG任意值,硬件加载初值。
读取当前计数值前,必须写TIMG_TxUPDATE_REG锁存硬件实时数值。
3. Comparator 比较器(报警模块)
两路输入:
TIMG_VALUE:计数器实时计数值。
ALARM_VALUE:预设报警阈值(TIMG_TxALARMLO_REG + TIMG_TxALARMHI_REG 拼接 54 位)。
控制信号:TIMG_Tx_ALARM_EN,使能比较报警功能。
触发逻辑:
当 计数器数值 == 报警阈值 → 比较器输出 TIMG_Tx_INT 中断信号,输送至中断矩阵。
三、GPTimer
1、GPTimer
GPTimer通用定时器是定时器组外设的专用驱动程序。
该定时器可以选择不同的时钟源和分频系数,能满足纳秒级的分辨率要求。
2、关键参数
Resolution(计数分辨率):
每计 1 步代表多少时间,单位 Hz。
例:resolution_hz = 1000000 → 1 tick = 1μs。
驱动内部自动换算预分频器 TIMG_Tx_DIVIDER。
Alarm Count(报警计数值):
计数器走到该数值触发报警中断。
Auto Reload(自动重载):
触发报警后,计数器自动加载 reload_count 数值,持续周期性运行。
四、GPTimer API
1、头文件/组件
头文件:
#include "driver/gptimer.h"
组件:
REQUIRES esp_driver_gptimer
2、结构体
1. gptimer_config_t 基础配置
c
typedef struct {
gptimer_clock_source_t clk_src; // 时钟源:GPTIMER_CLK_SRC_APB / GPTIMER_CLK_SRC_XTAL
gptimer_count_direction_t direction; // GPTIMER_COUNT_UP / GPTIMER_COUNT_DOWN
uint32_t resolution_hz; // 计数器分辨率
int intr_priority; // 中断优先级,0=默认
} gptimer_config_t;
2. gptimer_alarm_config_t 报警配置
c
typedef struct {
uint64_t alarm_count; // 触发报警的计数值
uint64_t reload_count; // 自动重载载入初值
struct {
uint32_t auto_reload_on_alarm : 1; // 是否开启周期重载
} flags;
} gptimer_alarm_config_t;
3. 回调结构体
c
typedef struct {
uint64_t count_value; // 触发报警时计数器当前值
} gptimer_alarm_event_data_t;
// 回调函数原型
typedef bool (*gptimer_alarm_cb_t)(gptimer_handle_t timer,
const gptimer_alarm_event_data_t *edata,
void *user_ctx);
// 回调集合
typedef struct {
gptimer_alarm_cb_t on_alarm;
} gptimer_event_callbacks_t;
注意:on_alarm 运行在【中断上下文】。
限制:禁止printf、ESP_LOG、任何阻塞 API、malloc/free。
标准方案:xQueueSendFromISR()通知任务处理业务。
3、定时器生命周期管理 API
1. gptimer_new_timer
esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer);
功能:分配硬件资源,创建 GPTimer 实例。
返回:ESP_OK / ESP_ERR_NOT_FOUND / ESP_ERR_INVALID_ARG。
2. gptimer_del_timer
esp_err_t gptimer_del_timer(gptimer_handle_t timer);
功能:销毁定时器、释放硬件资源;必须先 stop、disable。
3. gptimer_enable
esp_err_t gptimer_enable(gptimer_handle_t timer);
使能定时器硬件,准备启动;必须在start()之前调用。
4. gptimer_disable
esp_err_t gptimer_disable(gptimer_handle_t timer);
关闭定时器硬件。
5. gptimer_start
esp_err_t gptimer_start(gptimer_handle_t timer);
启动计数器开始计数。
6. gptimer_stop
esp_err_t gptimer_stop(gptimer_handle_t timer);
暂停计数,计数值保留。
4、报警 & 中断回调 API
1. gptimer_set_alarm_action
esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *alarm_cfg);
设置报警阈值、自动重载模式;运行时可动态调用修改定时时间。
2. gptimer_register_event_callbacks
esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer,
const gptimer_event_callbacks_t *cbs,
void *user_ctx);
注册报警中断回调;user_ctx 会透传给回调函数。
5、计数值读写 API
1. gptimer_get_raw_count
esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, uint64_t *value);
读取计数器当前原始 tick 值。
2. gptimer_set_raw_count
esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, uint64_t value);
手动修改计数器当前计数值(定时器暂停状态使用更稳妥)。
6、辅助工具 API
1. gptimer_get_resolution
esp_err_t gptimer_get_resolution(gptimer_handle_t timer, uint32_t *resolution_hz);
获取定时器分辨率。
2. gptimer_apply_alarm_config
运行时动态更新报警(等价于gptimer_set_alarm_action)。
五、示例代码
1、periodic(周期性中断)
1.示例代码
c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "esp_log.h"
static const char *TAG = "GPTIMER_PERIOD";
#define PERIOD_US 1000000 // 1秒 = 1000000us
static gptimer_handle_t s_timer = NULL;
static QueueHandle_t s_queue = NULL;
/* ===== 中断回调(中断上下文!禁止printf/ESP_LOG) ===== */
static bool IRAM_ATTR alarm_cb(gptimer_handle_t timer,
const gptimer_alarm_event_data_t *edata,
void *user_ctx)
{
BaseType_t yield = pdFALSE;
uint32_t sig = 1;
xQueueSendFromISR(s_queue, &sig, &yield);
return yield;
}
/* ===== 处理任务(任务上下文,允许打印) ===== */
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++;
uint64_t count;
gptimer_get_raw_count(s_timer, &count);
ESP_LOGI(TAG, "[周期中断] #%lu count=%llu",
(unsigned long)cnt, (unsigned long long)count);
}
}
}
void app_main(void)
{
ESP_LOGI(TAG, "===== GPTimer 周期性中断 =====");
ESP_LOGI(TAG, "周期 = %d us (%d ms)", PERIOD_US, PERIOD_US / 1000);
// 1. 创建队列
s_queue = xQueueCreate(4, sizeof(uint32_t));
// 2. 定时器基础配置
gptimer_config_t timer_cfg = {
.clk_src = GPTIMER_CLK_SRC_APB, // APB 80MHz
.direction = GPTIMER_COUNT_UP, // 向上计数
.resolution_hz = 1000000, // 1MHz → 1tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_cfg, &s_timer));
// 3. 报警配置:自动重载 = 周期模式
gptimer_alarm_config_t alarm_cfg = {
.alarm_count = PERIOD_US,
.reload_count = 0,
.flags.auto_reload_on_alarm = true,
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(s_timer, &alarm_cfg));
// 4. 注册中断回调
gptimer_event_callbacks_t cb_cfg = {
.on_alarm = alarm_cb
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(s_timer, &cb_cfg, NULL));
// 5. 使能 + 启动
ESP_ERROR_CHECK(gptimer_enable(s_timer));
ESP_ERROR_CHECK(gptimer_start(s_timer));
xTaskCreate(process_task, "period", 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "周期定时器已启动");
}
2.执行结果

2、oneshot(单次定时)
1.示例代码
c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "esp_log.h"
static const char *TAG = "GPTIMER_ONESHOT";
#define DELAY_US 500000 // 500ms 后触发
static gptimer_handle_t s_timer = NULL;
static QueueHandle_t s_queue = NULL;
/* ===== 中断回调(中断上下文) ===== */
static bool IRAM_ATTR alarm_cb(gptimer_handle_t timer,
const gptimer_alarm_event_data_t *edata,
void *user_ctx)
{
BaseType_t yield = pdFALSE;
uint32_t sig = 1;
xQueueSendFromISR(s_queue, &sig, &yield);
return yield;
}
/* ===== 处理任务 ===== */
static void oneshot_task(void *arg)
{
(void)arg;
uint32_t sig;
if (xQueueReceive(s_queue, &sig, portMAX_DELAY))
{
ESP_LOGI(TAG, "===== 单次定时触发 =====");
ESP_LOGI(TAG, "%d us 延时到达!", DELAY_US);
// 用完销毁资源
gptimer_stop(s_timer);
gptimer_disable(s_timer);
gptimer_del_timer(s_timer);
s_timer = NULL;
ESP_LOGI(TAG, "定时器已销毁");
}
vTaskDelete(NULL);
}
void app_main(void)
{
ESP_LOGI(TAG, "===== GPTimer 单次定时 =====");
ESP_LOGI(TAG, "延时 = %d us (%d ms)", DELAY_US, DELAY_US / 1000);
// 1. 创建队列
s_queue = xQueueCreate(2, sizeof(uint32_t));
// 2. 定时器配置
gptimer_config_t timer_cfg = {
.clk_src = GPTIMER_CLK_SRC_APB,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000,
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_cfg, &s_timer));
// 3. 单次模式:关闭自动重载
gptimer_alarm_config_t alarm_cfg = {
.alarm_count = DELAY_US,
.flags.auto_reload_on_alarm = false, // false = 单次
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(s_timer, &alarm_cfg));
// 4. 注册回调
gptimer_event_callbacks_t cb_cfg = {
.on_alarm = alarm_cb
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(s_timer, &cb_cfg, NULL));
// 5. 启动
ESP_ERROR_CHECK(gptimer_enable(s_timer));
ESP_ERROR_CHECK(gptimer_start(s_timer));
xTaskCreate(oneshot_task, "oneshot", 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "单次定时器已启动,等待触发...");
}
2.执行结果

3、benchmark(计数值读取 & 耗时测量)
1.示例代码
c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gptimer.h"
#include "esp_log.h"
static const char *TAG = "GPTIMER_BENCH";
static gptimer_handle_t s_timer = NULL;
/* ===== 便捷宏 ===== */
#define BENCH_START() uint64_t __t1, __t2; gptimer_get_raw_count(s_timer, &__t1)
#define BENCH_END() gptimer_get_raw_count(s_timer, &__t2)
#define BENCH_US() (uint64_t)(__t2 - __t1)
void app_main(void)
{
ESP_LOGI(TAG, "===== GPTimer 耗时测量 =====");
// 1. 创建定时器(1us 精度)
gptimer_config_t timer_cfg = {
.clk_src = GPTIMER_CLK_SRC_APB,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000, // 1MHz → 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_cfg, &s_timer));
ESP_ERROR_CHECK(gptimer_enable(s_timer));
ESP_ERROR_CHECK(gptimer_start(s_timer));
ESP_LOGI(TAG, "定时器已启动,开始测量...");
ESP_LOGI(TAG, "---------------------------");
// 测试1:vTaskDelay 实际耗时
{
BENCH_START();
vTaskDelay(pdMS_TO_TICKS(10));
BENCH_END();
ESP_LOGI(TAG, "vTaskDelay(10ms) : %llu us",
(unsigned long long)BENCH_US());
}
// 测试2:空循环 10000 次
{
volatile uint32_t dummy = 0;
BENCH_START();
for (uint32_t i = 0; i < 10000; i++) dummy++;
BENCH_END();
ESP_LOGI(TAG, "空循环10000次 : %llu us",
(unsigned long long)BENCH_US());
}
// 测试3:printf 一次
{
BENCH_START();
printf("benchmark test line\n");
BENCH_END();
ESP_LOGI(TAG, "一次printf : %llu us",
(unsigned long long)BENCH_US());
}
// 测试4:memcpy 1KB
{
uint8_t src[1024], dst[1024];
memset(src, 0xAA, sizeof(src));
BENCH_START();
memcpy(dst, src, 1024);
BENCH_END();
ESP_LOGI(TAG, "memcpy 1KB : %llu us",
(unsigned long long)BENCH_US());
}
// 测试5:ESP_LOGI 一次
{
BENCH_START();
ESP_LOGI(TAG, "test log message");
BENCH_END();
ESP_LOGI(TAG, "一次ESP_LOGI : %llu us",
(unsigned long long)BENCH_US());
}
ESP_LOGI(TAG, "---------------------------");
ESP_LOGI(TAG, "测量完成");
// 循环打印当前计数值
uint32_t cnt = 0;
while (1)
{
uint64_t now;
gptimer_get_raw_count(s_timer, &now);
ESP_LOGI(TAG, "[%lu] count = %llu us",
(unsigned long)cnt++, (unsigned long long)now);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
2.执行结果

4、dynamic(动态修改周期)
1.示例代码
c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "esp_log.h"
static const char *TAG = "GPTIMER_DYN";
#define INIT_PERIOD_US 500000 // 初始 500ms
#define NEW_PERIOD_US 200000 // 改为 200ms
static gptimer_handle_t s_timer = NULL;
static QueueHandle_t s_queue = NULL;
/* ===== 中断回调 ===== */
static bool IRAM_ATTR alarm_cb(gptimer_handle_t timer,
const gptimer_alarm_event_data_t *edata,
void *user_ctx)
{
BaseType_t yield = pdFALSE;
uint32_t sig = 1;
xQueueSendFromISR(s_queue, &sig, &yield);
return yield;
}
/* ===== 处理任务:触发3次后动态修改周期 ===== */
static void dynamic_task(void *arg)
{
(void)arg;
uint32_t sig;
uint32_t cnt = 0;
bool changed = false;
while (1)
{
if (xQueueReceive(s_queue, &sig, portMAX_DELAY))
{
cnt++;
ESP_LOGI(TAG, "[%lu] 触发", (unsigned long)cnt);
// 第3次触发后,动态改为 200ms 周期
if (cnt == 3 && !changed)
{
changed = true;
ESP_LOGI(TAG, ">>> 修改周期为 %d us <<<", NEW_PERIOD_US);
// 动态修改步骤:stop → 清零 → 设新报警 → start
gptimer_stop(s_timer);
gptimer_set_raw_count(s_timer, 0);
gptimer_alarm_config_t new_alarm = {
.alarm_count = NEW_PERIOD_US,
.reload_count = 0,
.flags.auto_reload_on_alarm = true,
};
gptimer_set_alarm_action(s_timer, &new_alarm);
gptimer_start(s_timer);
}
}
}
}
void app_main(void)
{
ESP_LOGI(TAG, "===== GPTimer 动态修改周期 =====");
ESP_LOGI(TAG, "初始周期 = %d us", INIT_PERIOD_US);
ESP_LOGI(TAG, "目标周期 = %d us", NEW_PERIOD_US);
// 1. 创建队列
s_queue = xQueueCreate(4, sizeof(uint32_t));
// 2. 定时器配置
gptimer_config_t timer_cfg = {
.clk_src = GPTIMER_CLK_SRC_APB,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000,
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_cfg, &s_timer));
// 3. 初始报警配置
gptimer_alarm_config_t alarm_cfg = {
.alarm_count = INIT_PERIOD_US,
.reload_count = 0,
.flags.auto_reload_on_alarm = true,
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(s_timer, &alarm_cfg));
// 4. 注册回调
gptimer_event_callbacks_t cb_cfg = {
.on_alarm = alarm_cb
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(s_timer, &cb_cfg, NULL));
// 5. 启动
ESP_ERROR_CHECK(gptimer_enable(s_timer));
ESP_ERROR_CHECK(gptimer_start(s_timer));
xTaskCreate(dynamic_task, "dynamic", 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "动态周期定时器已启动");
}
2.执行结果

5、capture(输入捕获模式)
测量外部脉冲信号的宽度、频率。
捕获输入引脚:GPIO_NUM_0(BOOT 键,按下产生下降沿 / 上升沿)。
1.示例代码
c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "driver/gpio.h"
#include "esp_log.h"
static const char *TAG = "GPTIMER_CAP";
#define CAPTURE_GPIO GPIO_NUM_0
static gptimer_handle_t s_timer = NULL;
static QueueHandle_t s_edge_queue = NULL;
// 边沿事件
typedef enum {
EDGE_RISING,
EDGE_FALLING
} signal_edge_t;
// 【重点】GPIO中断函数:返回值 void,不能返回bool!
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
BaseType_t yield = pdFALSE;
signal_edge_t edge;
uint32_t gpio_level = gpio_get_level(CAPTURE_GPIO);
if(gpio_level == 1) {
edge = EDGE_RISING;
} else {
edge = EDGE_FALLING;
}
xQueueSendFromISR(s_edge_queue, &edge, &yield);
portYIELD_FROM_ISR(yield);
}
static void capture_process_task(void *arg)
{
(void)arg;
signal_edge_t edge;
uint64_t rise_time = 0;
bool rise_saved = false;
ESP_LOGI(TAG, "脉冲捕获启动, GPIO%d 测试(BOOT按键)", CAPTURE_GPIO);
while(1)
{
if(xQueueReceive(s_edge_queue, &edge, portMAX_DELAY))
{
uint64_t now_us;
gptimer_get_raw_count(s_timer, &now_us);
if(edge == EDGE_RISING)
{
ESP_LOGI(TAG, "上升沿 t=%llu us", (unsigned long long)now_us);
rise_time = now_us;
rise_saved = true;
}
else if(edge == EDGE_FALLING)
{
ESP_LOGI(TAG, "下降沿 t=%llu us", (unsigned long long)now_us);
if(rise_saved)
{
uint64_t pulse_width = now_us - rise_time;
ESP_LOGI(TAG, "==== 脉冲宽度: %llu us (%.3f ms) ====",
(unsigned long long)pulse_width, pulse_width / 1000.0f);
rise_saved = false;
}
}
}
}
}
void app_main(void)
{
// 1. GPIO初始化,双边沿中断
gpio_config_t gpio_conf = {
.pin_bit_mask = 1ULL << CAPTURE_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_ANYEDGE,
};
gpio_config(&gpio_conf);
// 2. 创建事件队列
s_edge_queue = xQueueCreate(8, sizeof(signal_edge_t));
// 3. GPTimer 自由运行,作为高精度时基
gptimer_config_t timer_cfg = {
.clk_src = GPTIMER_CLK_SRC_APB,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000, // 1us分辨率
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_cfg, &s_timer));
ESP_ERROR_CHECK(gptimer_enable(s_timer));
ESP_ERROR_CHECK(gptimer_start(s_timer));
// 注册GPIO中断服务
gpio_install_isr_service(0);
gpio_isr_handler_add(CAPTURE_GPIO, gpio_isr_handler, NULL);
xTaskCreate(capture_process_task, "capture_task", 3072, NULL, 5, NULL);
ESP_LOGI(TAG, "GPTimer 时基 + GPIO中断脉冲测量 启动成功");
}
2.执行结果
