详解FreeRTOS中的软件定时器

2023-10-29

软件定时器用于让某个任务定时执行,或者周期性执行。比如设定某个时间后执行某个函数,或者每隔一段时间执行某个函数。由软件定时器执行的函数称为软件定时器的回调函数

参考资料:

《Mastering the FreeRTOS™ Real Time Kernel》——Chapter 5 Software Timer Management

FreeRTOS全解析-6.软件定时器

目录

1.软件定时器的属性和状态

1.1软件定时器的周期

1.2软件定时器状态

1.3软件定时器的执行环境(上下文Context)

1.3.1RTOS守护任务(Daemon Task)(定时器服务Time Sevice)

1.3.2定时器命令队列

1.3.3守护任务调度

2.创建并启动软件定时器

3.定时器ID(Timer ID)

4.修改定时器的周期和重置软件定时器


1.软件定时器的属性和状态

在FreeRTOS中开启软件定时器功能:

1.构建FreeRTOS源文件FreeRTOS/ source /timers.c作为项目的一部分。

2. 在“FreeRTOSConfig.h”中将“configUSE_TIMERS”设置为1。

软件定时器回调函数

void ATimerCallback(TimerHandle_t xTimer)

软件定时器回调函数是在定时器服务中执行的,它们应该保持简短,并且不能进入阻塞态。定时器服务阻塞会影响内核,因此不能调用任何会导致阻塞的函数,比如vTaskDelay()。可以调用xQueueReceive()等函数,但前提是函数的xTicksToWait参数(指定函数的阻塞时间)设置为0。

1.1软件定时器的周期

一个软件定时器的“周期”是指软件定时器被启动和软件定时器的回调函数执行之间的时间。

单次定时器(一次性 one-shot)和周期性定时器(自动重载 Auto/-reload):

1.单次定时器只执行一次回调函数。可以手动重启,但不会自动重启。

2. 周期性定时器将在每次到期时重新启动自己,从而周期性地执行其回调函数。

1.2软件定时器状态

软件定时器可以处于以下两种状态之一:

1.休眠

休眠状态的软件定时器,是指一个软件定时器存在,且可以通过定时器句柄被引用,但是它并没有运行,所以它的回调函数不会执行。

2.运行

运行状态的软件定时器,根据设定的参数,到期运行一次或者周期性运行回调函数。

周期性定时器执行了回调函数后自动重新进入运行状态。

单次定时器执行过回调函数后就会进入休眠状态

1.3软件定时器的执行环境(上下文Context)

1.3.1RTOS守护任务(Daemon Task)(定时器服务Time Sevice)

所有软件定时器回调函数都在同一个RTOS守护(或'定时器服务')任务的上下文中执行。(在Linux上叫守护进程,FreeRTOS里称作任务)

守护任务是一个标准的FreeRTOS任务,在启动调度器时自动创建。它的优先级和堆栈大小分别由FreeRTOSConfig.h中的configTIMER_TASK_PRIORITY和configTIMER_TASK_STACK_DEPTH设置。

软件定时器回调函数不能调用会导致调用任务进入阻塞状态的FreeRTOS API函数,因为这样会导致守护任务进入阻塞态。

1.3.2定时器命令队列

一个任务调用软件定时器的API函数向守护任务发送命令,这个命令会被存在一个队列里,这个队列就叫定时器命令队列

命令示例“启动计时器”、“停止计时器”和“重置计时器”。

定时器命令队列是一个标准的FreeRTOS队列,在启动调度器时自动创建。定时器命令队列的长度由FreeRTOSConfig.h中的configTIMER_QUEUE_LENGTH设置。

1.3.3守护任务调度

守护任务像任何其他FreeRTOS任务一样调度。它只处理命令,或者当它是能够运行的最高优先级任务时,执行定时器回调函数。

