ESP32S3学习——通用定时器

文章详细介绍了ESP32-S3芯片的定时器配置,包括初始化设置、定时器控制、警报功能和中断处理。通过定时器初始化、设置计数器值、启用警报和中断回调函数,实现定时器在不同模式下的工作。示例代码展示了如何应用这些功能。

芯片:esp32s3

开发环境:espidfv4.4

一、官网相关资料

1)简介

ESP32-S3 芯片提供两组硬件定时器,每组包含两个通用硬件定时器。共四个定时器

所有通用定时器均基于 16 位预分频器54 位可自动重新加载向上/向下计数器。

2)配置和操作流程

  • 定时器初始化 - 启动定时器前应设置的参数,以及每个设置提供的具体功能。

  • 定时器控制 - 如何读取定时器的值,如何暂停/启动定时器以及如何改变定时器的操作方式。

  • 警报 - 如何设置和使用警报。

  • 处理中断事务- 如何使用中断提供的回调函数。

3)定时器初始化

1、s3的定时器组:timer_group_t,;其中的个体定时器类型为timer_idx_t

2、timer_init()->timer_config_t 初始划定时器工作方式

3、时钟源

4、预分频:默认的时钟源是 APB_CLK (一般是 80 MHz)

5、模式:向上、下;从 timer_count_dir_t 中选取一个值,后使用 counter_dir 来选择模式

6、计时器使能:调用 timer_init() 后计数器将立即开始递增/递减,timer_start_t 中选取一个值,后使用 counter_en 改变此行为

7、报警使能:alarm_en??

8、自动重装: 设置计数器是否应该在定时器警报上使用 auto_reload 自动重载首个计数值,还是继续递增或递减。-

9、获取定时器当前值:timer_get_config()

4)定时器控制

1、使能后即开始计时,使counter_entrue为使能,通过timer_init(),或者直接调用函数 timer_start()开始计时

2、timer_set_counter_value() 来指定定时器的首个计数值。

3、检查定时器的当前值,调用函数 timer_get_counter_value()timer_get_counter_time_sec()

除此之外,还可通过使用专有函数更改个别设置来重新配置定时器:

设置专有函数描述
分频器timer_set_divider()更改计数频率。为避免发生不可预测情况,更改分频器时应暂停定时器。如果定时器正在运行,则使用 timer_set_divider() 将其暂停并更改设置,然后重启定时器。
模式timer_set_counter_mode()设置计数器应递增还是递减
自动重载timer_set_auto_reload()设置是否应在定时器警报上重载首个计数值

5)警报,arr吧

先调用函数 timer_set_alarm_value(),然后使用 timer_set_alarm() 使能警报。当调用函数 timer_init() 时,也可以在定时器初始化阶段使能警报

警报已使能且定时器达到警报值后,根据配置,可能会出现以下两种行为:

  • 如果先前已配置,此时将触发中断。有关如何配置中断,请参见 处理中断事务

  • auto_reload 已使能,定时器的计数器将重新加载,从先前配置好的值开始再次计数。应使用函数 timer_set_counter_value() 预先设置该值。(神魔用呢

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

  • 一旦触发后,警报将自动关闭,需要重新使能以再次触发。

  • 要检查某特定的警报值,调用函数 timer_get_alarm_value()

6)中断

调用 timer_isr_callback_add() 函数可以给某个定时器注册一个中断回调函数,不要执行阻塞式事务

状态位无需关系,驱动考虑好了

7)官方示例

54 位通用硬件定时器示例:peripherals/timer_group

三、上手敲代码

1)示例位置:54 位通用硬件定时器示例:peripherals/timer_group

2)readme

*配置了两个定时器 *每个定时器都设置了一些采样报警间隔 *当达到间隔值时,每个计时器将产生警报 *其中一个计时器被配置为在警报上自动重新加载其计数器值 *另一个定时器被配置为保持递增,每次报警时由应用程序重新加载 *报警触发后续中断,通过打印在终端上的消息进行跟踪:

3)初始化定时器

/* Select and initialize basic parameters of the timer */

timer_config_t config = {

.divider = TIMER_DIVIDER,//16分频 80M/16=5M

.counter_dir = TIMER_COUNT_UP,.//向上计数模式

.counter_en = TIMER_PAUSE,//暂停定时器

.alarm_en = TIMER_ALARM_EN,//开启警报

.auto_reload = auto_reload,//自动重装载

}; // default clock source is APB

timer_init(group, timer, &config);//初始化结构体

timer_set_counter_value(group, timer, 0);设置开始的初值

//设置报警值TIMER_SCALE:经过分频的时钟频率

timer_set_alarm_value(group, timer, timer_interval_sec * c);

timer_enable_intr(group, timer);//使能报警
//添加中断回调函数

timer_isr_callback_add(group, timer, timer_group_isr_callback, timer_info, 0);

//开启定时器

timer_start(group, timer);
//获取定时器值

timer_get_counter_value(evt.info.timer_group, evt.info.timer_idx, &task_counter_value);

