当前位置: 首页 > news >正文

ESP32-IDF 通用定时器 GPTimer

目录

  • 一、基本介绍
    • 1、配置结构体
      • 1.1 gptimer_config_t
      • 1.2 gptimer_event_callbacks_t
      • 1.3 gptimer_alarm_config_t
    • 2、常用 API
      • 2.1 gptimer_new_timer
      • 2.2 gptimer_del_timer
      • 2.3 gptimer_set_raw_count
      • 2.4 gptimer_get_raw_count
      • 2.5 gptimer_get_resolution
      • 2.6 gptimer_get_captured_count
      • 2.7 gptimer_register_event_callbacks
      • 2.8 gptimer_set_alarm_action
      • 2.9 gptimer_enable
      • 2.10 gptimer_disable
      • 2.11 gptimer_start
      • 2.12 gptimer_stop
    • 3、枚举类型
      • 3.1 gptimer_clock_source_t
      • 3.2 gptimer_count_direction_t
      • 3.3 gptimer_etm_task_type_t
      • 3.4 gptimer_etm_event_type_t
  • 二、GTIM 配置
    • 1、资源分配
    • 2、设置和获取计数值
    • 3、设置警报动作
    • 4、注册事件回调函数
    • 5、使能和禁用定时器
    • 6、启动和停止定时器
    • 7、电源管理
    • 8、线程安全
  • 三、实例操作
    • 1、核心代码讲解
    • 2、完整代码和效果演示


一、基本介绍

1、配置结构体

1.1 gptimer_config_t

typedef struct {gptimer_clock_source_t clk_src;      /*!< GPTimer clock source */gptimer_count_direction_t direction; /*!< Count direction */uint32_t resolution_hz;              /*!< Counter resolution (working frequency) in Hz,hence, the step size of each count tick equals to (1 / resolution_hz) seconds */int intr_priority;                   /*!< GPTimer interrupt priority,if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */struct {uint32_t intr_shared: 1;         /*!< Set true, the timer interrupt number can be shared with other peripherals */uint32_t backup_before_sleep: 1; /*!< If set, the driver will backup/restore the GPTimer registers before/after entering/exist sleep mode.By this approach, the system can power off GPTimer's power domain.This can save power, but at the expense of more RAM being consumed */} flags;                             /*!< GPTimer config flags*/
} gptimer_config_t;
  • clk_src:时钟源
  • direction:计数方向(如递增、递减)
  • resolution_hz:计数器分辨率(工作频率),以 Hz 为单位,因此,每个计数滴答的步长等于(1 / resolution_hz)秒
  • intr_priority:GPTimer 中断优先级,如果设置为 0,驱动程序将尝试分配优先级相对较低的中断 (1,2,3)
  • flags
    • intr_shared:设置为 1,定时器中断号可以与其他外设共享
    • backup_before_sleep:设置为 1,驱动程序会在进入睡眠模式前备份并在恢复时恢复 GPTimer 的寄存器。这可以节省电力,但会增加 RAM 的使用。

1.2 gptimer_event_callbacks_t

一组支持 GPTimer 的回调函数。

这些回调都在 ISR 环境下运行

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; /*!< Timer alarm callback */
} gptimer_event_callbacks_t;
  • on_alarm:alarm 回调函数

补充

这个结构体保存的是 gptimer alarm 的数据:

typedef struct {uint64_t count_value; /* 定时器自启动以来的累积计数 */uint64_t alarm_value; /* 触发警报所需达到的计数阈值 */
} gptimer_alarm_event_data_t;

1.3 gptimer_alarm_config_t

配置 GPTimer 的 alarm

