1. 定义
FreeRTOS中的定时器与MCU定时器一样,提供定时功能。当设置的时间到达以后,就会调用回调函数进行执行。
2.软件定时器的分类
软件定时器分两种:单次定时器和周期定时器,单次定时器指的是定时器回调函数就执行一 次,比如定时 1s,当定时时间到了以后就会执行一次回调函数,然后定时器就会停止运行。对于单次定时器我们可以再次手动重新启动(调用相应的 API 函数即可),但是单次定时器不能自动重启。相反的,周期定时器一旦启动以后就会在执行完回调函数以后自动的重新启动,这样回调函数就会周期性的执行,类似于重装载。
2. 软件定时器核心原理
我们知道,设置好定时时间后。当时间到达即可调用定时器回调函数,这是两个动作:一个是判断时间是否到达,第二是调用定时器回调函数。那么FreeRTOS的内核如何实现这两个动作呢?
当创建好定时器后,需要调用API启动定时器(如xTimerStart)。此时,该API会发送启动命令到定时队列中,定时器服务任务接收到队列消息(启动命令)后(因此该任务优先级需要设置的较高),判断定时时间是否到达,到达以后调用回调函数执行。
这个定时器服务任务是一个内核任务,当调用vTaskStartScheduler()开启多任务调度时,只要使能了与软件定时器有关的宏,就会在vTaskStartScheduler()函数里边创建定时器服务任务(prvTimerTask),相关代码如下所示:
接下来,看该prvTimeTask任务函数源码,如下图所示:
其中,546-556行源码是设置的钩子函数,562行源码是得到下一个定时器的到期时间(因为针对于到期定时器,专门设置了一个定时器链表来链接各个定时器,到期时间越短就越靠近表头,该函数就是获得到期列表的列表项值,此时列表项值存放的就是到期时间),
566行源码是核心处理函数,其功能是先检查时钟节拍计数是否溢出,如果没有溢出的情况下:发现有定时器到期时,将相关定时器从active timer列表上移除,并调用定时器回调函数。如果该定时器为周期定时器,则重新计算下次到期时间并重新插回链表。
如果在没有溢出的情况下,此时没有定时器到期,那么就进行阻塞等待队列命令或者是一个到期定时器时间;如果存在时钟计数溢出,则不做处理。相关源码如下图所示:
总结:定时器的到期和调用回调函数是通过定时器服务任务prvTimerTask来完成的,无需用户直接访问。
3. 使用软件定时器
3.1 内核裁剪相关配置
想要使用软件定时器,需要使能对应的宏,以及划分好prvTimerTask任务的优先级及堆栈大小,具体配置示例如下图所示:
3.2 定时器创建函数
此处讲解动态创建定时器函数xTimerCreate,有五个参数:
pcTimerName: 软件定时器名字,名字是一串字符串,用于调试使用。
xTimerPeriodInTicks : 软件定时器的定时器周期,单位是时钟节拍数。
uxAutoReload: 设置定时器模式,单次定时器还是周期定时器当此参数为 pdTRUE 的时候表示创建的是周期定时器。如果为 pdFALSE 的话表示创建的 是单次定时器。
pvTimerID: 定时器 ID 号,一般情况下每个定时器都有一个回调函数,当定时器定 时周期到了以后就会执行这个回调函数。但是 FreeRTOS 也支持多个 定时器共用同一个回调函数,在回调函数中根据定时器的 ID 号来处 理不同的定时器。
pxCallbackFunction: 定时器回调函数,当定时器定时周期到了以后就会调用这个函数。
函数的返回值是软件定时器句柄。
3.3 定时器开启函数
在任务中使用的是xTimerStart()函数,其有两个参数:
xTimer: 要开启的软件定时器的句柄。
xTicksToWait: 设置阻塞时间,调用函数 xTimerStart()开启软件定时器其实就是向定时器命令 队列发送一条 tmrCOMMAND_START 命令,既然是向队列发送消息,那肯 定会涉及到入队阻塞时间的设置。
在中断函数中使用的是xTimerStartFromISR(),参数为:
xTimer: 要开启的软件定时器的句柄。
pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来 保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务 函数之前一定要进行一次任务切换。
在定时器运行过程中,调用xTimerReset函数与开始函数是一样的,此处不再对其进行解释。
3.4 定时器结束函数
在任务中使用的是xTimeStop()函数,参数如下:
xTimer: 要停止的软件定时器的句柄。
xTicksToWait: 设置阻塞时间,调用函数 xTimerStop()停止软件定时器其实就是向定时器命令 队列发送一条 tmrCOMMAND_STOP 命令,既然是向队列发送消息,那肯定 会涉及到入队阻塞时间的设置。
在中断函数中使用的是xTimerStopFromISR(),与定时器开始函数中断版本类似,此处不再具体说明。
4. 参考文献
[1] FreeRTOS开发手册V1.1
ps:如有需要开发手册,请联系1840813505@qq.com,备注csdn。