<think> 我们已经在之前的回答中介绍了ESP32-S3在ESP-IDF环境下使用单次定时器的基本步骤。现在,用户明确要求提供示例代码。我们将根据ESP-IDF的GPTimer(通用定时器)示例,编写一个单次启动的定时器代码。 注意:用户要求的是单次启动的定时器,即触发一次后就停止。 步骤回顾: 1. 配置定时器(包括时钟源、计数方向、分辨率和自动重载标志) 2. 创建定时器实例 3. 设置报警值并注册回调函数(在回调函数中我们可以停止定时器,因为是单次) 4. 启动定时器 另外,在回调函数中,我们可以进行一些操作(比如打印日志),然后停止定时器(因为单次触发后我们不需要再运行)。 参考引用[1]中提到的示例:esp-idf/examples/peripherals/timer_group/gptimer。我们将以此为基础,修改为单次定时器。 注意:在配置中,我们需要设置`auto_reload_on_alarm`为false(在定时器配置结构体中)和`auto_reload`为false(在报警配置结构体中)以确保单次模式。 我们将编写一个完整的示例代码,包括初始化、回调函数和启动定时器,并在回调函数中释放定时器资源(但注意:通常我们不会在回调函数中删除定时器,因为删除操作可能会涉及到资源释放,而回调函数在中断上下文中,不能进行复杂的操作。因此,我们通常是在回调函数中设置一个标志,然后在任务中处理定时器的停止和删除)。 但是,为了简化,我们可以在回调函数中停止定时器(gptimer_stop)并禁用定时器(gptimer_disable),但删除定时器(gptimer_del_timer)建议在任务中进行。不过,由于是单次定时器,我们可能希望它在触发后自动停止,并不再使用,所以我们可以选择在回调函数中停止定时器,然后在主任务中删除。 然而,为了简单演示,我们在回调函数中停止定时器,然后在主循环中删除定时器并退出。另一种做法是:在回调函数中发送一个信号量或任务通知,通知主任务定时器已经触发,然后由主任务来删除定时器。 这里我们采用第二种方式:使用FreeRTOS的信号量来通知主任务。 代码结构如下: 1. 包含必要的头文件 2. 定义定时器句柄和信号量句柄 3. 编写定时器回调函数(在中断中触发,发送信号量) 4. 创建定时器,配置定时器 5. 创建信号量 6. 启用并启动定时器 7. 主任务等待信号量,然后停止并删除定时器 注意:在中断回调函数中不能调用可能引起阻塞的函数,所以信号量必须使用二值信号量,并且使用`xSemaphoreGiveFromISR`给予信号量。 下面我们提供示例代码: </think> 以下是 ESP32-S3 在 ESP-IDF 环境下实现的单次启动定时器完整示例代码,基于 GPTimer 外设: ```c #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "driver/gptimer.h" static const char* TAG = "SingleShot Timer"; static gptimer_handle_t gptimer = NULL; // 定时器中断回调函数 (需放在 IRAM) static bool IRAM_ATTR timer_callback( gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) { BaseType_t high_task_awoken = pdFALSE; // 此处执行定时触发后的操作 ESP_DRAM_LOGI(TAG, "Timer triggered! Count=%llu", edata->count_value); // 停止定时器 (单次模式) gptimer_stop(timer); return high_task_awoken == pdTRUE; } void app_main(void) { ESP_LOGI(TAG, "Creating single-shot timer"); // 1. 配置定时器参数 gptimer_config_t timer_config = { .clk_src = GPTIMER_CLK_SRC_DEFAULT, .direction = GPTIMER_COUNT_UP, .resolution_hz = 1 * 1000 * 1000, // 1MHz 分辨率 (1微秒/计数) .flags.auto_reload_on_alarm = false // 禁用自动重载 → 单次模式[^1] }; ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer)); // 2. 设置警报参数 gptimer_alarm_config_t alarm_config = { .alarm_count = 3 * 1000 * 1000, // 定时3秒 (3e6 微秒) .flags.auto_reload = false // 确保单次触发[^1] }; ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config)); // 3. 注册回调函数 gptimer_event_callbacks_t cbs = { .on_alarm = timer_callback, }; ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL)); // 4. 启动定时器 ESP_ERROR_CHECK(gptimer_enable(gptimer)); ESP_ERROR_CHECK(gptimer_start(gptimer)); ESP_LOGI(TAG, "Timer started, will trigger after 3 seconds"); // 等待定时器触发 vTaskDelay(pdMS_TO_TICKS(4000)); // 5. 清理资源 ESP_ERROR_CHECK(gptimer_disable(gptimer)); ESP_ERROR_CHECK(gptimer_del_timer(gptimer)); ESP_LOGI(TAG, "Timer resources released"); } ``` ### 关键代码说明: 1. **定时器配置** - `resolution_hz = 1e6`:设置定时器分辨率为 1MHz(1微秒/计数) - `auto_reload_on_alarm = false`:禁用自动重载,实现单次触发[^1] 2. **警报配置** - `alarm_count = 3e6`:设置 3 秒定时(3,000,000 微秒) - `auto_reload = false`:确保单次触发[^1] 3. **中断处理** - `IRAM_ATTR`:确保回调函数在 RAM 中执行(避免闪存访问延迟) - `gptimer_stop()`:触发后立即停止定时器 ### 使用说明: 1. 将代码保存为 `main.c` 2. 在项目目录中创建 `CMakeLists.txt`: ```cmake cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(single_shot_timer) ``` 3. 编译并烧录: ```bash idf.py set-target esp32s3 idf.py build idf.py -p PORT flash monitor ``` ### 预期输出: ``` I (327) SingleShot Timer: Creating single-shot timer I (337) SingleShot Timer: Timer started, will trigger after 3 seconds I (3007) SingleShot Timer: Timer triggered! Count=3000000 I (4007) SingleShot Timer: Timer resources released ``` > 📌 **注意**: > 1. 回调函数必须标记 `IRAM_ATTR`,否则可能导致崩溃 > 2. 分辨率可调整(如 `10e6` 为 100ns 精度) > 3. 定时结束后调用 `gptimer_del_timer()` 释放资源[^3] > 4. 完整示例参考:[esp-idf/examples/peripherals/timer_group/gptimer][^1]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值