【ESP32 IDF 定时器Timer】

目录

  • TIM定时器
    • 介绍
    • 硬件定时器和软件定时器
    • 硬件定时器基本参数
    • 硬件定时器的操作流程
    • 初始化硬件定时器
    • 设置报警
    • 注册回调函数
    • 使能和禁用定时器
    • 启动和停止定时器
    • 硬件定时器驱动代码
      • 调试
    • 软件定时器
      • 使用软件定时器
      • 代码编写

TIM定时器

介绍

定时器是单片机内部集成,可以通过编程控制。单片机的定时功能是通过计数来实现的,当单片机每一个机器周期产生一个脉冲时,计数器就加一。定时器的主要功能是用来计时,时间到达之后可以产生中断,提醒计时时间到,然后可以在中断函数中去执行功能。比如我们想让一个 led 灯 1 秒钟翻转一次,就可以使用定时器配置为 1 秒钟触发中断,然后在中断函数中执行 led 翻转的程序。
主要作用包括:

  1. 执行定时任务:定时器的最常见的使用场景就是执行定时任务。例如,如果你希望每隔一定的时间,如 500 毫秒,执行某个特定的任务,那么你可以使用定时器来实现这种需求。
  2. 时间测量:定时器也可以用于测量时间,例如,你可以使用定时器来测量某个代码段的执行时间或者测量某个事件发生的间隔时间。
  3. 精确延时:定时器也可用于产生精确的延时。例如,如果你需要一个精确到微秒级的延时,你可以使用定时器来实现这种延时。
  4. PWM 信号生成:通过定时器,你还可以实现生成 PWM(脉宽调制)信号,该信号可用于驱动电机或者调节 LED 的亮度等。
  5. 事件触发:很多时候我们需要通过定时器 trigger一些事件,如中断。此外,定时器还用于 watch dog(看门狗)的实现,用于监控或者复位系统。

硬件定时器和软件定时器

定时器可以基于硬件也可以基于软件实现,两者有各自的特点和适用场景:

  • 硬件定时器是由微控制器硬件提供的定时功能,由专门的计时/计数器电路实现。硬件定时器的最大优势在于精确度高和可靠性强,因为它们不受软件任务和操作系统调度的影响。当需要非常精确的定时功能,如产生PWM信号或者获取精确的时间测量时,硬件定时器是首选。由于定时操作由硬件直接完成,即使主CPU忙于其他任务,定时器仍然可以在预定时间到达时准确地执行回调操作。
  • 软件定时器是由操作系统或者软件库实现的定时器,它们利用操作系统提供的机制来模拟定时器功能。软件定时器的实现受到当前系统负载和任务调度策略的影响,因此相对来说不如硬件定时器精确。但是软件定时器通常更灵活,可以创建大量的定时器适用于不需要精确时间控制的场合
    在某些情况下,软件定时器可能会引起定时精度问题,例如在高负载条件下,或者当系统中有许多其他高优先级任务时。对于不需要高精度的简单延时,软件定时器通常足够使用。

硬件定时器基本参数

ESP32芯片具有两个通用定时器组,每个定时器组包含两个通用定时器,例如 Timer0、Timer1 等,每个定时器都包含多个通道。可以通过指定定时器号和通道号来选择具体使用的定时器和通道。每个定时器都可以单独地进行编程,并且每个定时器可以以微秒精度产生基于时间的中断。基本的定时器参数包括:定时器号、通道号、预分频器、自动重新加载值、定时器中断使能等。
在这里插入图片描述

以下是一些基本概念和定时器的共有属性:

  • 计时器(Counter): 定时器的核心组件,负责持续计数。
  • 定时器溢出(Overflow): 当计数器达到其最大值然后归零时发生。
  • 预置值(Preset Value): 计数器达到该值时会产生中断或其它事件。
  • 分频器(Prescaler): 用于减小计数器接收的时钟信号频率,以延长定时器的最大计时范围。
  • 中断(Interrupt): 当定时器达到预置值时,可以配置它来产生一个中断,中断处理程序将执行一些任务。

硬件定时器的操作流程

导入头文件

ESP-IDF(ESP32的官方开发框架)提供了一套 API 来配置和控制定时器。要使用 ESP32 定时器功能,首先需要在代码中包含必要的头文件,例如:

#include "driver/gptimer.h"

初始化硬件定时器

在这里插入图片描述
一般时钟选择默认即可
在这里插入图片描述

