搜索
bottom↓
回复: 0
打印 上一主题 下一主题

《DNESP32S3使用指南-IDF版_V1.6》第十四章 ESPTIMER实验

[复制链接]

出0入234汤圆

跳转到指定楼层
1
发表于 2024-7-17 18:00:33 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729



第十四章 ESPTIMER实验


       ESP32-S3 芯片内置一组 52 位系统定时器。该定时器可用于生成操作系统所需的滴答定时中断,也可用作普通定时器生成周期或单次延时中断。通过本章的学习,开发者将学习到高分辨率定时器的使用。
本章分为如下几个小节:
       14.1 定时器简介
       14.2 硬件设计
       14.3 程序设计
       14.4 下载验证

       14.1 定时器简介
       定时器是单片机内部集成的功能,它能够通过编程进行灵活控制。单片机的定时功能依赖于内部的计数器实现,每当单片机经历一个机器周期并产生一个脉冲时,计数器就会递增。定时器的主要作用在于计时,当设定的时间到达后,它会触发中断,从而通知系统计时完成。在中断服务函数中,我们可以编写特定的程序以实现所需的功能。例如,若要让LED灯每秒钟切换一次状态,我们只需将定时器配置为每秒触发一次中断,并在中断处理程序中编写LED状态切换的逻辑。

       1,定时器能做什么
       定时器的主要作用包括但不限于:

       (1)执行定时任务:定时器常用于周期性执行特定任务。例如,若需要每500毫秒执行某项任务,定时器能够精准地满足这一需求。

       (2)时间测量:定时器能够精确测量时间,无论是代码段的执行时间还是事件发生的间隔时间,都能通过定时器进行准确的计量。

       (3)精确延时:对于需要微秒级精度的延时场景,定时器能够提供可靠的解决方案,确保延时的精确性。

       (4)PWM信号生成:通过定时器的精确控制,我们可以生成PWM(脉宽调制)信号,这对于驱动电机、调节LED亮度等应用至关重要。

       (5)事件触发与监控:定时器不仅用于触发中断,实现事件驱动的逻辑,还可用于实现看门狗功能,监控系统状态,并在必要时进行复位操作,确保系统的稳定运行。

       2,硬件定时器与软件定时器
       定时器既可通过硬件实现,也可基于软件进行设计,二者各具特色,适用于不同场景:
       硬件定时器,依托微控制器的内置硬件机制,通过专门的计时/计数器电路达成定时功能。其显著优势在于高精度与高可靠性,这是因为硬件定时器的工作独立于软件任务和操作系统调度,故而不受它们的影响。在追求极高定时精确度的场合,如生成PWM信号或进行精确时间测量时,硬件定时器无疑是最佳选择。其工作原理确保即便主CPU忙于其他任务,定时器也能在预设时间准确触发相应操作。
       而软件定时器,则是通过操作系统或软件库模拟实现的定时功能。这类定时器的性能受系统当前负载和任务调度策略制约,因此在精度上较硬件定时器稍逊一筹。然而,软件定时器在灵活性方面更胜一筹,允许创建大量定时器,适用于对时间控制要求不那么严格的场景。
       值得注意的是,软件定时器在某些情况下可能面临定时精度问题,特别是在系统负载较重或存在众多高优先级任务时。不过,对于简单的非高精度延时需求,软件定时器通常已经足够应对。

       3,ESP32-S3的定时器整体框架介绍
       下面先来简单了解一下系统定时器结构图,通过学习系统定时器结构图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。

图14.1.1系统定时器结构图

       系统定时器内置两个计数器UNIT0和UNIT1(如图14.1.1中①所示)以及三个比较器COMP0、COMP1、COMP2(如图14.1.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
       关于这三位的配置请参考《esp32-s3_technical_reference_manual_cn》。

       (2)比较器
       COMP0、COMP1、COMP2均为ESP32-S3系统定时器内置的52位比较器。比较器同样使用XTAL_CLK作为时钟源(40MHz)。

图14.1.2系统定时器生成报警

       图14.1.2展示了系统定时器生成报警的过程。在上述过程中用到一个计数器(Timer Countern)和一个比较器(Timer Comparatorx),比较器将根据比较结果,生成报警中断。

       14.2 硬件设计

       14.2.1 例程功能
       实验现象:程序运行后配置高分辨率定时器,并开启中断,在中断回调函数中翻转LED的状态。

       14.2.2 硬件资源
       1. LED
              LED - IO1
       2. 高分辨率定时器(ESP定时器)

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

       14.3 程序设计

       14.3.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

图14.3.1.1 ESPTIMER实验程序流程图

       14.3.2 ESPTIMER函数解析
       ESP-IDF提供了一套API来配置高精度定时器。要使用此功能,需要导入必要的头文件:
  1. #include "esp_timer.h"
复制代码
       接下来,作者将介绍一些常用的ESPTIMER函数,这些函数的描述及其作用如下:

       1,创建一个事件
       该函数用于创建ESPTIMER实例,其函数原型如下所示:
  1. esp_err_t esp_timer_create(const esp_timer_create_args_t* create_args,
  2.                            esp_timer_handle_t* out_handle);
复制代码
       该函数的形参描述如下表所示:

表14.3.2.1 函数esp_timer_create()形参描述

       返回值:ESP_OK表示创建成功。其他表示创建失败。
       下面是esp_timer_create_args_t结构体的成员变量描述。

表14.3.2.2 结构体tim_periodic_arg描述

       2,每个周期内触发一次
       该函数用于使能定时器的指定中断,其函数原型如下所示:
  1. esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer,
  2.                                                                                            uint64_t period_us);