如图,Task1运行在Task1中调用定时器API函数向守护任务发送启动定时器命令。因为守护任务优先级没有Task1高,所以守护任务不会立即处理命令,而是等到t4时,Task1进入阻塞态,守护任务才开始处理命令

假如守护任务优先级高的话,一旦发送命令,就切换到守护任务了,所以定时器也就立即启动了。

注意了,定时器的超时时间不是从守护任务接收到命令开始算的,而是从发送时间开始算的。

实际上发送的命令里包含了一个时间戳。时间戳记录了发送时间。例如,如果发送一个启动一个周期为10ms的定时器的命令,时间戳可以保证是发送后的10ms而不是守护任务处理命令后的10ms。

2.创建并启动软件定时器

xTimerCreate()用于创建一个软件计时器,并返回一个TimerHandle_t(软件定时器句柄)。软件定时器创建的时候是休眠状态,并没有立即启动。

软件计时器可以在调度程序运行之前创建,也可以在启动调度程序之后从任务中创建。

TimerHandle_t xTimerCreate( const char * const pcTimerName,                            TickType_t xTimerPeriodInTicks,                            UBaseType_t uxAutoReload,                            void * pvTimerID,                            TimerCallbackFunction_t pxCallbackFunction );
参数 作用
pcTimerName 软件定时器的名字,FreeRTOS不会用到,便于自己记忆就行
xTimerPeriodInTicks 以tick为单位指定的计时器周期。pdMS_TO_TICKS()宏可用于将以毫秒为单位指定的时间转换为以tick为单位指定的时间。
uxAutoReload 设置为pdTRUE创建周期(自动重载)计时器。设置为pdFALSE以创建单次(一次性)计时器。
pvTimerID 每个软件定时器都有一个ID值。ID是一个空指针,应用程序编写人员可以将其用于任何目的。当同一个回调函数被多个软件计时器使用时,ID特别有用,因为它可以用于提供计时器特定的存储。后面演示。
pxCallbackFunction 回调函数指针
返回值 如果返回NULL,则不能创建软件计时器,因为没有足够的堆内存。返回非NULL值表示软件计时器已经创建成功。返回值是已创建计时器的句柄。

xTimerStart()用于启动处于休眠状态的软件定时器,或重置(重新启动)处于运行状态的软件定时器。可以在启动调度器之前调用xTimerStart(),但是软件定时器在启动调度器之前不会实际启动。

BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );

这个函数的底层其实就是上期讲的队列发送FreeRTOS全解析-5.队列(Queue)

参数的意思也就显而易见了。

参数 作用
xTimer 软件定时器句柄。就是创建定时器的返回值。
xTicksToWait

指定如果队列已满,则调用任务应保持在Blocked状态等待的最大时间。

如果xTicksToWait为零且定时器命令队列已满,xTimerStart()将立即返回。

那么将xTicksToWait设置为portMAX_DELAY将导致调用任务无限期地保持在Blocked状态(没有超时),以等待timer命令队列中的可用空间。

和队列一样要使用portMAX_DELAY宏就要先FreeRTOSConfig.ht中的INCLUDE_vTaskSuspend设置为1

如果在启动调度器之前调用xTimerStart(),那么xTicksToWait的值将被忽略,xTimerStart()的行为就像xTicksToWait已被设置为零一样。

返回值

1.pdPASS命令成功发送。

2.pdFALSE队列已满无法写入。

xTimerStop()用于停止处于运行状态的软件定时器。停止软件计时器与将计时器转换为休眠状态相同。

例子如下:

程序创建了两个定时器,一个是单次的,一个是周期性的,回调函数里打印时间。

