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)flagsintr_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.h、esp-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 硬件定时器分辨率高,具有灵活的报警功能。定时器内部计数器达到特定目标数值的行为被称为定时器报警。定时器报警时将调用用户注册的不同定时器回调函数。
通用定时器通常在以下场景中使用:
- 如同挂钟一般自由运行,随时随地获取高分辨率时间戳;
- 生成周期性警报,定期触发事件;
- 生成一次性警报,在目标时间内响应。
配置和操作定时器的常规步骤如下:
- 资源分配 - 获取定时器句柄应设置的参数,以及如何在通用定时器完成工作时回收资源。
- 设置和获取计数值 - 如何强制定时器从起点开始计数,以及如何随时获取计数值。
- 设置警报动作 - 启动警报事件应设置的参数。
- 注册事件回调函数 - 如何将用户的特定代码挂载到警报事件回调函数。
- 使能和禁用定时器 - 如何使能和禁用定时器。
- 启动和停止定时器 - 通过不同报警行为启动定时器的典型使用场景。
- 电源管理 - 选择不同的时钟源将会如何影响功耗。
- 线程安全 - 驱动程序保证哪些 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);
}

相关文章:
ESP32-IDF 通用定时器 GPTimer
目录 一、基本介绍1、配置结构体1.1 gptimer_config_t1.2 gptimer_event_callbacks_t1.3 gptimer_alarm_config_t 2、常用 API2.1 gptimer_new_timer2.2 gptimer_del_timer2.3 gptimer_set_raw_count2.4 gptimer_get_raw_count2.5 gptimer_get_resolution2.6 gptimer_get_captu…...
C#学习笔记(十)
C#学习笔记(十) 第七章 对象的构造方法与实例方法一、对象的构造方法1. 构造方法初识2. 构造方法的创建3. this关键字4. 构造方法的规范和重载4.1 构造方法的规范 5. 对象初始化器5.1 对象初始化器和构造方法的区别 二、对象的实例方法1. 简单应用2.实例…...
出手!快手可灵开源版,AI视频生成整合包!
在2024年,人工智能领域迎来了一位新星——AI视频生成技术。在这场技术革命中,快手推出的可灵AI无疑是最耀眼的明星之一。然而,其高昂的年费让不少用户望而却步,毕竟数千元的开销对于普通人来说是个不小的负担。 幸运的是ÿ…...
【Linux】进程池
目录 进程池 进程池的概念: 手搓进程池: 1、创建信道和子进程 2、通过channel控制子进程 3、回收管道和子进程 进程池 进程池的概念: 定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中…...
实验23:DA呼吸灯实验
电路硬件: 实现功能: 代码: public.h #ifndef _public_H #define _public_H#include "reg52.h" //#include "key.h"typedef unsigned int u16; typedef unsigned char u8;void delay_10us(u16 n); void delay_ms(u16 ms);#endif public.c #include …...
安科瑞智慧能源管理系统EMS3.0在浙江某能源集团有限公司的应用
安科瑞戴婷 Acrel-Fanny 一、项目背景 浙江某能源集团有限公司位于浙江省宁波前湾新区,主营业务范围包括了储能技术服务,光伏风力发电技术服务,充电桩技术服务,新能源项目的施工以及为企业提供配电房运维服务。 随着新能源的兴…...
线性代数学习
1.标量由只有一个元素的张量表示 import torchx torch.tensor([3,0]) y torch.tensor([2,0])x y, x * y, x / y, x**y 2.可以将向量视为标量值组成的列表 x torch.arange(4) x 3.通过张量的索引访问任一元素 x[3] 4.访问张量长度 len(x) 5.只有一个轴的张量,…...
FineReport 数据显示格式
原始 修改 选择「单元格元素>格式」,选择「日期型」,改成 「yyyy 年 MM 月 dd 日」,如下图所示: 注:若列表中没有 yyyy 年 MM 月 dd 日 格式,可手动输入 选择运货费数据列单元格,选择「单元…...
leetcode.204.计数质数
#中等#枚举 给定整数 n ,返回 所有小于非负整数 n 的质数的数量 。 埃氏筛 枚举没有考虑到数与数的关联性,因此难以再继续优化时间复杂度。接下来我们介绍一个常见的算法,该算法由希腊数学家厄拉多塞(Eratosthenes)提…...
Mysql环境安装
1,下载压缩包 下载压缩包解压 2,配置环境变量 i,高级系统设置-->环境变量-->系统变量-->path-->添加mysql的bin目录路径 ii,新建my.ini文件 basedir:MYSQL的路径 datadir:这个data路径不用手动创建&am…...
请问平面仓系统的盘点如何做?
盘点流程 一、盘点任务生成 手动发起:仓库管理人员可以根据实际需要,在系统中手动发起库存盘点任务。例如,定期进行全盘、抽盘或者在发现库存数据异常时发起盘点。自动触发:系统可以设置自动触发盘点的条件,如每隔一…...
STM32笔记(1)GPIO之点亮LED
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 总结 第一步:先看原理图。PB0输出高电平是,LED1点亮。 初始化完成了两项工作: (1)从时钟上启动所用GPIO所在的总线;…...
自动化工具
自动化工具确保测试准确性的关键在于采取一系列综合性措施,包括但不限于以下几点: 环境一致性:确保测试环境与生产环境尽可能相似,减少环境差异导致的结果不准确。可以通过容器技术(如Docker和Kubernetes)确…...
CTFHUB技能树之HTTP协议——响应包源代码
开启靶场,打开链接: 是个贪吃蛇小游戏,看不出来有什么特别的地方 用burp抓包看看情况: 嗯?点击“开始”没有抓取到报文,先看看网页源代码是什么情况 居然直接给出flag了,不知道这题的意义何在 …...
Java会话技术,拦截器,过滤器,登录校验
目录 1.会话: 2.会话跟踪: 3.会话跟踪方案: 客户端会话跟踪技术:Cookie 服务端会话跟踪技术:Session JWT令牌技术(https://jwt.io/) 定义: 优点: 缺点: 组成: …...
Spring Security 如何进行权限验证
阅读本文之前,请投票支持这款 全新设计的脚手架 ,让 Java 再次伟大! FilterSecurityInterceptor FilterSecurityInterceptor 是负责权限验证的过滤器。一般来说,权限验证是一系列业务逻辑处理完成以后,最后需要解决的…...
计算机砖头书的学习建议
纸上得来终觉浅,绝知此事要躬行,技术来源于实践,光看不练意义不大,过阵子全忘记,并且没有实践来深化理论认知。 “砖头书”通常指的是那些厚重、内容详实且权威的书籍,对于计算机科学领域而言,…...
我与C语言二周目邂逅vlog——7.预处理
C语言预处理详解 C语言预处理是编译过程中的重要组成部分,用于对源代码进行文本替换和修改。预处理发生在编译的前期,通过特定的指令来控制代码的编译行为,最终生成可以交给编译器进行进一步处理的代码。预处理的目的是简化代码编写…...
Python无监督学习中的聚类:K均值与层次聚类实现详解
📘 Python无监督学习中的聚类:K均值与层次聚类实现详解 无监督学习是一类强大的算法,能够在没有标签的数据集中发现结构与模式。聚类作为无监督学习的重要组成部分,在各类数据分析任务中广泛应用。本文将深入讲解聚类算法中的两种…...
C++ 中 new 和 delete 详解,以及与 C 中 malloc 和 free 的区别
1. C 中 new 和 delete 的基本用法 在 C 中,new 和 delete 是用来动态分配和释放内存的关键字,它们是面向对象的替代方式,提供了比 C 语言更优雅的内存管理工具。 1.1 new 的使用 new 用于从堆中分配内存,并且自动调用对象的构造…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
