FreeRTOS基础五:软件定时器

2023-11-11

软件定时器简介

软件定时器的作用:在指定的时间到来时执行指定的函数,或者以某个频率周期性地执行某个函数。被执行的函数叫做软件定时器回调函数

软件定时器由FreeRTOS内核实现,不需要硬件支持。软件定时器只有在软件定时器回调函数被调用时才需要占用CPU时间

在FreeRTOS应用程序中使用软件定时器,则需要:

  • 将软件定时器的源码(FreeRTOS/Source/timers.c)添加到项目中
  • 在FreeRTOSConfig.h中定义configUSE_TIMERS为1

软件定时器回调函数

软件定时器回调函数的原型如下:

//参数xTimer :因定时到期而调用这个回调函数的定时器的句柄
void ATimerCallback( TimerHandle_t xTimer );

忠告:软件定时器回调函数是在软件定时器任务中被执行的,这个任务是在vTaskStartScheduler()函数内部由内核自动创建的。不要在回调函数中使用一些导致任务阻塞的函数或代码,例如vTaskDelay(),否则会导致FreeRTOS后台任务进入到阻塞状态。而且应该尽量让定时器回调函代码简洁高效快速执行。

软件定时器任务

软件定时器任务现在最准确的叫法应该叫做FreeRTOS后台任务(FreeRTOS daemon task),因为早期的FreeRTOS专门用这个任务去实现软件定时器,而随着FreeRTOS版本更新,这个任务也用于执行一些其他的操作。本文为了便于理解都叫软件定时器任务。

在FreeRTOS中,可以创建多个软件定时器,所有的软件定时器回调函数都执行在软件定时器任务上下文环境中。

软件定时器任务在调度器启动时由内核自动创建,它是一个标准的FreeRTOS任务。当调用vTaskStartScheduler()来开启调度器后,这个函数内部使用xTimerCreateTimerTask()来创建软件定时器任务。

软件定时器任务的任务优先级和栈深度是在FreeRTOSConfig.h中通过configTIMER_TASK_PRIORITY和configTIMER_TASK_STACK_DEPTH这两个宏配置的。

软件定时器的属性

软件定时器的类型

  • 单次触发定时器:当定时器启动,并到达定时时间后,回调函数只会执行一次。定时器不会自动重新启动,但可以手动启动
  • 自动重装定时器:每次达到定时时间间隔后,除了执行回调函数,还会自动重新启动,可以实现周期性执行回调函数

如下图所示,Timer1是单次触发定时器,定时时间为6个tick;Timer2是自动重装定时器,定时时间为5个tick。两个定时器都在t1时刻启动。Timer1的回调函数在t7时刻执行一次就不再执行了,而Timer2的回调函数则会以5个tick的固定周期反复执行。

软件定时器的状态

  • 休眠状态:休眠状态时,定时器是存在的,可以通过它的句柄来操作它,但是它没有运行,没有计时,所以它的回调函数不会执行
  • 运行状态:定时器正在计时,包括正在执行它的回调函数

如下图所示,单次触发定时器和自动重装定时器主要的区别在于,当定时到期后,自动重装定时器会执行回调函数并再次进入到运行态,而单次触发定时器则会执行回调函数并进入到休眠状态。

 

软件定时器命令队列

软件定时器的相关API本质上是操作队列的API。当FreeRTOS调度器启动时,内核除了自动创建软件定时器任务外,还会自动创建一个软件定时器命令队列。在用户任务中使用软件定时器相关的API,例如启动定时器,停止定时器,复位定时器,本质上就是通过这个队列来向软件定时器任务发送相关消息。

软件定时器命令队列的长度在FreeRTOSConfig.h中使用configTIMER_QUEUE_LENGTH来定义。

所以软件定时器大部分情况下阻塞状态,只有当用户发送了一个软件定时器命令或者一个软件定时器定时器到期需要执行回调函数时,才会被唤醒进入就绪状态。

那么为什么说不能在定时器回调函数中使用让软件定时器阻塞的代码呢?因为软件定时器任务的状态只应该由内核或者定时器API来控制而不能由用户代码控制,这样才能保证软件定时器的能正常使用。

软件定时器的使用

创建软件定时器

使用xTimerCreate()函数来创建一个软件定时器对象,并返回它的句柄。创建好的软件定时器的初始状态为休眠状态。可以在FreeRTOS调度器运行之前创建软件定时器对象,也可以在一个任务中创建软件定时器对象。