在这里插入图片描述

    //定义一个通用定时器实例gptimer_handle_t gptimer = NULL;//配置定时器gptimer_config_t timer_config = {.clk_src = GPTIMER_CLK_SRC_DEFAULT, //定时器时钟来源 选择APB作为默认选项.direction = GPTIMER_COUNT_UP,      //向上计数.resolution_hz = 1000000, // 1MHz, 1 tick=1us};//将配置设置到定时器实例中gptimer_new_timer(&timer_config, &gptimer);

做完上面的准备工作之后,按照STM32中的流程,我们应该要写中断函数了吧。

在ESP32中,我们换了个叫法,我们叫警报。所以流程就是配置定时器,然后配置警报,接着绑定警报的回调函数,这个回调函数可以理解成就是STM32中的中断函数。

设置报警

在这里插入图片描述
一共需要配置的结构体成员有三个。

第一个设置触发警报事件的目标计数值,也就是当计数器的值到达这个目标值的时候触发警报。

第二个设置重装载的值,一般咱就选择重装成0,然后计数模式是向上计数。

第三个选择是否自动重载,也就是说我们是否要这个定时器警报周期性的给我们警报。

给结构体的成员配置完之后,还需要使用gptimer_set_alarm_action这个函数去激活,第一个参数是定时器的句柄,第二个参数是上面配置好的结构体变量的地址。

在这里插入图片描述

esp_err_t gptimer_set_alarm_action(gptimer_handle_t timer, const gptimer_alarm_config_t *config)

注册回调函数

定时器启动后,可动态产生特定事件(如“警报事件”)。如需在事件发生时调用某些函数,可以通过gptimer_register_event_callbacks()将函数挂载到中断服务例程 (ISR)。

esp_err_t gptimer_register_event_callbacks(gptimer_handle_t timer, const gptimer_event_callbacks_t *cbs, void *user_data)

在这里插入图片描述
但注意这个下方框住的部分可以看出,函数必须确保这个函数不会试图阻塞,甚至是使用FreeRTOS的API

没错,ESP-IDF给我们自带了FreeRTOS的API
在这里插入图片描述

使能和禁用定时器

在对定时器进行控制之前,需要先调用 gptimer_enable() 使能定时器。此函数功能如下:

esp_err_t gptimer_enable(gptimer_handle_t timer)
  • 此函数将把定时器驱动程序的状态从 初始化 切换为 使能状态。
  • 如果 gptimer_register_event_callbacks() 已经延迟安装回调服务函数,此函数将使能回调服务函数。
    失能定时器
  • 调用 gptimer_disable() 会进行相反的操作,即将定时器驱动程序恢复到 初始化 状态,禁用回调服务并释放定时器。

启动和停止定时器

我们使能了定时器之后,并没有代表定时器已经开始运行,还需要通过调用 gptimer_start() 函数使内部计数器开始工作。而 gptimer_stop() 可以使计数器停止工作。

硬件定时器驱动代码

实现功能:每1s打印一次number的值,number计数至20归零重新计数
myTimer.c

#include "myTimer.h"// 定时器回调函数
/*** @函数说明        定时器回调函数* @传入参数        timer 定时器句柄, *                 *edata:定时器回调函数的一个指针,指向一个包含定时器警报事件数据的结构体。*                 *user_data:用户自带的数据,将传递进来的 user_data 转换为队列句柄,以便在回调函数中使用。* @函数返回        1:发送队列数据成功,0:失败*/
static bool IRAM_ATTR myTimer_Callback(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;/*这里可以写要执行的逻辑,但是不建议写,因为防止阻塞,可以写简单的逻辑,通知另一个任务,在另一个任务中执行逻辑*/static int time = 0;time %= 20;time++;// 从中断服务程序(ISR)中发送数据到队列xQueueSendFromISR(queue, &time, &high_task_awoken);return high_task_awoken == pdTRUE;
}/*** @函数说明        定时器初始化配置* @传入参数        resolution_hz=定时器的分辨率  alarm_count=触发警报事件的目标计数值* @函数返回        创建的定时器回调队列*/
QueueHandle_t myTimer_Init(uint32_t resolution_hz, uint64_t alarm_count) {gptimer_handle_t gptimer_handle = NULL; // 定时器句柄// 创建一个队列,使用了队列QueueHandle_t queue = xQueueCreate(10, sizeof(int));// 如果创建不成功if (!queue) {ESP_LOGE("queue", "Creating queue failed");return NULL;}// 配置定时器gptimer_config_t gptimer_config = {.clk_src = GPTIMER_CLK_SRC_DEFAULT, // 配置时钟源.direction = GPTIMER_COUNT_UP,      // 选择模式为向上计数.resolution_hz = resolution_hz      // 分辨率,即溢出频率 单位Hz 例如1e6=1MHz, t=1/f =us级别 1微秒};ESP_ERROR_CHECK(gptimer_new_timer(&gptimer_config, &gptimer_handle));// 配置警报gptimer_alarm_config_t gptimer_alarm_config = {.alarm_count = alarm_count,             // 设置报警时间 100000 us = 100ms,到达100ms就会清零重新开始计数.flags.auto_reload_on_alarm = true,     // 开启循环报警模式(重加载).reload_count = 0                       // 重装值};ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer_handle, &gptimer_alarm_config));// 配置回调函数gptimer_event_callbacks_t ecallbacks = {.on_alarm = myTimer_Callback};// 官方描述:确保 gptimer_register_event_callbacks 这个函数不会试图阻塞,甚至是使用FreeRTOS的API 这里使用了队列ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer_handle, &ecallbacks, queue));// 使能定时器ESP_ERROR_CHECK(gptimer_enable(gptimer_handle));// 开启定时器ESP_ERROR_CHECK(gptimer_start(gptimer_handle));return queue; // 返回配置好的Queue句柄
}