#define mainONE_SHOT_TIMER_PERIOD pdMS_TO_TICKS( 3333 )#define mainAUTO_RELOAD_TIMER_PERIOD pdMS_TO_TICKS( 500 )static void prvOneShotTimerCallback( TimerHandle_t xTimer ){  TickType_t xTimeNow;  xTimeNow = xTaskGetTickCount();  vPrintStringAndNumber( "One-shot timer callback executing", xTimeNow );  ulCallCount++;}static void prvAutoReloadTimerCallback( TimerHandle_t xTimer )  TickType_t xTimeNow;  xTimeNow = uxTaskGetTickCount();  vPrintStringAndNumber( "Auto-reload timer callback executing", xTimeNow );  ulCallCount++;}int main( void ){  TimerHandle_t xAutoReloadTimer, xOneShotTimer;  BaseType_t xTimer1Started, xTimer2Started;  xOneShotTimer = xTimerCreate("OneShot",mainONE_SHOT_TIMER_PERIOD,pdFALSE,0,prvOneShotTimerCallback );  xAutoReloadTimer = xTimerCreate("AutoReload",mainAUTO_RELOAD_TIMER_PERIOD,pdTRUE,0,prvAutoReloadTimerCallback );  if( ( xOneShotTimer != NULL ) && ( xAutoReloadTimer != NULL ) )  {    xTimer1Started = xTimerStart( xOneShotTimer, 0 );    xTimer2Started = xTimerStart( xAutoReloadTimer, 0 );      if( ( xTimer1Started == pdPASS ) && ( xTimer2Started == pdPASS ) )      {        vTaskStartScheduler();      }    }  for( ;; );}

效果:

3.定时器ID(Timer ID)

前文讲了每个软件定时器都有一个ID值。ID是一个空指针,应用程序编写人员可以将其用于任何目的。因为ID存储在void指针(void *)中,因此可以直接存储整数值,指向任何其他对象,或用作函数指针。

在 用函数xTimerCreate创建软件计时器时,会为ID分配一个初始值。在此之后,可以使用vTimerSetTimerlD() API函数更新ID,并使用pvTimerGetTimerID()来查询ID。

与其他软件定时器API函数不同,vTimerSetTimerlD()和pvTimerGetTimerlD()直接访问软件定时器——它们不向定时器命令队列发送命令。

void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID );void *pvTimerGetTimerID( TimerHandle_t xTimer );

例子:

static void prvTimerCallback( TimerHandle_t xTimer ){  TickType_t xTimeNow;  uint32_t ulExecutionCount;  ulExecutionCount = ( uint32_t ) pvTimerGetTimerID( xTimer );  ulExecutionCount++;  vTimerSetTimerID( xTimer, ( void * ) ulExecutionCount );  xTimeNow = xTaskGetTickCount();  if( xTimer == xOneShotTimer ) {    vPrintStringAndNumber( "One-shot timer callback executing", xTimeNow );  } else {    vPrintStringAndNumber( "Auto-reload timer callback executing", xTimeNow );    if( ulExecutionCount == 5 ) {      xTimerStop( xTimer, 0 );    }  }}

把定时器回调函数改成如上,把ID当做回调函数运行次数的计数,每次运行都取出ID并且加一,然后更新ID,当等于五时停止定时器,效果如下:

4.修改定时器的周期和重置软件定时器

软件定时器的周期可以使用xTimerChangePeriod()函数来改变。

如果使用xTimerChangePeriod()来更改已经在运行的计时器的周期,则计时器将使用新的周期值重新计算到期时间。重新计算的到期时间相对于调用xTimerChangePeriod()的时间,而不是相对于最初启动计时器的时间。

如果使用xTimerChangePeriod()来改变处于休眠状态(未运行的计时器)的周期,那么计时器将计算到期时间,并转换到运行状态(计时器将开始运行)。

BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,                              TickType_t xNewTimerPeriodInTicks,                              TickType_t xTicksToWait );

xTimerReset()用于重置定时器,重置软件定时器意味着重新启动定时器;计时器的到期时间被重新计算为相对于计时器重置的时间,而不是计时器最初启动的时间。

BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );

往期精彩:

嵌入式C语言几个重点(const、static、voliatile、位运算)