TimerHandle_t xTimerCreate( const char * const pcTimerName,
                            TickType_t xTimerPeriodInTicks,
                            UBaseType_t uxAutoReload,
                            void * pvTimerID,
                            TimerCallbackFunction_t pxCallbackFunction );

参数pcTimerName:软件定时器的名称,仅仅用于调试目的,内核不使用这个参数。

参数xTimerPeriodInTicks:软件定时器的定时周期,也就是从定时器启动后多少个tick后定时到期。开发者可以使用pdMS_TO_TICKS()来将毫秒转换为tick数。

参数uxAutoReload:pdTRUE来创建自动重装定时器;pdFALSE来创建单次定时器。

参数pvTimerID:定时器ID。当多个软件定时器使用同一个回调函数时,此参数非常有用。如果不需要使用则传递NULL即可。

参数pxCallbackFunction:回调函数的指针

返回值:返回NULL代表没有足够的堆内存来创建当前定时器;否则返回当前定时器的句柄。

启动软件定时器

使用xTimerStart() 来让一个处于休眠状态的定时器开始运行,也可以让一个处于运行状态的定时器重新开始计数运行。可以在调度器运行之前调用xTimerStart(),但是只有当调度器运行后,定时器才能开始运行。

BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, TickType_t xTicksToWait );

参数xTimer:定时器的句柄。

参数xTicksToWait:xTimerStart()函数本质上是向软件定时器队列中放一个启动定时器的命令,这个参数值就是当队列满时,调用这个函数的任务阻塞并等待队列有空闲空间可以接收新的指令时阻塞的tick数。如果参数为0,则当队列满时,这个函数会立刻返回而不阻塞调用者任务。如果参数为portMAX_DELAY(需要在FreeRTOSConfig.h中将INCLUDE_vTaskSuspend定义为1),则当队列一直满时等待时间为永久等待并阻塞。如果在调度器运行之前调用xTimerStart(),则此参数作用失效,等价于参数为0。

返回值:当定时器启动指令成功发送到软件定时器队列时,返回pdPASS。如果xTicksToWait不为0,则调用者任务可能会阻塞,但是在阻塞时间超时前命令队列有空闲空间使得指令成功发送了,则也是会返回pdPASS的。当定时器启动指令没有发送到软件定时器队列时,返回pdFAIL。如果xTicksToWait不为0,则调用者任务可能一直阻塞,直到阻塞时间超时,命令队列也没有空闲空间,则返回pdFAIL。

复位软件定时器

函数xTimerReset()用于复位一个已经存在的软件定时器对象。如果软件定时器正在运行,则调用后软件定时器将立刻从新开始一个定时周期,从头开始计时。如果定时器处于休眠状态,则调用效果和调用xTimerStart()的一样。所以调用xTimerReset()成功后会确保这个定时器一定是处于运行状态。

//参数xTimer:软件定时器的句柄
//参数xTicksToWait:参见xTimerStart()函数的解释
//返回值:参见xTimerStart()函数的解释
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );

停止软件定时器

使用xTimerStop()来停止一个正在运行的软件定时器。调用xTimerStop()成功后会确保这个定时器一定是非运行状态(休眠状态)。

//参数xTimer:软件定时器的句柄
//参数xTicksToWait:参见xTimerStart()函数的解释
//返回值:参见xTimerStart()函数的解释
BaseType_t xTimerStop( TimerHandle_t xTimer,
                           TickType_t xTicksToWait);

修改软件定时器定时周期

使用xTimerChangePeriod()来修改一个已经存在的软件定时器的定时周期。无论之前定时器的状态是什么,此函数调用成功后,定时器将变为运行态,开始运行。

//参数xTimer:软件定时器的句柄

//参数xNewPeriod:新的定时周期,以tick为单位。

//参数xTicksToWait:参见xTimerStart()函数的解释

//返回值:参见xTimerStart()函数的解释
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
                                   TickType_t xNewPeriod,
                                   TickType_t xTicksToWait);

删除软件定时器

使用xTimerDelete()函数来删除一个已经存在的软件定时器,软件定时器可以在任何时间被删除。

//参数xTimer:软件定时器的句柄

//参数xTicksToWait:参见xTimerStart()函数的解释