typedef struct {uint64_t alarm_count;  /*!< Alarm target count value */uint64_t reload_count; /*!< Alarm reload count value, effect only when `auto_reload_on_alarm` is set to true */struct {uint32_t auto_reload_on_alarm: 1; /*!< Reload the count value by hardware, immediately at the alarm event */} flags;                              /*!< Alarm config flags*/
} gptimer_alarm_config_t;
  • alarm_count:报警触发的目标计数值。当定时器的计数器达到这个值时,将触发报警事件。
  • reload_count:报警触发后重新加载的计数值。这个值只有在 auto_reload_on_alarm 被设置为 true 时才会生效,定时器会在触发报警后重新加载这个值作为新的计数值。
  • auto_reload_on_alarm:标志位,表示是否在报警事件发生时自动重载计数值。如果设置为 true,则在触发报警后,定时器将自动将计数器的值重置为 reload_count

2、常用 API

需要包含头文件 #include "driver/gptimer.h"

2.1 gptimer_new_timer

esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *ret_timer)
  • 参数
    • config:GPTimer 配置结构体
    • ret_timer:返回的 timer 句柄
  • 作用
    • 创建一个新的 GPTimer,并返回 handle。
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_ERR_NO_MEM:内存不足
    • ESP_ERR_NOT_FOUND:所有硬件计时器都已用完,没有更多空闲计时器
    • ESP_FAIL:由于其他错误

2.2 gptimer_del_timer

esp_err_t gptimer_del_timer(gptimer_handle_t timer)
  • 参数
    • timer:timer 句柄
  • 作用
    • 删除 GPTimer 句柄。
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_ERR_INVALID_STATE:计时器未处于 init 状态
    • ESP_FAIL:由于其他错误

计时器必须处于 “init” 状态,然后才能删除。

2.3 gptimer_set_raw_count

esp_err_t gptimer_set_raw_count(gptimer_handle_t timer, uint64_t value)
  • 参数
    • timer:timer 句柄
    • value:要设置的 Count 值
  • 作用
    • 设置 GPTimer 原始计数值。
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_FAIL:由于其他错误
  • 当更新活动计时器的原始计数时,计时器将立即开始从新值开始计数。
  • 允许此函数在 ISR 上下文中运行

2.4 gptimer_get_raw_count

esp_err_t gptimer_get_raw_count(gptimer_handle_t timer, uint64_t *value)
  • 参数
    • timer:timer 句柄
    • value:保存获取的 Count 值
  • 作用
    • 获取 GPTimer 原始计数值。
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_FAIL:由于其他错误
  • 使用原始计数值和从 gptimer_get_resolution 返回的分辨率,您可以将计数值转换为秒。
  • 此函数将触发软件捕获事件,然后返回捕获的计数值。
  • 允许此函数在 ISR 上下文中运行

2.5 gptimer_get_resolution

esp_err_t gptimer_get_resolution(gptimer_handle_t timer, uint32_t *out_resolution)
  • 参数
    • timer:timer 句柄
    • out_resolution:返回的计时器分辨率,以 Hz 为单位
  • 作用
    • 返回计时器的实际分辨率。
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_FAIL:由于其他错误

通常 timer 分辨率与您在 gptimer_config_t::resolution_hz 配置的相同,但一些不稳定的 clock source (例如 RC_FAST) 会进行校准,实际分辨率可能与配置的分辨率不同。

2.6 gptimer_get_captured_count

esp_err_t gptimer_get_captured_count(gptimer_handle_t timer, uint64_t *value)
  • 参数
    • timer:timer 句柄
    • value:保存捕获到的 count 值
  • 作用
    • 获取 GPTimer 捕获到的 count 值
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_FAIL:由于其他错误

2.7 gptimer_register_event_callbacks

esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data)
  • 参数
    • timer:timer 句柄
    • cbs:回调函数组
    • user_data:用户数据,将直接传递给回调函数
  • 作用
    • 设置 GPTimer 回调函数
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_ERR_INVALID_STATE:设置事件回调失败,因为计时器未处于 init 状态
    • ESP_FAIL:由于其他错误
  • 用户注册的回调应在 ISR 上下文中运行
  • 对此函数的第一次调用需要在调用 gptimer_enable 之前
  • 用户可以通过调用此函数并将 cbs 结构中的 callback 成员设置为 NULL 来取消注册之前注册的回调。