交叉编译环境、bootloader、kernel、根文件系统是什么?有什么联系?

嵌入式Linux驱动学习-7.什么是设备树?

从Linux内核中学习高级C语言宏技巧

嵌入式Linux驱动学习-5.驱动的分层分离思想

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

详解FreeRTOS中的软件定时器 的相关文章

  • 人脸检测——UnitBox

    本次介绍一篇来自旷视科技的人脸检测文章 2016 ACM MM UnitBox An Advanced Object Detection Network 代码应该是不会放出来了 但好在实现比较简单 插播一句 论文里面说速度可以达到12fps
  • c语言include使用

    gcc编译过程中查看详细信息会看到以下信息 第一个目录是使用 I加入的 也就是include lt gt 也会优先搜索指定目录 如果此时你的文件和系统文件重名就会导致真正的系统文件不能完成加载 include search starts h
  • cmake 优化

    export CXX FLAGS O3 cmake DCMAKE BUILD TYPE Release
  • android log丢失(一)使用logd丢失log原理

    之前我们分析过关于Android log机制 在这里我们再详细说下 log丢失的原理 一 统计log logd监听了logdw的socket来保存从log打印函数通过logdw socket传过来的log 最后会调用LogBuffer lo

随机推荐

  • C# 正则表达式30分钟入门教程到放弃

    最近用到了C 的Regex类 所以又复习了一遍C 的正则表达式 发现还是和其他语言或者说以前的学习有很多不同以及提高之处 所以写这篇文章 用来给后来人一个系统性的教程或者引导 目录 一 正则表达式到底是什么东西 二 正则入门 三 元字符 四
  • Hooks的常用Api

    Ref Hook Ref Hook可以在函数组件中存储 查找组件内的标签或其他数据 语法 const refContainer useRef 获取值 refContainer current value 作用 保存标签对象 功能与React
  • 必学的web前端三大核心:JavaScript css3 HTML5

    对于前端的学习和提高 我的基本思路是这样的 首先 前端的三个最基本的东西 HTML5 CSS3 和 JavaScript ES6 是必须要学好的 这其中有很多很多的技术 比如 CSS3 引申出来的 Canvas 位图 SVG 矢量图 和 W
  • [Ctf show web]萌新计划9

    system exec highlight 等于or 题目要求我们利用 system exec highlight才能执行eval preg match a b 函数可以返回 a匹配次数 它的值将是 0 次 不匹配 或 1 次 因为 pre
  • ※机器学习函数调用/绘图/读取文件/分析常用代码总结

    机器学习函数调用代码 决策树 from sklearn tree import DecisionTreeRegressor 随机森林 from sklearn ensemble import RandomForestClassifier 线
  • [数据可视化]使用matplotlib对CSV文件数据进行绘图

    数据可视化 使用matplotlib对CSV文件数据进行绘图 使用CSV格式的锡特卡天气数据 绘制2018年1月1日的天气数据 先放成果 代码部分包括以下内容 分析CSV文件头 打印文件头及其位置 提取并读取数据 绘制温度图表 模块date
  • redis 配置文件详解

    Redis 配置文件 当配置中需要配置内存大小时 可以使用 1k 5GB 4M 等类似的格式 其转换方式如下 不区分大小写 1k gt 1000 bytes 1kb gt 1024 bytes 1m gt 1000000 bytes 1mb
  • Qt篇——QTcpSocket设置连接超时时间

    QTcpSocket默认是没有函数接口设置超时时间的 使用以下方法可以给socket设置超时时间 QNetworkConfigurationManager manager QNetworkConfiguration config manag
  • 机器学习单变量线性回归

    1 模型概述 给出一些带有标签的数据 即带有 正确答案 的数据 用y ax b 的形式去拟合数据 线性 单变量 大致过程如下 给出训练集 带有标签的数据 通过学习算法选择不错的参数 theta0 theta1 得到假设函数h 从x到y 的映
  • 数据库之分库分表-垂直?水平?

    一 数据库瓶颈 不管是IO瓶颈 还是CPU瓶颈 最终都会导致数据库的活跃连接数增加 进而逼近甚至达到数据库可承载活跃连接数的阈值 在业务Service来看就是 可用数据库连接少甚至无连接可用 接下来就可以想象了吧 并发量 吞吐量 崩溃 1
  • 小智ai:ChatGPT3主要功能这些功能可以说明什么问题?

    ChatGPT3在自然语言处理领域中的强大能力 尤其是在文本生成和语言模型训练方面 它可以模拟人类的语言能力 理解人类的意图并生成自然的回复和文本 ChatGPT3的语言翻译和语音转文本功能也使得跨语言交流和语音交互变得更加容易 它可以自动
  • QQ取色器功能

    一 使用方法 QQ截图功能具有取色器功能 二 截取颜色三原色 1 截图 Ctral ALT A 2 找到取色目标 3 按 C 键 4 CTRL V 5 得到颜色三原色 255 255 255 三 截取十六进制颜色 1 截图 Ctral AL
  • 第三方yum源仓库搭建

    作用 第三方软件是yum仓库里没有的软件 如果从网上下载得到了软件 用yum还是不能安装 因为会有依赖性 解决依赖性非常麻烦 但是如果搭建了第三方软件仓库 yum就会自己解决这个问题 下载 浏览器搜索 sourceforge 源码安装包 r
  • 爬虫 第五讲 多线程爬虫

    文章目录 爬虫 第五讲 多线程爬虫 一 多线程 1 多线程基本介绍 2 主线程和子线程的执行关系 3 查看线程数量 4 验证子线程的执行与创建 5 继承Thread类创建线程 6 线程间的通信 多线程共享全局变量 7 线程间的资源竞争 二
  • 什么是Service, 以及Service 模板

    Service本质就是一个驻留Process 驻留Prcess至少有一个驻留线程 这个线程处在waiting的状态 相对于Runable 控制流停在某个点上 等待外部事件驱动 或者是自己的timer驱动 Windows Service 是一
  • 十行代码,我用Python做一个迷你版的美图秀秀!

    美图秀秀相信大家都不陌生 大家只要操作美图秀秀 就可以P掉图片中脸上的一些瑕疵 让人变得更加的美丽 今天小编就带领大家来借助Python和Flask来实现一个美图秀秀的网页设计 大家只需要通过网页上传需要美颜的图片 然后就可以从网页下载美颜
  • linux-shell是什么

    由于安全 复杂 繁琐等原因 用户不能直接解除内核 需要一个程序来接收用户的操作 进行处理后再传给内核 这一过程不仅做到了简化 同时也保护了内核 说白了Shell就是用来解释用户命令的 linuxshell可以看成一种脚本语言 高级语言能做到
  • 华为OD机试真题-匿名信C++实现【2023.Q1】

    题目内容 电视剧 分界线 里面有一个片段 男主为了向警察透露案件细节 且不暴露自己 于是将报刊上的字减下来 剪拼成匿名信 现在有一名举报人 希望借鉴这种手段 使用英文报刊完成举报操作 但为了增加文章的混淆度 只需满足每个单词中字母数量一致即
  • 取消springsecurity默认的登录验证

    取消springsecurity默认的登录验证 问题描述 解决方法一 方法二 问题描述 springboot 2 x 访问swagger ui html时 会自动跳转到springsecurity的login页 自定义过滤路径的拦截器无效
  • 详解FreeRTOS中的软件定时器

    软件定时器用于让某个任务定时执行 或者周期性执行 比如设定某个时间后执行某个函数 或者每隔一段时间执行某个函数 由软件定时器执行的函数称为软件定时器的回调函数 参考资料 Mastering the FreeRTOS Real Time Ke