当前位置: 首页 > 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);
}

相关文章:

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#学习笔记&#xff08;十&#xff09; 第七章 对象的构造方法与实例方法一、对象的构造方法1. 构造方法初识2. 构造方法的创建3. this关键字4. 构造方法的规范和重载4.1 构造方法的规范 5. 对象初始化器5.1 对象初始化器和构造方法的区别 二、对象的实例方法1. 简单应用2.实例…...

出手!快手可灵开源版,AI视频生成整合包!

在2024年&#xff0c;人工智能领域迎来了一位新星——AI视频生成技术。在这场技术革命中&#xff0c;快手推出的可灵AI无疑是最耀眼的明星之一。然而&#xff0c;其高昂的年费让不少用户望而却步&#xff0c;毕竟数千元的开销对于普通人来说是个不小的负担。 幸运的是&#xff…...

【Linux】进程池

目录 进程池 进程池的概念&#xff1a; 手搓进程池&#xff1a; 1、创建信道和子进程 2、通过channel控制子进程 3、回收管道和子进程 进程池 进程池的概念&#xff1a; 定义一个池子&#xff0c;在里面放上固定数量的进程&#xff0c;有需求来了&#xff0c;就拿一个池中…...

实验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 一、项目背景 浙江某能源集团有限公司位于浙江省宁波前湾新区&#xff0c;主营业务范围包括了储能技术服务&#xff0c;光伏风力发电技术服务&#xff0c;充电桩技术服务&#xff0c;新能源项目的施工以及为企业提供配电房运维服务。 随着新能源的兴…...

线性代数学习

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.只有一个轴的张量&#xff0c…...

FineReport 数据显示格式

原始 修改 选择「单元格元素>格式」&#xff0c;选择「日期型」&#xff0c;改成 「yyyy 年 MM 月 dd 日」&#xff0c;如下图所示&#xff1a; 注&#xff1a;若列表中没有 yyyy 年 MM 月 dd 日 格式&#xff0c;可手动输入 选择运货费数据列单元格&#xff0c;选择「单元…...

leetcode.204.计数质数

#中等#枚举 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 埃氏筛 枚举没有考虑到数与数的关联性&#xff0c;因此难以再继续优化时间复杂度。接下来我们介绍一个常见的算法&#xff0c;该算法由希腊数学家厄拉多塞&#xff08;Eratosthenes&#xff09;提…...

Mysql环境安装

1&#xff0c;下载压缩包 下载压缩包解压 2&#xff0c;配置环境变量 i&#xff0c;高级系统设置-->环境变量-->系统变量-->path-->添加mysql的bin目录路径 ii&#xff0c;新建my.ini文件 basedir:MYSQL的路径 datadir&#xff1a;这个data路径不用手动创建&am…...

请问平面仓系统的盘点如何做?

盘点流程 一、盘点任务生成 手动发起&#xff1a;仓库管理人员可以根据实际需要&#xff0c;在系统中手动发起库存盘点任务。例如&#xff0c;定期进行全盘、抽盘或者在发现库存数据异常时发起盘点。自动触发&#xff1a;系统可以设置自动触发盘点的条件&#xff0c;如每隔一…...

STM32笔记(1)GPIO之点亮LED

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 总结 第一步&#xff1a;先看原理图。PB0输出高电平是&#xff0c;LED1点亮。 初始化完成了两项工作&#xff1a; (1)从时钟上启动所用GPIO所在的总线&#xff1b…...

自动化工具

自动化工具确保测试准确性的关键在于采取一系列综合性措施&#xff0c;包括但不限于以下几点&#xff1a; 环境一致性&#xff1a;确保测试环境与生产环境尽可能相似&#xff0c;减少环境差异导致的结果不准确。可以通过容器技术&#xff08;如Docker和Kubernetes&#xff09;确…...

CTFHUB技能树之HTTP协议——响应包源代码

开启靶场&#xff0c;打开链接&#xff1a; 是个贪吃蛇小游戏&#xff0c;看不出来有什么特别的地方 用burp抓包看看情况&#xff1a; 嗯&#xff1f;点击“开始”没有抓取到报文&#xff0c;先看看网页源代码是什么情况 居然直接给出flag了&#xff0c;不知道这题的意义何在 …...

Java会话技术,拦截器,过滤器,登录校验

目录 1.会话&#xff1a; 2.会话跟踪&#xff1a; 3.会话跟踪方案&#xff1a; 客户端会话跟踪技术&#xff1a;Cookie 服务端会话跟踪技术&#xff1a;Session JWT令牌技术(https://jwt.io/) 定义&#xff1a; 优点&#xff1a; 缺点&#xff1a; 组成&#xff1a; …...

Spring Security 如何进行权限验证

阅读本文之前&#xff0c;请投票支持这款 全新设计的脚手架 &#xff0c;让 Java 再次伟大&#xff01; FilterSecurityInterceptor FilterSecurityInterceptor 是负责权限验证的过滤器。一般来说&#xff0c;权限验证是一系列业务逻辑处理完成以后&#xff0c;最后需要解决的…...

计算机砖头书的学习建议