2.8 gptimer_set_alarm_action

esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config)
  • 参数
    • timer:timer 句柄
    • config:alarm 配置,特别是将 config 设置为 NULL 意味着关闭 alarm 功能
  • 作用
    • 设置 GPTimer alarm 事件操作
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_FAIL:由于其他错误
  • 该功能允许在 ISR 上下文中运行,以便用户可以立即在 ISR 回调中设置新的 alarm 动作。

2.9 gptimer_enable

esp_err_t gptimer_enable(gptimer_handle_t timer)
  • 参数
    • timer:timer 句柄
  • 作用
    • 使能 GPTimer
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_FAIL:由于其他错误
  • 此函数会将计时器状态从 “init” 转换为 “enable”。
  • 启用计时器并不意味着启动它。另请参阅 gptimer_start 了解如何使计时器开始计数。

2.10 gptimer_disable

esp_err_t gptimer_disable(gptimer_handle_t timer)
  • 参数
    • timer:timer 句柄
  • 作用
    • 禁用 GPTimer
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_ERR_INVALID_STATE:禁用 GPTimer 失败,因为计时器尚未启用
    • ESP_FAIL:由于其他错误
  • 此函数会将计时器状态从 “enable” 转换为 “init”。
  • 如果安装了中断服务,此功能将禁用中断服务。
  • 禁用计时器并不意味着停止它。另请参阅 gptimer_stop 了解如何使计时器停止计数。

2.11 gptimer_start

esp_err_t gptimer_start(gptimer_handle_t timer)
  • 参数
    • timer:timer 句柄
  • 作用
    • 启动 GPTimer
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_ERR_INVALID_STATE:启动 GPTimer 失败,因为计时器未启用或已在运行
    • ESP_FAIL:由于其他错误
  • 此函数会将计时器状态从 “enable” 转换为 “init”。
  • 如果安装了中断服务,此功能将禁用中断服务。
  • 禁用计时器并不意味着停止它。另请参阅 gptimer_stop 了解如何使计时器停止计数。
  • 此函数会将计时器状态从 “enable” 转换为 “run”。
  • 允许此函数在 ISR 上下文中运行

2.12 gptimer_stop

esp_err_t gptimer_stop(gptimer_handle_t timer)
  • 参数
    • timer:timer 句柄
  • 作用
    • 停止 GPTimer(内部计数器停止计数)
  • 返回值
    • ESP_OK:成功
    • ESP_ERR_INVALID_ARG:参数无效
    • ESP_ERR_INVALID_STATE:停止 GPTimer 失败,因为计时器未运行。
    • ESP_FAIL:由于其他错误
  • 此函数会将计时器状态从 “run” 转换为 “enable”。
  • 允许此函数在 ISR 上下文中运行

3、枚举类型

见文件:esp-idf/components/esp_driver_gptimer/include/driver/gptimer_types.hesp-idf/components/hal/include/hal/timer_types.h

3.1 gptimer_clock_source_t

设置时钟源。

typedef soc_periph_gptimer_clk_src_t gptimer_clock_source_t;...typedef enum {GPTIMER_CLK_SRC_APB = SOC_MOD_CLK_APB,     /*!< Select APB as the source clock */GPTIMER_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default choice */
} soc_periph_gptimer_clk_src_t;

3.2 gptimer_count_direction_t

设置计数方向。

typedef enum {GPTIMER_COUNT_DOWN, /* 递减 */GPTIMER_COUNT_UP,   /* 递增 */
} gptimer_count_direction_t;

3.3 gptimer_etm_task_type_t

表示 GPTimer 特定任务,支持 ETM 模块。

typedef enum {GPTIMER_ETM_TASK_START_COUNT, /* 启动计数器 */GPTIMER_ETM_TASK_STOP_COUNT,  /* 停止计数器 */GPTIMER_ETM_TASK_EN_ALARM,    /* 启用计时器 alarm */GPTIMER_ETM_TASK_RELOAD,      /* 重新加载计数器的预设值 */GPTIMER_ETM_TASK_CAPTURE,     /* 捕获当前计数值,并将其存入特定的寄存器 */GPTIMER_ETM_TASK_MAX,         /* 表示这个枚举中任务的最大数量,用于范围检查 */
} gptimer_etm_task_type_t;

