概述
ESP32-S3 芯片内置一组 52 位系统定时器。该定时器可用于生成操作系统所需的滴答定时 中断,也可用作普通定时器生成周期或单次延时中断。
定时器的功能
定时器的作用包括但不限于:
- 执行定时任务:定时器常用于周期性执行特定任务。例如,若需要每500毫秒执行某 项任务,定时器能够精准地满足这一需求。
- 时间测量:定时器能够精确测量时间,无论是代码段的执行时间还是事件发生的间隔 时间,都能通过定时器进行准确的计量。
- 精确延时:对于需要微秒级精度的延时场景,定时器能够提供可靠的解决方案,确保 延时的精确性。
- PWM信号生成:通过定时器的精确控制,我们可以生成PWM(脉宽调制)信号,这 对于驱动电机、调节LED亮度等应用至关重要。
- 事件触发与监控:定时器不仅用于触发中断,实现事件驱动的逻辑,还可用于实现看 门狗功能,监控系统状态,并在必要时进行复位操作,确保系统的稳定运行。
ESP32-S3的定时器整体框架介绍
系统定时器内置两个计数器UNIT0和UNIT1(如图14.1.1中①所示)以及三个比较器COMP0、COMP1、COMP2(如图14.1.1中②所示)。比较器用于监控计数器的计数值是否达到报警值。
①计数器
UNIT0、UNIT1均为ESP32-S3系统定时器内置的52位计数器。计数器使用XTAL_CLK作 为时钟源(40MHz)。XTAL_CLK经分频后,在一个计数周期生成频率为fXTAL_CLK/3的时钟信 号,然后在另一个计数周期生成频率为 fXTAL_CLK/2 的时钟信号。因此,计数器使用的时钟 CNT_CLK,其实际平均频率为fXTAL_CLK/2.5,即16MHz,见图14.1.2。每个CNT_CLK时钟 周期,计数递增1/16µs,即16个周期递增1µs。 用户可以通过配置寄存器SYSTIMER_CONF_REG中下面三个位来控制计数器 UNITn,这 三个位分别是:
①:SYSTIMER_TIMER_UNITn_WORK_EN
②:SYSTIMER_TIMER_UNITn_CORE0_STALL_EN
③:SYSTIMER_TIMER_UNITn_CORE1_STALL_EN
关于这三的配置请参考
②比较器
COMP0、COMP1、COMP2均为 ESP32-S3系统定时器内置的 52位比较器。比较器同样使 用XTAL_CLK作为时钟源(40MHz)。
图14.1.2展示了系统定时器生成报警的过程。在上述过程中用到一个计数器(Timer Countern) 和一个比较器(Timer Comparatorx),比较器将根据比较结果,生成报警中断。
函数解析
必要的头文件 :
#include "esp_timer.h"
特别注意!!!!!!!!!!!!!
一定一定要在CMakeLists.txt中的set(requires中添加esp_timer;如下图,不然一定会报错,这一点很多教程都没说!!!查错查了很久才找到!!!!
创建事件函数:
esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
esp_timer_handle_t* out_handle);
入参
create_args //指向arg外设结构体的指针
esp_tim_handle //指定使能的中断
例程:
esp_timer_handle_t esp_tim_handle; /* 定时器回调函数句柄 */
/* 定义一个定时器结构体 */
esp_timer_create_args_t tim_periodic_arg = {
.callback = &esptim_callback, /* 设置回调函数 */
.arg = NULL, /* 不携带参数 */
};
esp_timer_create(&tim_periodic_arg, &esp_tim_handle); /* 创建一个事件 */
/**
* @brief 定时器回调函数
* @param arg: 不携带参数
* @retval 无
*/
void esptim_callback(void *arg)
{
//用户程序
}
esp_timer_create_args_t :结构体成员变量描述
esp_timer_create_args_t 是一个结构体
typedef struct {
esp_timer_cb_t callback; //定时器在周期内调用的函数。
void* arg; //一个指针类型,将参数传递给回调函数。
注:一般设置为NULL,即不携带参数
esp_timer_dispatch_t dispatch_method; //从task或ISR调用回调。
const char* name; //定时器名称,用于ESPTIMER转储功能。
bool skip_unhandled_events; //跳过周期计时器的未处理事件。
} esp_timer_create_args_t;
每个周期内触发一次
esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer,
uint64_t period_us);
入参
timer //使用esp_timer_create创建的定时器句柄
period_us //计时器周期,以微秒为单位
例程:
uint32_t tps = 1000000;
esp_timer_start_periodic(esp_tim_handle, tps); /* 每周期内触发一次 */
/**
* @brief 初始化高精度定时器(ESP_TIMER)
* @param tps: 定时器周期,以微妙为单位(μs),以一秒为定时器周期来执行一次定时器中断,
* 那此处tps = 1s = 1000000μs
* @retval 无
*/
例程
.H文件
#ifndef __ESPTIM_H_
#define __ESPTIM_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "led.h"
/* 函数声明 */
void esptim_int_init(uint64_t tps); /* 初始化初始化高分辨率定时器 */
void esptim_callback(void *arg); /* 定时器回调函数 */
#endif
.C文件
#include "esptim.h"
/**
* @brief 初始化高精度定时器(ESP_TIMER)
* @param tps: 定时器周期,以微妙为单位(μs),以一秒为定时器周期来执行一次定时器中断,
* 那此处tps = 1s = 1000000μs
* @retval 无
*/
void esptim_int_init(uint64_t tps)
{
esp_timer_handle_t esp_tim_handle; /* 定时器回调函数句柄 */
/* 定义一个定时器结构体 */
esp_timer_create_args_t tim_periodic_arg = {
.callback = &esptim_callback, /* 设置回调函数 */
.arg = NULL, /* 不携带参数 */
};
esp_timer_create(&tim_periodic_arg, &esp_tim_handle); /* 创建一个事件 */
esp_timer_start_periodic(esp_tim_handle, tps); /* 每周期内触发一次 */
}
/**
* @brief 定时器回调函数
* @param arg: 不携带参数
* @retval 无
*/
void esptim_callback(void *arg)
{
LED_TOGGLE();
}
main.C
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "led.h"
#include "esptim.h"
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
esptim_int_init(1000000); /* 初始化高分辨率定时器,此处设置定时器周期为1秒,
但该函数事宜微妙为单位进行计算,
故而1秒钟换算为1000000微秒 */
}