//返回值:参见xTimerStart()函数的解释
BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );

定时器ID

在使用软件xTimerCreate()来创建软件定时器时需要指定一个void*类型的TimerID参数,所以可以使用整数值作为ID参数,或者任何类型的指针(字符串指针,函数指针等)。

定时器ID可以当做区分不同定时器的标志,因为多个定时器实例可以使用同一个定时器回调函数,这个时候在回调函数中可以读取定时器的ID来区别到底是哪个定时器到期了。

可以使用vTimerSetTimerID()来修改软件定时器的ID,也可以使用pvTimerGetTimerID()来读取一个软件定时器的ID。这两个函数直接作用在定时器对象上,而不需要通过软件定时器队列来工作。

//参数xTimer:软件定时器定时器句柄
//参数pvNewID:新的ID值
void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID );


//参数xTimer:软件定时器定时器句柄
//返回:软件定时器的ID值
void *pvTimerGetTimerID( TimerHandle_t xTimer );

软件定时器的使用心得

1、软件定时器的最小定时周期等于一个tick周期。如果你需要更短的定时周期则考虑使用硬件定时器。

2、软件定时器的精度一般没有硬件定时器的高,但基本可以满足一般定时需求。提高软件定时器任务的优先级为最高可以提高软件定时器的精度。

3、软件定时器一般用于处理一些逻辑不复杂的,简短的定时任务,它的优点是简单方便,占用的系统资源低,缺点是不够灵活,扩展性不强,当业务逻辑复杂时,考虑使用单独的任务去做。

4、不能在软件定时器回调函数中执行让软件定时器任务阻塞的代码。要尽量保持回调函数简短高效执行。

5、软件定时器可以开多个,理论上只要FreeRTOS的堆内存足够,就可以继续创建新的软件定时器。

6、软件定时器的相关API同样有两个版本,普通版本和中断安全版本(FromISR),使用时需要注意在中断函数中只能使用中断安全版本的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS基础五:软件定时器 的相关文章

  • C# Yield

    https blogs msdn microsoft com oldnewthing 20080814 00 p 21243 https blogs msdn microsoft com oldnewthing 20080815 00 p
  • NIM Server、Client交互操作过程分析——暨NIM防火墙要求

    最后更新2021 08 10 nimsh端口 从AIX 5 2 TL7开始 NIM增加了 nimsh 功能 此功能是原有NIM基于rsh协议操作的升级版 众所周知 rsh协议没有加密传输功能 安全性也很差 nimsh对此进行了升级 以提供更
  • lowbit

    lowbit用来计算二进制数 从右往左数第一个1与其后面的0组成的数 int lowbit int x return x x x 12 1100 lowbit 12 100 4 7 111 lowbit 7 1 1
  • Flutter优秀第三方常用框架

    名称 GitHub地址 下拉刷新上拉加载 EasyRefresh 下拉刷新上拉加载 PullToRefresh SharedPreferences shared preferences 中国城市选择器 city picker 设备信息 de