3.4 gptimer_etm_event_type_t

表示 GPTimer 特定事件,支持 ETM 模块。

typedef enum {GPTIMER_ETM_EVENT_ALARM_MATCH, /* 当计数器的当前值与预设的报警值相等时,会触发这个事件 */GPTIMER_ETM_EVENT_MAX,         /* 表示这个枚举中事件的最大数量,用于范围检查 */
} gptimer_etm_event_type_t;

二、GTIM 配置

通用定时器是 ESP32-C3 定时器组外设的驱动程序。ESP32-C3 硬件定时器分辨率高,具有灵活的报警功能。定时器内部计数器达到特定目标数值的行为被称为定时器报警。定时器报警时将调用用户注册的不同定时器回调函数。

通用定时器通常在以下场景中使用:

  • 如同挂钟一般自由运行,随时随地获取高分辨率时间戳;
  • 生成周期性警报,定期触发事件;
  • 生成一次性警报,在目标时间内响应。

配置和操作定时器的常规步骤如下:

  1. 资源分配 - 获取定时器句柄应设置的参数,以及如何在通用定时器完成工作时回收资源。
  2. 设置和获取计数值 - 如何强制定时器从起点开始计数,以及如何随时获取计数值。
  3. 设置警报动作 - 启动警报事件应设置的参数。
  4. 注册事件回调函数 - 如何将用户的特定代码挂载到警报事件回调函数。
  5. 使能和禁用定时器 - 如何使能和禁用定时器。
  6. 启动和停止定时器 - 通过不同报警行为启动定时器的典型使用场景。
  7. 电源管理 - 选择不同的时钟源将会如何影响功耗。
  8. 线程安全 - 驱动程序保证哪些 API 线程安全。

1、资源分配

通用定时器实例由 gptimer_handle_t 表示。可用硬件资源汇集在资源池内,由后台驱动程序管理,无需考虑硬件所属的定时器以及定时器组。

要安装一个定时器实例,需要提前提供配置结构体 gptimer_config_t。完成结构配置之后,可以将结构传递给 gptimer_new_timer(),用以实例化定时器实例并返回定时器句柄。

如已不再需要之前创建的通用定时器实例,应通过调用 gptimer_del_timer() 回收定时器,以便底层硬件定时器用于其他目的。在删除通用定时器句柄之前,请通过 gptimer_disable() 禁用定时器,或者通过 gptimer_enable() 确认定时器尚未使能。

下例中创建了一个分辨率为 1 MHz 的通用定时器句柄:

gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {.clk_src = GPTIMER_CLK_SRC_DEFAULT,.direction = GPTIMER_COUNT_UP,.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));

2、设置和获取计数值

创建通用定时器时,内部计数器将默认重置为。计数值可以通过 gptimer_set_raw_count() 异步更新。当更新活动定时器的原始计数值时,定时器将立即从新值开始计数。

最大计数值取决于硬件定时器的位宽,这也会在 SOC 宏 SOC_TIMER_GROUP_COUNTER_BIT_WIDTH 中有所反映。

计数值可以随时通过 gptimer_get_raw_count() 获取。

3、设置警报动作

对于大多数通用定时器使用场景而言,应在启动定时器之前设置警报动作,但不包括简单的挂钟场景,该场景仅需自由运行的定时器。设置警报动作,需要根据如何使用警报事件来配置 gptimer_alarm_config_t 的不同参数。

要使警报配置生效,需要调用 gptimer_set_alarm_action()。特别是当 gptimer_alarm_config_t 设置为 NULL 时,报警功能将被禁用。

如果警报值已设置且定时器超过该值,则会立即触发警报。

4、注册事件回调函数