复制代码
       该函数的形参描述,如下表所示:

表14.3.2.3 函数esp_timer_start_periodict()形参描述

       返回值:ESP_OK表示开启定时器成功。其他表示开启失败。

       14.3.3 ESPTIMER驱动解析
       在IDF版的05_esp_timer例程中,作者在05_esp_timer \components\BSP路径下新增了一个ESPTIM文件夹,用于存放esptim.c和esptim.h这两个文件。其中,esptim.h文件负责声明ESPTIMER相关的函数和变量,而esptim.c文件则实现了ESPTIMER的驱动代码。下面,我们将详细解析这两个文件的实现内容。

       1,esptim.h文件
  1. /* 函数声明 */
  2. void esptim_int_init(uint64_t tps);                 /* 初始化初始化高分辨率定时器 */
  3. void esptim_callback(void *arg);                    /* 定时器回调函数 */
复制代码

       2,esptim.c文件
  1. /**
  2. * @brief       初始化高精度定时器(ESP_TIMER)
  3. * @param       tps: 定时器周期,以微妙为单位(μs),以一秒为定时器周期来执行一次定时器中断,那此处tps = 1s = 1000000μs
  4. * @retval      无
  5. */

  6. void esptim_int_init(uint64_t tps)
  7. {
  8.     esp_timer_handle_t esp_tim_handle;                           /* 定时器回调函数句柄 */

  9.     /* 定义一个定时器结构体 */
  10.     esp_timer_create_args_t tim_periodic_arg = {
  11.         .callback = &esptim_callback,                             /* 设置回调函数 */
  12.         .arg = NULL,                                             /* 不携带参数 */
  13.     };

  14.     esp_timer_create(&tim_periodic_arg, &esp_tim_handle);        /* 创建一个事件 */
  15. esp_timer_start_periodic(esp_tim_handle, tps);               /* 每周期内触发一次 */
  16. }

  17. /**
  18. * @brief       定时器回调函数
  19. * @param       arg: 不携带参数
  20. * @retval      无
  21. */
  22. void esptim_callback(void *arg)
  23. {
  24.     LED_TOGGLE();
  25. }
复制代码
       从ESPTIMER的初始化代码中可以看到,结构体esp_timer_create_args_t通过其中两个结构体成员,以指针的形式调用定时器回调函数。传入的参数tim_periodic_arg,目的在于方便后续的调用,而esp_timer_create()函数便是通过指针的方式完成对该结构体的调用,之后再通过esp_timer_start_periodic()函数设定周期,最终完成ESPTIMER的初始化配置。
       在定时器回调函数中,我们调用了LED状态翻转函数,并设定,定时器的每一次计数溢出都会翻转一次LED的状态。关于ESPTIMER结构体的介绍,请读者回顾本章节的14.3.2小节内容。

       14.3.4 CMakeLists.txt文件
       打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
  1. set(src_dirs            
  2.             ESPTIM
  3.             LED)

  4. set(include_dirs
  5.             ESPTIM
  6.             LED)

  7. set(requires
  8.                          driver
  9.                          esp_timer)

  10. idf_component_register(SRC_DIRS ${src_dirs}
  11. INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

  12. component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
复制代码
       上述的红色ESPTIM驱动需要由开发者自行添加,以确保ESPTIM驱动能够顺利集成到构建系统中,需要注意的是我们的依赖库(requires)需要添加上ESP32-S3定时器的库。这一步骤是必不可少的,它确保了ESPTIM驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。

       14.3.5 实验应用代码
       打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
  1. /**
  2. * @brief       程序入口
  3. * @param       无
  4. * @retval      无
  5. */
  6. void app_main(void)
  7. {
  8.     esp_err_t ret;
  9.     uint8_t len = 0;
  10.     uint16_t times = 0;
  11.     unsigned char data[RX_BUF_SIZE] = {0};

  12.     ret = nvs_flash_init();        /* 初始化NVS */

  13. if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
  14.             ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  15.     {
  16.         ESP_ERROR_CHECK(nvs_flash_erase());
  17.         ret = nvs_flash_init();
  18.     }
  19.    
  20.     led_init();                                        /* 初始化LED */
  21.     esptim_int_init(1000000);         /* 初始化高分辨率定时器,此处设置定时器周期为1秒,
  22.                                    但该函数以微秒为单位进行计算,
  23.                                    故而1秒钟换算为1000000微秒 */
  24. }
复制代码
       从上面的代码中可以看到,ESP定时器的周期值配置为1000000,因为ESP32-S3高分辨率定时器的计数周期是以微秒作为基础单位进行运算,所以当我们设定计数周期为1秒时需要将单位换算为微秒。因此LED的闪烁周期为1秒。

       14.4 下载验证
       在完成编译和烧录操作后,可以看到板子上的LED在闪烁,LED在ESP定时器的中断回调函数中被改变状态,LED每间隔1000毫秒改变一次状态。

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-25 04:13

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表