纸上得来终觉浅&#xff0c;绝知此事要躬行&#xff0c;技术来源于实践&#xff0c;光看不练意义不大&#xff0c;过阵子全忘记&#xff0c;并且没有实践来深化理论认知。 “砖头书”通常指的是那些厚重、内容详实且权威的书籍&#xff0c;对于计算机科学领域而言&#xff0c;…...

我与C语言二周目邂逅vlog——7.预处理

C语言预处理详解 C语言预处理是编译过程中的重要组成部分&#xff0c;用于对源代码进行文本替换和修改。预处理发生在编译的前期&#xff0c;通过特定的指令来控制代码的编译行为&#xff0c;最终生成可以交给编译器进行进一步处理的代码。预处理的目的是简化代码编写&#xf…...

Python无监督学习中的聚类:K均值与层次聚类实现详解

&#x1f4d8; Python无监督学习中的聚类&#xff1a;K均值与层次聚类实现详解 无监督学习是一类强大的算法&#xff0c;能够在没有标签的数据集中发现结构与模式。聚类作为无监督学习的重要组成部分&#xff0c;在各类数据分析任务中广泛应用。本文将深入讲解聚类算法中的两种…...

C++ 中 new 和 delete 详解,以及与 C 中 malloc 和 free 的区别

1. C 中 new 和 delete 的基本用法 在 C 中&#xff0c;new 和 delete 是用来动态分配和释放内存的关键字&#xff0c;它们是面向对象的替代方式&#xff0c;提供了比 C 语言更优雅的内存管理工具。 1.1 new 的使用 new 用于从堆中分配内存&#xff0c;并且自动调用对象的构造…...

YOLOv11来了 | 自定义目标检测

概述 YOLO11 在 2024 年 9 月 27 日的 YOLO Vision 2024 活动中宣布&#xff1a;https://www.youtube.com/watch?vrfI5vOo3-_A。 YOLO11 是 Ultralytics YOLO 系列的最新版本&#xff0c;结合了尖端的准确性、速度和效率&#xff0c;用于目标检测、分割、分类、定向边界框和…...

Vue3 集成Monaco Editor编辑器

Vue3 集成Monaco Editor编辑器 1. 安装依赖2. 使用3. 效果 Monaco Editor &#xff08;官方链接 https://microsoft.github.io/monaco-editor/&#xff09;是一个由微软开发的功能强大的在线代码编辑器&#xff0c;被广泛应用于各种 Web 开发场景中。以下是对 Monaco Editor 的…...

一文详解Mysql索引

背景 索引是存储引擎用于快速找到一条记录的数据结构。索引对良好的性能非常关键。尤其是当表中的数据量越来越大时&#xff0c;索引对性能的影响愈发重要。接下来&#xff0c;就来详细探索一下索引。 索引是什么 索引&#xff08;Index&#xff09;是帮助数据库高效获取数据的…...

基于JAVA+SpringBoot+Vue的旅游管理系统

基于JAVASpringBootVue的旅游管理系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345; 哈喽兄…...

STM32_实验3_控制RGB灯

HAL_Delay 是 STM32 HAL 库中的一个函数&#xff0c;用于在程序中产生一个指定时间的延迟。这个函数是基于系统滴答定时器&#xff08;SysTick&#xff09;来实现的&#xff0c;因此可以实现毫秒级的延迟。 void HAL_Delay(uint32_t Delay); 配置引脚&#xff1a; 点击 1 到 IO…...

RISC-V笔记——Pipeline依赖

1. 前言 RISC-V的RVWMO模型主要包含了preserved program order、load value axiom、atomicity axiom、progress axiom和I/O Ordering。今天主要记录下preserved program order(保留程序顺序)中的Pipeline Dependencies(Pipeline依赖)。 2. Pipeline依赖 Pipeline依赖指的是&a…...

构建后端为etcd的CoreDNS的容器集群(六)、编写自动维护域名记录的代码脚本

本文为系列测试文章&#xff0c;拟基于自签名证书认证的etcd容器来构建coredns域名解析系统。 一、前置文章 构建后端为etcd的CoreDNS的容器集群&#xff08;一&#xff09;、生成自签名证书 构建后端为etcd的CoreDNS的容器集群&#xff08;二&#xff09;、下载最新的etcd容…...

Leetcode 剑指 Offer II 098.不同路径

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下…...

LabVIEW智能螺杆空压机测试系统

基于LabVIEW软件开发的螺杆空压机测试系统利用虚拟仪器技术进行空压机的性能测试和监控。系统能够实现对螺杆空压机关键性能参数如压力、温度、流量、转速及功率的实时采集与分析&#xff0c;有效提高测试效率与准确性&#xff0c;同时减少人工操作&#xff0c;提升安全性。 项…...

在 Ubuntu 22.04 上安装 PHP 8.2

在 Ubuntu 22.04 上安装 PHP 8.2&#xff0c;可以按照以下步骤进行&#xff1a; 更新系统软件包&#xff1a; 首先&#xff0c;确保你的系统软件包是最新的。 sudo apt update sudo apt upgrade 安装 PHP PPA&#xff08;Personal Package Archive&#xff09;&#xff1a; U…...