定时器启动后,可动态产生特定事件(如“警报事件”)。如需在事件发生时调用某些函数,则通过 gptimer_register_event_callbacks() 将函数挂载到中断服务例程 (ISR)。

也可以通过参数 user_data,将自己的上下文保存到 gptimer_register_event_callbacks() 中。用户数据将直接传递给回调函数。

注意,此功能将为定时器延迟安装中断服务,但不使能中断服务。所以,请在 gptimer_enable() 之前调用这一函数,否则将返回 ESP_ERR_INVALID_STATE 错误。

在这里插入代码片

5、使能和禁用定时器

在对定时器进行 IO 控制之前,需要先调用 gptimer_enable() 使能定时器。它把定时器驱动程序的状态从 init 切换为 enable。如果 gptimer_register_event_callbacks() 已经延迟安装中断服务,此函数将使能中断服务。

调用 gptimer_disable() 会进行相反的操作,即将定时器驱动程序恢复到 init 状态,禁用中断服务并释放电源管理锁。

6、启动和停止定时器

启动和停止是定时器的基本 IO 操作。调用 gptimer_start() 可以使内部计数器开始工作,而 gptimer_stop() 可以使计数器停止工作。下文说明了如何在存在或不存在警报事件的情况下启动定时器。

注意,确保 start 和 stop 函数成对使用,否则,函数可能返回 ESP_ERR_INVALID_STATE

7、电源管理

有些电源管理的策略会在某些时刻关闭时钟源,或者改变时钟源的频率,以求降低功耗。比如在启用 DFS 后,APB 时钟源会降低频率。如果浅睡眠 (Light-sleep) 模式也被开启,PLL 和 XTAL 时钟都会被默认关闭,从而导致 GPTimer 的计时不准确。

驱动程序会根据具体的时钟源选择,通过创建不同的电源锁来避免上述情况的发生。驱动会在 gptimer_enable() 函数中增加电源锁的引用计数,并在 gptimer_disable() 函数中减少电源锁的引用计数,从而保证了在 gptimer_enable()gptimer_disable() 之间,GPTimer 的时钟源始处于稳定工作的状态。

8、线程安全

驱动提供的所有 API 都是线程安全的。使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。以下这些函数还支持在中断上下文中运行。

  • gptimer_start()
  • gptimer_stop()
  • gptimer_get_raw_count()
  • gptimer_set_raw_count()
  • gptimer_get_captured_count()
  • gptimer_set_alarm_action()

三、实例操作

下面通过一段代码演示 gptimer 的一些基本用法。

1、核心代码讲解

首先初始化 gtimer,得到 timer 句柄:

gptimer_handle_t gptimer = NULL;
gptimer_config_t time_config = {.clk_src = GPTIMER_CLK_SRC_DEFAULT,.direction = GPTIMER_COUNT_UP,.resolution_hz = 1000000,  // 1MHz, 1 tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&time_config, &gptimer));

第一个例子我们创建一个 alarm 回调函数,并回调函数中关闭 gptimer:

static bool example_timer_on_alarm_cb_v1(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{BaseType_t high_task_awoken = pdFALSE;QueueHandle_t queue = (QueueHandle_t)user_data;gptimer_stop(timer);example_queue_element_t ele = {.event_count = edata->count_value,};xQueueSendFromISR(queue, &ele, &high_task_awoken);return (high_task_awoken == pdTRUE);
}
...
gptimer_event_callbacks_t cbs = {.on_alarm = example_timer_on_alarm_cb_v1,
};
// 注册回调函数
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));
ESP_ERROR_CHECK(gptimer_enable(gptimer));gptimer_alarm_config_t alarm_config1 = {.alarm_count = 1000000,  // 1s, 1 tick = 1us
};
// 设置 alarm 参数
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config1));
ESP_ERROR_CHECK(gptimer_start(gptimer));
if (xQueueReceive(queue, &ele, pdMS_TO_TICKS(2000))) {ESP_LOGI(TAG, "Timer stopped, count = %llu", ele.event_count);
} else {ESP_LOGW(TAG, "Missed one count event");
}

