ESP32学习笔记_FreeRTOS(3)——SoftwareTimer

摘要(From AI):
这篇笔记全面介绍了 FreeRTOS 软件定时器的核心概念和使用方法,包括定时器的创建、管理、常用 API 和辅助函数,并通过示例代码演示了如何启动、重置和更改定时器的周期。它强调了软件定时器的灵活性、平台无关性以及与硬件定时器的对比

前言:本文档是本人在依照B站UP:Michael_ee的视频教程进行学习时所做的学习笔记,可能存在疏漏和错误,如有发现,望指正。

上一篇:ESP32学习笔记_FreeRTOS(2)——Queue

文章目录

  • Software Timer
    • Creating a Software Timer
      • xTimerCreate()
    • Managing Software Timers
      • xTimerStart()
      • xTimerStop()
      • pcTimerGetName()
      • pvTimerGetTimerID()
      • xTimerReset()
      • xTimerChangePeriod()
    • Example Code:Using Software Timers

参考资料:

[Michael_ee视频教程] https://www.bilibili.com/video/BV1Nb4y1q7xz?spm_id_from=333.788.videopod.sections&vd_source=4d8bd0ed3878ef81b239bf69bf38e741
freeRTOS官网
espressif 在线文档


Software Timer

硬件定时器会有数量等方面的限制,使用较不灵活,而软件定时器使用更为灵活,其与硬件、平台无关,在不同的 MCU 都可以使用 FreeRTOS API 进行调用

特性硬件定时器软件定时器
数量固定,受 MCU 硬件资源限制(通常只有几个)灵活,可以根据需要动态创建(受内存和任务管理能力限制)
依赖性依赖具体硬件平台,配置方式和功能因芯片而异与硬件平台无关,可通过 FreeRTOS API 在不同 MCU 上使用
精度高精度,直接由硬件计时,通常用于实时性要求高的场景精度依赖于 RTOS 的调度周期(tick 周期),不适合极高实时性场景
性能高性能,独立运行,不占用 CPU 资源运行在 RTOS 守护任务上下文中,占用 CPU 资源
适用场景适合时间敏感的应用,如 PWM 信号生成、脉冲捕获、输入输出事件计时等适合通用定时功能,如定时任务执行、软件超时处理等
灵活性配置固定,功能和用途受限灵活性高,可动态调整超时时间、回调函数等
使用复杂度配置复杂,需根据芯片手册手动设置寄存器使用方便,通过 FreeRTOS API 调用
移植性差,代码与硬件平台强耦合好,代码与硬件无关,便于跨平台移植

所有软件定时器的回调函数都在同一个RTOS守护任务(也称为“定时器服务任务”)的上下文中执行(该任务最初被称为“定时器服务任务”,因为最初它只用于执行软件定时器回调函数。现在同一任务也用于其他用途,因此被改名为更通用的“RTOS 守护任务”)
守护任务是一个标准的FreeRTOS任务,会在调度器启动时自动创建。其优先级和堆栈大小由编译时配置常量configTIMER_TASK_PRIORITYconfigTIMER_TASK_STACK_DEPTH分别设置,这两个常量在FreeRTOSConfig.h中定义

需要注意,软件定时器的回调函数是在 RTOS 守护任务的上下文中执行的,而不是在独立的任务中运行。因此,回调函数中不能调用可能使任务进入阻塞状态的 FreeRTOS API 函数,因为这会阻塞整个守护任务,导致系统运行异常

Creating a Software Timer

xTimerCreate()

xTimerCreate() 用于创建一个新的软件定时器,并返回一个句柄以引用创建的定时器

#include “FreeRTOS.h”
#include “timers.h”TimerHandle_t xTimerCreate( const char *pcTimerName,const TickType_t xTimerPeriod,const UBaseType_t uxAutoReload,void * const pvTimerID,TimerCallbackFunction_t pxCallbackFunction );