随机推荐

  • 硬盘存储知识

    存储知识 内存和外存 硬盘 1 物理磁盘类型 硬盘分为 机械硬盘 HDD 和固态硬盘 SSD 注意 买硬盘的时候要注意转速 机械硬盘是以下三种 物理磁盘类型 SATA盘 物理磁盘类型 SAS盘 物理磁盘类型 NL SAS盘 固态硬盘 物理磁
  • 微信小程序期末大作业 中草药小程序 药海拾遗

    微信小程序期末大作业 中草药小程序 药海拾遗 小程序详情如下 下载链接在文末 学习社区可以自己添加内容 点我下载资源 https download csdn net download weixin 43474701 59675965
  • 【Struts2六】ui标签之form标签及数据回显

    ui标签 用在jsp页面用于回显数据的标签 这些标签是由框架定义的 用来替代原生的标签 ui标签有
  • WPF编程,Live Charts使用说明(11)——基本折线图

    后台 using System using System Windows Controls using System Windows Media using LiveCharts using LiveCharts Wpf namespace
  • Spring整合Druid

    Druid是Java语言中最好的数据库连接池 Druid能够提供强大的监控和扩展功能 Druid是阿里巴巴开源平台上的一个项目 整个项目由数据库连接池 插件框架和SQL解析器组成 该项目主要是为了扩展JDBC的一些限制 可以让程序员实现一些
  • 厉害了|十分钟掌握python3语言特性

    看了王垠的 如何掌握所有程序语言 感触甚深 如果说程序语言有其通用规律的话 那就是语言特性 也就是这些语言的通用概念 这些概念的具体语法的形式可能都不一样 但是所内涵的功能是一致的 比如英语中的bird和汉语中鸟 其实指的都是同一种事物 关
  • python自动生成电子邮箱'@hotmail.com', '@msn.com', '@yahoo.com', '@gmail.com', '@aim.com', '@aol.com', '@mail

    def getAutoEmail self 自动生成电子邮箱 Fist email join random sample string ascii letters string digits 9 last emailList hotmail
  • 使用Docker及Docker-compose部署SpringBoot项目

    1 环境准备 Windows下安装Docker需要WSL2及Hyper v Windows家庭版没有 Linux下安装Docker 参考官方文档 Install Docker Engine Docker Documentation 根据自己
  • Poi实现Excel导出

    Poi实现Excel导出 Appache Poi提供了HSSFWorkbook操作2003版本的Excel文件 XSSFWorkbook操作2007版Excel文件 简单的具体实现在网上有很多案例可以参考学习 我就不写入门案例了 下面我会将
  • AutoML系列

    本文是对 Neural Architecture Search A Survey 的翻译 这篇Paper 很好的总结分析了 NAS 这一领域的研究进展 摘要 在过去几年中 深度学习在各种任务上 例如图像识别 语音识别和机器翻译 取得了显著进
  • 利用javascript的算术运算符获取一个数字的每位数字

  • element tree 树形控件

    组件 Element 地址 http element eleme io zh CN component tree Tree树形控件
  • 【VAR模型

    向量自回归 VAR 是一种随机过程模型 用于捕获多个时间序列之间的线性相互依赖性 VAR 模型通过允许多个进化变量来概括单变量自回归模型 AR 模型 VAR 中的所有变量都以相同的方式进入模型 每个变量都有一个方程式 根据其自身的滞后值 其
  • IBM MQ 故障诊断(一)

    说明 本文主要是针对运维人员的手册 前面部分主要是应用三板斧的方式 后面的步骤可能会发散和具体深入一些 不过也不是严格的划分 读者就当看一遍杂文的方式来看待此文吧 一 队列管理器的启停 QMGR的启停是故障诊断中遇到最多的需求之一 启动队列
  • 【C语言】可变参数列表

    文章目录 前言 一 可变参数列表是什么 二 怎么用可变参数列表 三 对于宏的深度剖析 隐式类型转换 对两个函数的重新认知 总结 前言 可变参数列表 使用起来像是数组 学习过函数栈帧的话可以发现实际上他也就是在栈区定义的一块空间当中连续访问
  • 无服务器编程语言,腾讯云之无服务器云函数运行golang程序-Go语言中文社区

    使用腾讯的 无服务器云函数启动了一个服务 用golang代码生成以太坊的私钥跟地址 genEthAddr png 无服务器云函数是什么 腾讯云的无服务器云函数 跟 aws lambda类似 把一段代码放到云函数服务器上 设定好访问路径 就可
  • 高等代数 多项式环(第7章)5* 结式与域

    一 结式 1 概念 2 结式与公共复根 1 多项式存在公共复根的判定 定理1 设 f x a
  • 数据结构——>单向环形链表

    单向环形链表 一 单向环形链表应用场景 二 单向环形链表介绍 三 单向环形链表代码实现 1 代码实现思路 2 代码实现 一 单向环形链表应用场景 提起单向环形链表 就不得不说约瑟夫问题 约瑟夫环 什么事约瑟夫问题呢 1 约瑟夫问题 有时也称
  • 机器学习:EM算法

    一 初识EM算法 EM算法也称期望最大化 Expectation Maximum 简称EM 算法 它是一个基础算法 是很多机器学习领域算法的基础 比如隐式马尔科夫算法 HMM 等等 EM算法是一种迭代优化策略 由于它的计算方法中每一次迭代都
  • FreeRTOS基础五:软件定时器

    软件定时器简介 软件定时器的作用 在指定的时间到来时执行指定的函数 或者以某个频率周期性地执行某个函数 被执行的函数叫做软件定时器回调函数 软件定时器由FreeRTOS内核实现 不需要硬件支持 软件定时器只有在软件定时器回调函数被调用时才需