第二个例子演示了 alarm 的自动重装载,核心代码如下:

gptimer_alarm_config_t alarm_config2 = {.alarm_count = 1000000,  // 1s, 1 tick = 1us.reload_count = 0, .flags.auto_reload_on_alarm = true,
};

第三个例子是在 alarm 回调函数中动态地修改 alarm 的值:

gptimer_alarm_config_t alarm_config = {.alarm_count = edata->alarm_value + 1000000,  // increase alarm value by 1s
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(timer, &alarm_config));

2、完整代码和效果演示

static const char *TAG = "example";typedef struct {uint64_t event_count;
} example_queue_element_t;// test1: stop timer at alarm event
static bool example_timer_on_alarm_cb_v1(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{BaseType_t high_task_awoken = pdFALSE;QueueHandle_t queue = (QueueHandle_t)user_data;gptimer_stop(timer);example_queue_element_t ele = {.event_count = edata->count_value,};xQueueSendFromISR(queue, &ele, &high_task_awoken);return (high_task_awoken == pdTRUE);
}// test2: disable timer at alarm event
static bool example_timer_on_alarm_cb_v2(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{BaseType_t high_task_awoken = pdFALSE;QueueHandle_t queue = (QueueHandle_t)user_data;example_queue_element_t ele = {.event_count = edata->count_value,};xQueueSendFromISR(queue, &ele, &high_task_awoken);return (high_task_awoken == pdTRUE);
}// test3: update alarm value dynamically
static bool example_timer_on_alarm_cb_v3(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{BaseType_t high_task_awoken = pdFALSE;QueueHandle_t queue = (QueueHandle_t)user_data;example_queue_element_t ele = {.event_count = edata->count_value,};xQueueSendFromISR(queue, &ele, &high_task_awoken);gptimer_alarm_config_t alarm_config = {.alarm_count = edata->alarm_value + 1000000,  // increase alarm value by 1s};ESP_ERROR_CHECK(gptimer_set_alarm_action(timer, &alarm_config));return (high_task_awoken == pdTRUE);
}void app_main(void)
{example_queue_element_t ele;QueueHandle_t queue = xQueueCreate(10, sizeof(example_queue_element_t));if (!queue) {ESP_LOGE(TAG, "Failed to create queue");return;}ESP_LOGI(TAG, "Create timer handle");gptimer_handle_t gptimer = NULL;gptimer_config_t time_config = {.clk_src = GPTIMER_CLK_SRC_DEFAULT,.direction = GPTIMER_COUNT_UP,.resolution_hz = 1000000,  // 1MHz, 1 tick = 1us};ESP_ERROR_CHECK(gptimer_new_timer(&time_config, &gptimer));// TEST1/*******************************************************************/gptimer_event_callbacks_t cbs = {.on_alarm = example_timer_on_alarm_cb_v1,};ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));ESP_LOGI(TAG, "Enable timer");ESP_ERROR_CHECK(gptimer_enable(gptimer));ESP_LOGI(TAG, "Start timer, stop it at alarm event");gptimer_alarm_config_t alarm_config1 = {.alarm_count = 1000000,  // 1s, 1 tick = 1us};ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config1));ESP_ERROR_CHECK(gptimer_start(gptimer));if (xQueueReceive(queue, &ele, pdMS_TO_TICKS(2000))) {ESP_LOGI(TAG, "Timer stopped, count = %llu", ele.event_count);} else {ESP_LOGW(TAG, "Missed one count event");}ESP_LOGI(TAG, "Set count value");ESP_ERROR_CHECK(gptimer_set_raw_count(gptimer, 100));ESP_LOGI(TAG, "Get count value");uint64_t count = 0;ESP_ERROR_CHECK(gptimer_get_raw_count(gptimer, &count));ESP_LOGI(TAG, "Timer count value = %llu", count);// TEST2/*******************************************************************/ESP_LOGI(TAG, "Disable timer");ESP_ERROR_CHECK(gptimer_disable(gptimer));cbs.on_alarm = example_timer_on_alarm_cb_v2;ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));ESP_LOGI(TAG, "Enable timer");ESP_ERROR_CHECK(gptimer_enable(gptimer));ESP_LOGI(TAG, "Start timer, auto-reload at alarm event");gptimer_alarm_config_t alarm_config2 = {.alarm_count = 1000000,  // 1s, 1 tick = 1us.reload_count = 0, .flags.auto_reload_on_alarm = true,};ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config2));ESP_ERROR_CHECK(gptimer_start(gptimer));int record = 4;while (record) {if (xQueueReceive(queue, &ele, pdMS_TO_TICKS(2000))) {ESP_LOGI(TAG, "Timer reloaded, count = %llu", ele.event_count);record--;} else {ESP_LOGW(TAG, "Missed one count event");}}// TEST3/*******************************************************************/ESP_LOGI(TAG, "Stop timer");ESP_ERROR_CHECK(gptimer_stop(gptimer));ESP_LOGI(TAG, "Disable timer");ESP_ERROR_CHECK(gptimer_disable(gptimer));cbs.on_alarm = example_timer_on_alarm_cb_v3;ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, queue));ESP_LOGI(TAG, "Enable timer");ESP_ERROR_CHECK(gptimer_enable(gptimer));ESP_LOGI(TAG, "Start timer, update alarm value dynamically");gptimer_alarm_config_t alarm_config3 = {.alarm_count = 1000000,  // 1s, 1 tick = 1us};ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config3));ESP_ERROR_CHECK(gptimer_start(gptimer));record = 4;while (record) {if (xQueueReceive(queue, &ele, pdMS_TO_TICKS(2000))) {ESP_LOGI(TAG, "Timer alarmed, count = %llu", ele.event_count);record--;} else {ESP_LOGW(TAG, "Missed one count event");}}ESP_LOGI(TAG, "Stop timer");ESP_ERROR_CHECK(gptimer_stop(gptimer));ESP_LOGI(TAG, "Disable timer");ESP_ERROR_CHECK(gptimer_disable(gptimer));ESP_LOGI(TAG, "Delete timer");ESP_ERROR_CHECK(gptimer_del_timer(gptimer));vQueueDelete(queue);
}


http://www.mrgr.cn/news/53615.html

相关文章:

  • Kibana可视化Dashboard如何基于字段是否包含某关键词进行过滤
  • 告别手动刷新:如何让数据自动更新你的应用界面?
  • Qt中自定义qDebug打印信息的宏(文件名,行数,函数名,日期,时间等前缀)并取消打印
  • 数据分析人员需要掌握sql到什么程度?
  • 【C++刷题】力扣-#346-数据流中的移动平均值
  • CSS 选择器简单回顾
  • LeetCode题练习与总结:拼接最大数--321
  • 小白学大模型 RAG:GraphRAG 概念、组成和流程,看完这一篇你就懂了!!
  • 出手!快手可灵开源版,AI视频生成整合包!
  • 84.【C语言】数据结构之顺序表的头部插入和删除
  • 医疗领域的RAG技术:如何通过知识图谱提升准确性
  • vb操作电子表格 文件夹内多表格 提取数据 在生成一个新表格
  • Leetcode—192. 统计词频【中等】(Shell)
  • 【树莓派】树莓派搭建个人服务器
  • Qt之QObject
  • Java并发编程深度解析:从基础到实战
  • Shades of Gray 算法
  • 问:MySQL数据库存储引擎及对应的锁有哪些?
  • ​AI Sketchnotes Generator——解锁创意表达的新方式
  • 83.【C语言】数据结构之顺序表的尾部插入和删除
  • C语言 | Leetcode C语言题解之第493题翻转对
  • [实时计算flink]DataStream连接器设置方法
  • 骑砍霸主MOD天芒传奇Ⅱ·前传-序章
  • Cuda By Example - 8 (性能测量)
  • ChatGPT的150个角色提示场景实测(17)营养师
  • 一天认识一个硬件之路由器