myTimer.h

#ifndef _myTimer__H
#define _myTimer__H
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <driver/gpio.h>
#include <driver/gptimer.h>
#include "esp_log.h"
#include "freertos/queue.h"QueueHandle_t  myTimer_Init(uint32_t resolution_hz, uint64_t alarm_count);
#endif

main.c

#include <stdio.h>
#include "myTimer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"static const char *TAG = "main";void app_main(void)
{int number = 0,countValue=0;QueueHandle_t queue = 0;// 初始化定时器 1秒进入回调函数一次queue = myTimer_Init(1e6,1e6);while(1){//从队列中接收一个数据,不能在中断服务函数使用if (xQueueReceive(queue, &number, pdMS_TO_TICKS(2000))) {ESP_LOGI(TAG, "Timer stopped, count=%d", number);} else {ESP_LOGW(TAG, "Missed one count event");}}}

调试

在这里插入图片描述

软件定时器

上面介绍的通用定时器实际上是硬件定时器,所以相对的,我们能用的还有软件定时器。

软件定时器就是系统模拟出来的定时器,而通用定时器是实实在在有的硬件定时器,因此硬件定时器的精度会更高,而软件定时器使用起来可以有很多个,并且代码编写方面也比较简单,但是相对的,精度会略微下降。

使用软件定时器

使用软件定时器首先需要包含头文件。

#include "esp_timer.h"

在这里插入图片描述

    esp_timer_handle_t  timer1=0;esp_timer_create_args_t timer1_arg = {.callback = &timer1Callback,.arg = NULL};esp_timer_create(&timer1_arg , &timer1);

配置好了的定时器的句柄之后可以启动
在这里插入图片描述

一共有两种启动方式,一种是周期性定时,另一种是一次性定时

传入的第一个参数是定时器句柄,第二个是启动的时间,单位是微秒
在这里插入图片描述
可以看出使用周期性启动和一次性启动后,如果要删除定时器,必须先停止,但停止时不能自己在自己的回调函数中删掉自己

使用esp_timer_stop()可以停止函数
在这里插入图片描述

代码编写

mySoftTimer.c
效果:定时器2每1s打印一次信息,总共打印四次,第五次时由于定时器1开始执行打印数据滨并删除定时器2,因删除了定时器2,所以定时器2停止打印

#include <unistd.h>
#include "esp_timer.h"
#include "esp_log.h"static const char *TAG = "mySoftTimer";esp_timer_handle_t timer1 = 0;
esp_timer_handle_t timer2 = 0;#define mySoftTimer_timer1_after_xus_start (5 * 1000 * 1000) //timer1定时器隔xus执行 5s
#define mySoftTimer_timer2_after_xus_start (1000 * 1000)     //timer2定时器隔xus执行 1s/*** @description: 定时器1的回调函数* @param {void} *arg 形参* @return {无}*/ 
static void mySoftTimer_timer1Callback(void *arg) {ESP_LOGI(TAG, "我只执行一次");//删除定时器2 ESP_ERROR_CHECK(esp_timer_stop(timer2));   // 删除前需要停止ESP_ERROR_CHECK(esp_timer_delete(timer2)); // 删除定时器
}/*** @description: 定时器2的回调函数* @param {void} *arg 形参* @return {无}*/ 
static void mySoftTimer_timer2Callback(void *arg) {static int count = 0;count++;ESP_LOGI(TAG, "这时我执行的第%d次,每过%dus执行一次", count, mySoftTimer_timer2_after_xus_start);
}void mySoftTimer_Init(void) {esp_timer_create_args_t timer1_arg = { // 配置定时器1.callback = &mySoftTimer_timer1Callback, // 定时器1的回调函数.arg = NULL,.name = "timer1" // 定时器名称(可选)};esp_timer_create_args_t timer2_arg = { // 配置定时器2.callback = &mySoftTimer_timer2Callback, // 定时器2的回调函数.arg = NULL,.name = "timer2" // 定时器名称(可选)};ESP_ERROR_CHECK(esp_timer_create(&timer1_arg, &timer1)); // 单次定时器,只执行一次ESP_ERROR_CHECK(esp_timer_start_once(timer1, mySoftTimer_timer1_after_xus_start)); // 5s后执行一次ESP_ERROR_CHECK(esp_timer_create(&timer2_arg, &timer2)); // 创建定时器2,周期执行ESP_ERROR_CHECK(esp_timer_start_periodic(timer2, mySoftTimer_timer2_after_xus_start)); // 1s执行一次,周期执行
}

mySoftTimer.h

#ifndef _mySoftTimer__H
#define _mySoftTimer__H
void mySoftTimer_Init(void);
#endif

main.c


#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "mySoftTimer.h"static const char *TAG = "main";void app_main(void) {ESP_LOGI(TAG, "Initializing soft timers...");mySoftTimer_Init(); // 软件定时器初始化while (1) {vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1 秒}
}

在这里插入图片描述
``````````````````````````````````完结撒花``````````````````````

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/49992.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙HarmonyOS开发:多种内置弹窗及自定义弹窗的详细使用指南

文章目录 一、消息提示框&#xff08;showToast&#xff09;1、导入模块2、语法3、参数4、示例5、效果 二、对话框&#xff08;showDialog&#xff09;1、导入模块2、语法3、参数4、示例5、效果 三、警告弹窗&#xff08;AlertDialog&#xff09;1、语法2、参数3、AlertDialogP…

STM32的GPIO输入输出方式设置示例

1、GPIO口做基本的输入/输出口使用时&#xff0c;输入有上拉输入、下拉输入、浮空输入&#xff08;既无上拉电阻也无下拉电阻&#xff09;3种输入方式&#xff1b;输出有开漏输出、推挽输出2种输出方式。 2、示例 &#xff08;1&#xff09;示例1&#xff1a;GPIO做输出的设置…

项目比赛经验分享:如何让即兴发言出彩

项目比赛经验分享&#xff1a;如何让即兴发言出彩 前言1. 顺势趁便法2. 词语撮要法3. 起承转合法4. 数字串连法结语 在项目管理和比赛的激烈竞争中&#xff0c;即兴发言往往成为展示个人魅力和团队精神的重要环节。如何在短时间内组织语言&#xff0c;表达清晰、有力的观点&…

基于JSP、java、Tomcat三者的项目实战--校园交易网(3)主页

前文功能的实现 技术支持&#xff1a;JAVA、JSP 服务器&#xff1a;TOMCAT 7.0.86 编程软件&#xff1a;IntelliJ IDEA 2021.1.3 x64 前文三篇登录和注册功能的实现 基于JSP、java、Tomcat、mysql三层交互的项目实战--校园交易网&#xff08;1&#xff09;-项目搭建&#xf…

Cisco路由器OSPF配置

Cisco路由器OSPF配置 本文已经迁移至 https://www.geekery.cn 本文档将详细介绍如何配置 OSPF&#xff08;开放最短路径优先&#xff09;协议&#xff0c;使用三个路由器&#xff1a;RA、RB 和 RC。一步一步说明每个路由器的配置、设置 IPv4 和 IPv6 地址的、配置OSPF 的启用以…

Cocos Creator2D游戏开发-(1)初始化设置

初心: 做一款微信或者抖音小游戏,然后发布,对于我来说这是一个新的赛道; 写这些文档的原因,记录一下自己学习过程,下次用的时候方便找 cocos creator版本: 3.8.3 当前小游戏飞机大战教程来源于: 抖音: 禅影 chanying001 源码目录: https://www.kdocs.cn/l/caLr6XCbEfPa 创建一个…

zeal 开发者离线文档工具

zeal是一款程序开发者不可或缺的离线文档查看器 下载地址 官网地址&#xff1a; windows版csdn下载(开箱即用含)&#xff1a;https://download.csdn.net/download/xzzteach/89588765 已离线 Android.docset Apache_HTTP_Server.docset Bash.docset Bootstrap_4.docset Bootst…

selenium入门超详细教程——网页自动化操作

文章目录 简介一、环境安装1.selenium安装2.安装浏览器驱动2.1 确定浏览器版本2.2 下载驱动 二、基础用法1.对页面进行操作1.1 初始化浏览器对象1.2 访问页面1.3 设置浏览器大小1.4 前进后退1.5 获取页面基础属性 2.定位页面元素3.模拟鼠标操作3.1 常用操作 4.模拟键盘操作4.1 …

测评Copilot和ChatGPT-4o从PDF创建PPT功能

关于 Copilot 由 PDF 创建 PPT&#xff0c;之前已经发过几篇文档说明&#xff1a; Copilot for Microsoft 365 现已支持从PDF创建Word和PPT Copilot读PDF文献一键生成PPT&#xff0c;吓傻了导师 Copilot一键将PDF转为PPT&#xff0c;治好了我的精神内耗 今天来测评对比 Cop…

在LabVIEW中使用Modbus

NI 提供了三种主要机制与 Modbus 设备进行接口&#xff1a;(1) 高级 OPC 服务器&#xff0c;(2) Modbus I/O 服务器&#xff0c;以及 (3) 在 NI LabVIEW 软件中通过 LabVIEW 实时或 LabVIEW 数据记录与监控控制&#xff08;DSC&#xff09;模块引入的低级 Modbus API。 LabVIE…

黑马Java零基础视频教程精华部分_8_学生管理系统

系列文章目录 文章目录 系列文章目录一、业务分析二、结合业务流程图编写代码1、Student.java代码&#xff1a;2、StudentSystem.java代码&#xff1a;3、标号&#xff08;‌label&#xff09;‌ 三、学生管理系统升级版 一、业务分析 需求文档如图所示&#xff1a; 根据需求…

【初阶数据结构篇】单链表的实现(赋源码)

文章目录 单链表的实现代码位置概念与结构概念&#xff1a;结构&#xff1a; 链表的性质链表的分类单链表的实现单链表的创建和打印及销毁单链表的创建单链表的打印单链表的销毁 单链表的插入单链表头插单链表尾插单链表在指定位置之前插入数据单链表在指定位置之后插入数据 单…

SAP MM学习笔记47 - 实地棚卸(库存盘点)

上一章讲了SAP MM模块种的出力管理&#xff08;消息管理&#xff09;。 SAP MM学习笔记46 - 购买中的出力管理(消息管理)-CSDN博客 本章讲库存盘点的相关内容。 - 库存盘点分3步走&#xff1a;实地棚卸票登录&#xff0c;检数入力&#xff0c;差异分析及转记 - SAP中还提供了…

学习笔记 韩顺平 零基础30天学会Java(2024.7.22)

P407 接口使用细节2 P407 接口课堂练习 对于最后一个的输出&#xff1a;B因为实现了A的接口&#xff0c;所以和继承一样&#xff0c;B可以访问A的变量 P409 接口VS继承 接口对单继承机制&#xff08;是指只能继承一个类&#xff09;进行了补充 也可以理解为&#xff0c;子类通过…

24种设计模式介绍与6大设计原则(电子版教程)

前言 您是一个初级的 coder,可以从中领会到怎么设计一段优秀的代码&#xff1b;您是一个高级程序员&#xff0c;可以从中全面了解到设计模式以及 Java 的边角技术的使用&#xff1b;您是一个顶级的系统分析师&#xff0c;可以从中获得共鸣&#xff0c;寻找到项目公共问题的解决…

安全哈希算法:SHA算法

&#x1f3af; 主题简介 SHA&#xff08;Secure Hash Algorithm&#xff09;是比MD5更安全的哈希算法。通过案例形式了解SHA算法的原理、实现方法及注意细节。无论你是Python爱好者还是JavaScript高手&#xff0c;这篇内容都将为你提供一个深入了解SHA算法的机会。 &#x1f…

Spring Boot集成screw实现数据库文档生成

1.什么是screw&#xff1f; 在企业级开发中、我们经常会有编写数据库表结构文档的时间付出&#xff0c;从业以来&#xff0c;待过几家企业&#xff0c;关于数据库表结构文档状态&#xff1a;要么没有、要么有、但都是手写、后期运维开发&#xff0c;需要手动进行维护到文档中&…

免费【2024】springboot 成都奥科厨具厂产品在线销售系统设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

【期末考试复习】学术英语阅读

文章目录 前言1. 一篇学术论文的组成部分2. this unit2.1 Unit 1 标题第一类&#xff1a;陈述句结构&#xff08;Declarative Sentence Structure&#xff09;第二类&#xff1a;名词群结构&#xff08;Nominal Group Construction&#xff09;第三类&#xff1a;复合结构&…