ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《ESP32S3 Arduino开发指南 V1.0 》第十一章 TIMER_IT实验

《ESP32S3 Arduino开发指南 V1.0 》第十一章 TIMER_IT实验

原创 嵌入式/内核开发 作者:正点原子 时间:2024-12-05 09:09:51 0 删除 编辑

第十一章 TIMER_IT实验


       本章,我们将学习ESP32-S3的定时器,教会大家如何使用ESP32-S3的定时器实现定时功能。在本章中,我们将实现如下功能:开启ESP32-S3的定时器,并在定时器的回调函数中,翻转LED灯的状态。

       本章分为如下几个小节:

       11.1 定时器介绍

       11.2 硬件设计

       11.3 软件设计

       11.4 下载验证


        11.1 定时器简介


       11.1.1 定时器介绍

       定时器,顾名思义,用于设置定时。平常我们设定计时或闹钟,时间到了就告诉我们要做什么了。而这里的定时器同样也是如此,也需要设定定时到后的操作。

       ESP32-S3有通用定时器、系统定时器和看门狗定时器,本章主要讲解的是通用定时器。

       ESP32-S3有两个硬件定时器组,定时器组0和定时器组1,每组有两个硬件通用定时器,所以总共是有4个硬件通用定时器。它们都是基于16位预分频器和54位可自动重载的向上/向下计数器实现定时功能。

       ESP32-S3的计数频率为80MHz,假如对16位预分频器设置预分频系数为80,那么可得到1MHz的计数信号,每个计数信号的周期为1us,即每个计数单位为1us。基于要设定的时间,就可以对计数器进行设置。打个比方,要定时10ms,而每个计数周期为1us,这里得计算10ms需要多少个这样的1us周期:10ms / 1us = 10000,计数器就需要设置为10000,实现10ms定时,这个举例过程如下图所示。


图11.1.1.1 定时器定时配置过程


       当设置好定时器的预分频器以及计数器以及开启定时器,这时候定时开始,当计数值达到9999时,即到达设定时间,就会跳进中断回调函数中执行,执行完毕再回到主程序中运行。


       11.1.2 定时器函数介绍

       本小节介绍到的函数可在以下文件中找到:

       Arduino15\packages\esp32\hardware\esp32\2.0.11\cores\esp32\esp32-hal-timer.c

       接下来,我们介绍一下本章节所用到的TIMER相关函数。

       第一个函数:timerBegin函数,该函数功能是初始化一个定时器对象。

hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp);

       参数num为定时器编号,0到3,对应4个硬件通用定时器;

       参数divider为预分频系数;

       参数countUp为计数器计数方向标志,true:向上计数;false:向下计数

       返回值:定时器结构体指针

       第二个函数:timerAttachInterrupt函数,该函数功能是为目标定时器绑定一个中断回调函数,配置定时器中断。

void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(), bool edge);

       参数*timer为已初始化的目标定时器结构体指针;

       参数(*fn)()为定时器中断回调函数的函数指针;

       参数edge为中断触发类型,true:边沿触发,false:电平触发;

       返回值

       第三个函数:timerAlarmWrite函数,该函数功能是为目标定时器设置间隔定时参数和是否自动重装载。

void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload);

       参数*timer为已初始化的目标定时器结构体指针;

       参数alarm_value为最大计数值。向上计数到达该数值溢出,触发中断;

       参数autoreload为定时器在产生中断时是否重新加载的标志。true:自动加载,循环间隔定时,false:不自动加载,只进行一次间隔定时。

       无返回值。

       第四个函数:timerAlarmEnable函数,该函数功能是使能定时器,开始间隔定时。

void timerAlarmEnable(hw_timer_t *timer);

       参数*timer为已初始化的目标定时器结构体指针;

       无返回值。


        11.2 硬件设计


       1. 例程功能

       程序启动后配置定时器的定时时间为500毫秒,定时到来时执行中断回调函数翻转LED状态。


       2. 硬件资源


       1)LED灯

              LED-IO1


       2)Timer0


       3. 原理图

       本章实验使用的定时器为ESP32-S3的片上资源,因此并没有相应的连接原理图。


        11.3 软件设计


       11.3.1 程序流程图

       下面看看本实验的程序流程图:


图11.3.1.1 程序流程图


       11.3.2 程序解析


       1. timer驱动代码

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。EXTI驱动源码包括两个文件:tim.cpp和tim.h。

       下面我们先解析tim.h的程序。我们做了定时器的相关定义。

#define TIMx_INT        0
#define TIMx_ISR        tim0_ISR

       我们选择使用通用定时器0,当然你也可以通过改变TIMx_INT的值进行初始化别的定时器。TIMx_ISR宏是定时器0的中断回调函数,采用该宏移植性更加强。

       下面我们再解析tim.cpp的程序,这里有两个函数timx_int_init和TIMX_ISR,其定义如下:

hw_timer_t *timer = NULL;
 
/**
 * @brief   定时器TIMX定时中断初始化函数
 * @note
 *           定时器的时钟来自APB,而APB为80M
 *          所以定时器时钟 = (80/psc)Mhz, 单位时间为 1 / (80 / psc) = x us
 *         定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us
 *         Ft=定时器工作频率,单位:Mhz
 *
 * @param   arr: 自动重装值
 * @param   psc: 时钟预分频数
 * @retval 无
 */
void timx_int_init(uint16_t arr, uint16_t psc)
{
    timer = timerBegin(TIMx_INT, psc, true);      /* 初始化定时器0 */
 
    timerAlarmWrite(timer, arr, true);             /* 设置中断时间 */
 
    timerAttachInterrupt(timer, &TIMx_ISR, true);  /* 配置定时器中断回调函数 */
 
    timerAlarmEnable(timer);                          /* 使能定时器中断 */
}
 
/**
 * @brief  定时器TIMX中断回调函数
 * @param  无
 * @retval 无
 */
void TIMx_ISR(void)
{
    LED_TOGGLE();
}

       timx_int_init函数是初始化定时器0,首先调用timerBegin函数初始化一个定时器对象并采用向上计数方式,然后调用timerAlarmWrite函数配置中断时间并设置自动加载,后面再调用timerAttachInterrupt函数设置定时器边沿触发中断,最后调用timerAlarmEnable函数使能开启定时器。

       注意:定时器的定时时间是由timx_int_init函数的参数决定,这里我们定义了一个全局的hw_timer_t类型的结构体指针timer;timerAlarmWrite函数的autoreload参数需要设置成true,才能一直循环间隔定时,否则只会执行一次就停下来。

       TIMx_ISR函数就是定时器0中断回调函数。函数内部很简单,就是对LED灯的状态进行翻转。


       2. 05_timer_it.ino代码

       在05_timer_it.ino里面编写如下代码:

#include "tim.h"
#include "led.h"
 
 
/**
 * @brief   当程序开始执行时,将调用setup()函数,通常用来初始化变量、函数等
 * @param   无
 * @retval  无
 */
void setup() 
{
    led_init();                  /* LED初始化 */
    timx_int_init(5000, 8000);  /* 定时器初始化,定时时间为500ms */
}
 
/**
 * @brief   循环函数,通常放程序的主体或者需要不断刷新的语句
 * @param   无
 * @retval  无
 */
void loop() 
{
    /* 死循环,不做事情,等待定时器中断触发 */
    delay(1000);
}

       在setup函数中,除了要调用led_init函数对LED灯进行初始化,还要调用timx_int_init函数初始化定时器0,通过参数我们可以算出定时时间为500毫秒。启动定时器后,每隔500毫秒进入我们编写好的定时器中断回调函数中,执行LED灯状态翻转操作。

       由于该实验是用来测试定时器中断的,所以在loop函数中,不需要做事情,调用delay函数即可。


        11.4 下载验证

       下载代码完成后,ESP32-S3开发板每隔500毫秒触发一次中断,然后在中断回调函数中切换LED灯的状态。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3054704/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764184