ESP32-S3-TIMG(定时器组)

文章目录

  • 一、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.执行结果

      

相关推荐
天空'之城1 小时前
单片机基础核心知识点汇总(四十五)
单片机·嵌入式硬件
fan_0002 小时前
国密TLCP协议,LKT4305GMT护航物联网安全通信
单片机·嵌入式硬件·mcu·物联网·加密芯片
Naisu Xu2 小时前
记一次STM32H503冷启动后进入HardFult_Handler的问题
stm32·单片机·嵌入式硬件·调试·内存问题
程序员正茂2 小时前
Keil中将device由 STM32F103ZE 改为 STM32F103C8
stm32·单片机·嵌入式硬件
Zwawa2 小时前
定时器到底在数什么:STM32 的时间基准从哪里来
单片机·嵌入式
LCG元3 小时前
STM32实战:基于STM32F103的智能路灯系统(光敏检测+人体感应+自动照明)
stm32·单片机·嵌入式硬件
张海森-1688203 小时前
将 aac音频数据存为 aac文件,并给相应权限
单片机
LCG元3 小时前
STM32F103 I2C 驱动 BMP280 气压温度传感器实战:校准补偿公式拆解、IIR 滤波整定与海拔换算实测
stm32·单片机·嵌入式硬件
Amos zhang4 小时前
AWTK 开发实践(三):AMT630HV100 SDK 移植调用链分析
单片机·嵌入式·awtk