参数

  • pcTimerName定时器的名称,仅用于调试
  • xTimerPeriod定时器周期,单位为系统 tick,不能为 0。可以使用 pdMS_TO_TICKS()` 宏将毫秒转换为 tick。例如:
    • 100 tick直接设置为 100
    • 500ms可使用 pdMS_TO_TICKS(500),前提是 configTICK_RATE_HZ <= 1000
  • uxAutoReload设置定时器类型:
    • pdTRUE自动重载定时器(周期性触发)
    • pdFALSE一次性定时器(仅触发一次,可手动重新启动)
  • pvTimerID定时器标识符,用于在回调函数中区分不同的定时器,或在回调调用之间存储值
  • pxCallbackFunction:定时器到期时执行的回调函数,需符合 TimerCallbackFunction_t 原型:
void vCallbackFunctionExample(TimerHandle_t xTimer);

configTICK_RATE_HZ 是 FreeRTOS 配置文件 FreeRTOSConfig.h 中定义的一个宏,它表示 每秒系统 Tick 的次数,即 FreeRTOS 的调度器每秒中断的频率(单位为 Hz)
例如:
如果 configTICK_RATE_HZ = 1000,表示系统每 1 毫秒触发一次 Tick 中断
如果 configTICK_RATE_HZ = 100,表示系统每 10 毫秒触发一次 Tick 中断

返回值

  • NULL 定时器创建失败,原因可能是 FreeRTOS 堆内存不足
  • 其他值 定时器创建成功,返回的句柄可用于引用该定时器

配置要求(一般不用动)

  • FreeRTOSConfig.h 文件中,configUSE_TIMERSconfigSUPPORT_DYNAMIC_ALLOCATION 必须都设置为 1
  • 如果 configSUPPORT_DYNAMIC_ALLOCATION 未定义,其默认值为 1

创建定时器并不会立即启动。可以使用以下函数来启动或管理定时器

// 启动定时器。如果定时器已经在运行,则从当前时间重新开始。
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );// 重置(重新启动)定时器。确保定时器启动或重新计算到期时间。
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );// 从中断上下文启动定时器。等效于 xTimerStart(),用于中断中调用。
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,BaseType_t *pxHigherPriorityTaskWoken );// 从中断上下文重置(重新启动)定时器。等效于 xTimerReset(),用于中断中调用。
BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,BaseType_t *pxHigherPriorityTaskWoken );// 更改定时器的周期。如果定时器未运行,则会启动定时器。
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,TickType_t xNewPeriod,TickType_t xTicksToWait );// 从中断上下文更改定时器的周期。等效于 xTimerChangePeriod(),用于中断中调用。
BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer,TickType_t xNewPeriod,BaseType_t *pxHigherPriorityTaskWoken );

Managing Software Timers

xTimerStart()

xTimerStart() 用于启动一个软件定时器的运行

  • 如果定时器尚未运行,xTimerStart() 会计算一个到期时间,该时间相对于调用 xTimerStart() 的时刻
  • 如果定时器已经在运行,则 xTimerStart() 相当于调用了 xTimerReset(),即重置定时器
  • 定时器会在定义的周期后(即 xTimerStart() 调用后 n 个 tick)触发回调函数,除非定时器在此期间被停止、删除或重置
#include “FreeRTOS.h”
#include “timers.h”BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );

参数

  • xTimer:要启动、重置或重新启动的定时器句柄
  • xTicksToWait指定调用任务在 timer command queue 队列已满的情况下,等待空间可用的最大时间(单位为 tick)。这是任务在进入 Blocked 状态时的阻塞时间。如果队列已满,任务会被阻塞,直到有足够的空间来发送命令
    • 设置 xTicksToWaitportMAX_DELAY 将导致任务无限期等待,直到队列中有空间
    • 如果在调度器启动之前调用 xTimerStart(),则 xTicksToWait 会被忽略

当任务调用 xTimerStart() 或其他定时器相关 API 时,这些命令并不会立即由任务执行,而是通过一个队列传递给定时器服务任务
如果队列已满,新的命令会被阻塞,直到队列有空间可用。这时,调用 xTimerStart() 等 API 的任务会根据指定的阻塞时间(xTicksToWait)进入阻塞状态,等待队列空间变得可用
timer command queue 的大小由 FreeRTOS 的配置项决定。队列的大小设置影响系统可以同时处理多少个定时器命令。如果队列大小太小,可能会导致命令丢失或任务阻塞:
configTIMER_QUEUE_LENGTH:定义了 timer command queue 队列的最大长度(即可以存放多少个定时器命令)

  • configUSE_TIMERS:必须设置为 1,才能启用定时器功能和相关队列

返回值

  • pdPASS启动命令成功发送到定时器命令队列。如果指定了阻塞时间(即 xTicksToWait 不为零),则可能会因为队列已满,任务进入阻塞状态等待空间释放,直到数据成功写入队列
    • 定时器命令的处理时间会根据定时器服务任务的优先级而有所不同,但定时器的到期时间是相对于实际调用 xTimerStart() 时刻(从队列中取出命令并实际启动定时器)的
    • 定时器命令的处理时间受定时器服务任务优先级的影响,定时器服务任务的优先级由 configTIMER_TASK_PRIORITY 配置常量设置
  • pdFAIL启动命令未成功发送到定时器命令队列,原因是队列已满。如果指定了阻塞时间,任务会被阻塞等待队列有空间,直到指定的阻塞时间过期,但仍未成功写入数据到队列

注意事项(一般不用动)
FreeRTOSConfig.h 中,configUSE_TIMERS 必须设置为 1,才能使用 xTimerStart() 函数

xTimerStop()

xTimerStop() 用于停止一个运行中的软件定时器

  • 调用 xTimerStop() 可以停止一个正在运行的定时器。如果定时器已经停止或已过期,则调用 xTimerStop() 不会产生影响。
  • xTimerStop() 向定时器命令队列发送停止命令,定时器服务任务会在稍后处理该命令。
#include “FreeRTOS.h”
#include “timers.h”BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );

参数

  • xTimer要停止的定时器句柄。
  • xTicksToWait指定任务在定时器命令队列已满的情况下,最大等待时间(单位为 ticks)

返回值

  • pdPASS
  • pdFAIL

pcTimerGetName()

pcTimerGetName() 用于返回在创建定时器时分配的可读文本名称

#include “FreeRTOS.h”
#include “timers.h”const char * pcTimerGetName( TimerHandle_t xTimer );

返回值

  • 返回值为一个指向定时器名称的指针。
  • 定时器名称是一个标准的以 NULL 结尾的 C 字符串。

pvTimerGetTimerID()

pvTimerGetTimerID() 用于返回与定时器关联的标识符(ID)

  • 返回在创建定时器时分配的标识符,该标识符可以通过 vTimerSetTimerID() API 更新
  • 在回调函数中可以使用该标识符区分哪个定时器到期,特别是在多个定时器共享相同的回调函数时
#include “FreeRTOS.h”
#include “timers.h”void *pvTimerGetTimerID( TimerHandle_t xTimer );

返回值

  • 返回与指定定时器关联的标识符(指针类型)

xTimerReset()

xTimerReset() 用于重置、启动或重新启动一个软件定时器,能够起到 Watch Dog 的作用

  • 如果定时器正在运行,xTimerReset() 会将定时器的到期时间重新计算为相对于调用时间的周期
  • 如果定时器未运行,xTimerReset() 会启动定时器,并将到期时间计算为相对于调用时间的周期。此时等效于 xTimerStart()
  • 无论定时器当前是否运行,调用 xTimerReset() 后,定时器都将开始运行
#include “FreeRTOS.h”
#include “timers.h”BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );

返回值

  • pdPASS
  • pdFAIL

xTimerChangePeriod()

xTimerChangePeriod() 用于更改软件定时器的周期

  • 更改运行中定时器的周期
    • 如果定时器正在运行,则新周期将用于重新计算到期时间。
    • 新的到期时间相对于调用 xTimerChangePeriod() 的时刻,而不是定时器最初启动的时刻。
  • 启动未运行的定时器
    • 如果定时器未运行,则定时器将使用新的周期值计算到期时间,并开始运行。
#include “FreeRTOS.h”
#include “timers.h”BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,TickType_t xNewPeriod,TickType_t xTicksToWait );

参数

  • xTimer需要更改周期的定时器句柄
  • xNewPeriod定时器的新周期(单位为 tick)。可使用 pdMS_TO_TICKS() 将毫秒转换为 tick
  • xTicksToWait阻塞任务的最大时间(单位为 tick),如果定时器命令队列已满,则等待空间可用

返回值

  • pdPASS
  • pdFAIL

Example Code:Using Software Timers

#include <stdio.h>  
#include <inttypes.h>  
#include "sdkconfig.h"  
#include "freertos/FreeRTOS.h"  
#include "freertos/task.h"  
#include "esp_chip_info.h"  
#include "esp_flash.h"  
#include "esp_system.h"  #include "freertos/timers.h" // 定时器头文件  void TimerCallback(TimerHandle_t xTimer)  
{  const char *pcTimerName = pcTimerGetName(xTimer);// 获取定时器名称  uint32_t *uiTimerID = (uint32_t *)pvTimerGetTimerID(xTimer);// 获取定时器ID  printf("%s expired, ID: %lu.\n", pcTimerName, *uiTimerID);// 打印定时器名称和ID  
}  int id1 = 0;  
int id2 = 1;  void app_main(void)  
{  TimerHandle_t TimerHandle1 = NULL;  TimerHandle1 = xTimerCreate("Timer1", pdMS_TO_TICKS(1000), pdTRUE, (void *)&id1, TimerCallback);// 创建一个周期为1000ms的定时器  xTimerStart(TimerHandle1, 0);// 启动定时器  TimerHandle_t TimerHandle2 = NULL;  TimerHandle2 = xTimerCreate("Timer2", pdMS_TO_TICKS(2000), pdTRUE, (void *)&id2, TimerCallback);// 与Timer1公用同一个回调函数,观察pcTimerGetName的输出  xTimerStart(TimerHandle2, 0);// 启动定时器  // for(int i = 0; i < 10; i++)  // {    //     vTaskDelay(pdMS_TO_TICKS(1000));    //     xTimerReset(TimerHandle2, 0);// 重置定时器,观察pcTimerGetName的输出,此时Timer2不会被打印  // }  vTaskDelay(pdMS_TO_TICKS(5000));  xTimerChangePeriod(TimerHandle2, pdMS_TO_TICKS(1000), 0);// 修改Timer2的周期为1000ms  vTaskDelay(pdMS_TO_TICKS(5000));  xTimerStop(TimerHandle1, 0);// 停止定时器  xTimerStop(TimerHandle2, 0);// 停止定时器  
}

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

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

相关文章

多线程常见问题集

一、多线程预防和避免线程死锁 如何预防死锁&#xff1f; 破坏死锁的产生的必要条件即可&#xff1a; 破坏请求与保持条件&#xff1a;一次性申请所有的资源。破坏不剥夺条件&#xff1a;占用部分资源的线程进一步申请其他资源时&#xff0c;如果申请不到&#xff0c;可以主动释…

Java ArrayList 与顺序表:在编程海洋中把握数据结构的关键之锚

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 前言&#xff1a;在 Java编程的广袤世界里&#xff0c;数据结构犹如精巧的建筑蓝图&#xff0c;决定着程序在数据处理与存储时的效率、灵活性以…

【第三方云音乐播放器SPlayer本地安装结合内网穿透打造个性化远程音乐库】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 前言1. 安装Docker2. 创建并启动Splayer容器3. 本地访问测试4. 公网远程访问本地Splayer4.1 内网穿…

easyui combobox 只能选择第一个问题解决

easyui combobox 只能选择第一个问题解决 问题现象 在拆分开票的时候&#xff0c;弹出框上面有一个下拉框用于选择需要新增的明细行&#xff0c;但是每次只能选择到第一个 选择第二条数据的时候默认选择到第一个了 代码如下 /*新增发票编辑窗口*/function addTicketDialog…

从零开始:Linux 环境下的 C/C++ 编译教程

个人主页&#xff1a;chian-ocean 文章专栏 前言&#xff1a; GCC&#xff08;GNU Compiler Collection&#xff09;是一个功能强大的编译器集合&#xff0c;支持多种语言&#xff0c;包括 C 和 C。其中 gcc 用于 C 语言编译&#xff0c;g 专用于 C 编译。 Linux GCC or G的安…

transformer.js(三):底层架构及性能优化指南

Transformer.js 是一个轻量级、功能强大的 JavaScript 库&#xff0c;专注于在浏览器中运行 Transformer 模型&#xff0c;为前端开发者提供了高效实现自然语言处理&#xff08;NLP&#xff09;任务的能力。本文将详细解析 Transformer.js 的底层架构&#xff0c;并提供实用的性…

STM32 Keil5 attribute 关键字的用法

这篇文章记录一下STM32中attribute的用法。之前做项目的时候产品需要支持远程升级&#xff0c;要求版本只能向上迭代&#xff0c;不支持回退。当时想到的方案是把版本号放到bin文件的头部&#xff0c;设备端收到bin文件的首包部数据后判断是否满足升级要求&#xff0c;这里就可…

aws服务--机密数据存储KMS(1)介绍和使用

在AWS(Amazon Web Services)中存储机密数据时,安全性和合规性是最重要的考虑因素。AWS 提供了多个服务和工具,帮助用户确保数据的安全性、机密性以及合规性。AWS Secrets Manager、KMS(Key Management Service)是推荐的存储机密数据的AWS服务和最佳实践。这里先看KMS。 …

51c~C语言~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/12652943 一、嵌入式开发中的C语言编译器 如果你和一个优秀的程序员共事&#xff0c;你会发现他对他使用的工具非常熟悉&#xff0c;就像一个画家了解他的画具一样。----比尔.盖茨1 不能简单的认为是个工具 嵌入式程序开发…

ensp静态路由实验

一、实验目的 1、熟练掌握交换机的基本配置命令 2、熟练掌握静态路由的使用方法 3. 熟练掌握交换机端口模式 二、实验内容 需求&#xff1a; 根据要求利用现有实验设备组建小型局域网 实验设备&#xff1a; 交换机S37002台&#xff1b;PC机2台&#xff1b;路由器2台。 …

深度学习3

五、自动微分 1、基础概念 模块 autograd 负责自动计算张量操作的梯度&#xff0c;具有自动求导功能&#xff1b;autograd 创建一个动态计算图来跟踪张量的操作&#xff0c;每个张量是计算图中的一个节点&#xff0c;节点之间的操作构成图的边。 属性 requires_grad 决定…

路由器中继与桥接

一 . 背景 现在的路由器大多数已经开始支持多种网络连接模式&#xff0c;以下将以TP-Link迷你无线路由器为例进行展开介绍。在TP-Link迷你无线路由器上一般有AP&#xff08;接入点&#xff09;模式&#xff0c;Router&#xff08;无线路由&#xff09;模式&#xff0c;Repeate…

人工智能|计算机视觉——微表情识别(Micro expression recognition)的研究现状

一、简述 微表情是一种特殊的面部表情,与普通的表情相比,微表情主要有以下特点: 持续时间短,通常只有1/25s~1/3s;动作强度低,难以察觉;在无意识状态下产生,通常难以掩饰或伪装;对微表情的分析通常需要在视频中,而普通表情在图像中就可以分析。由于微表情在无意识状态…

嵌入式系统与OpenCV

目录 一、OpenCV 简介 二、嵌入式 OpenCV 的安装方法 1. Ubuntu 系统下的安装 2. 嵌入式 ARM 系统中的安装 3. Windows10 和树莓派系统下的安装 三、嵌入式 OpenCV 的性能优化 1. 介绍嵌入式平台上对 OpenCV 进行优化的必要性。 2. 利用嵌入式开发工具&#xff0c;如优…

React(五)——useContecxt/Reducer/useCallback/useRef/React.memo/useMemo

文章目录 项目地址十六、useContecxt十七、useReducer十八、React.memo以及产生的问题18.1组件嵌套的渲染规律18.2 React.memo18.3 引出问题 十九、useCallback和useMemo19.1 useCallback对函数进行缓存19.2 useMemo19.2.1 基本的使用19.2.2 缓存属性数据 19.2.3 对于更新的理解…

STM32设计学生宿舍监测控制系统-分享

目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 电路图采用Altium Designer进行设计&#xff1a; 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 本项目旨在利用STM32单片机为核心&#xff0c;结合传感器技术、无线通信技…

华为无线AC+AP组网实际应用小结

之前公司都是使用的H3C的交换机、防火墙以及无线AC和AP的&#xff0c;最近优化下无线网络&#xff0c;说新的设备用华为的&#xff0c;然后我是直到要部署的当天才知道用华为设备的&#xff0c;就很无语了&#xff0c;一点准备没有&#xff0c;以下为这次的实际操作记录吧&…

Linux麦克风录音实战

在 Linux 上使用麦克风进行录音可以通过多种方式实现&#xff0c;包括使用命令行工具、图形界面应用程序以及编程接口。下面我将介绍几种常见的方法&#xff0c;从简单的命令行工具到使用 PortAudio 库进行编程。 一. 使用arecord命令行工具 arecord 是 ALSA&#xff08;Adva…

虚拟苹果系统MacOS中新建自定义C++Dylib并用C++测试程序测试

前言 苹果系统中Dylib的建立和使用是一个非常基础的功能。本博客使用苹果虚拟机MacOS Ventura 13.6.7&#xff0c;XCode15.2&#xff0c;来复现这个过程。供参考。 1、Dylib框架的建立 2、增加一个函数 注意&#xff0c;向导自动生成的Helloworld函数中嵌套了一个函数Helloworl…

Windows系统电脑安装TightVNC服务端结合内网穿透实现异地远程桌面

文章目录 前言1. 安装TightVNC服务端2. 局域网VNC远程测试3. Win安装Cpolar工具4. 配置VNC远程地址5. VNC远程桌面连接6. 固定VNC远程地址7. 固定VNC地址测试 前言 在追求高效、便捷的数字化办公与生活的今天&#xff0c;远程桌面服务成为了连接不同地点、不同设备之间的重要桥…