定时器Timer
软件定时器是基于系统时钟中断且由软件来模拟的定时器,当经过设定的Tick 时钟计数值后会触发用户定义的回调函数。软件定时器不占用单片机宝贵的硬件资源和CPU资源 。FreeRTOS提供了完善的软件定时器的支持,为了启用软件定时器,需要在头文件FreeRTOSConfig.h中设置configUSE_TIMERS的值为1.
软件定时器需要定时或者延时控制的函数称为回调函数。 函数的原型如下:
void ATimerCallback( TimerHandle_t xTimer );
函数的返回值是空类型,xTimer的参数是软件定时器的句柄。回调函数的注意事项是代码应该尽可能地简短紧凑,并且避免调用FreeRTOS的API函数防止进入阻塞状态。
软件定时器的类型
软件定时器在FreeRTOS中分为两个类型
-
一次性定时器(One-shot timer)
-
自动重载定时器(Auto-reload timer)
一次性定时器启动后只会执行一次回调函数;自动重载定时器会周期性地执行回调函数。
软件定时器的状态
有以下两种
- 静止装态(Dormant)
- 运行状态(Running)
处于静止装态的定时器不会执行回调函数,可以通过调用定时器的具柄启用;处于运行状态的定时器会在设定的时间间隔(相对于定时器进入运行状态后)到达后调用回调函数。一次性定时器会在执行回调函数后进入静止状态,而自动重载定时器会在执行回调函数后重新进入运行状态。通过调用软件定时器相关的系统API函数可以在两种状态之间进行切换,其中两种定时器的状态转换图如下:
软件定时器的实质
软件定时器是由一个系统内核调度器自动生成的时间服务任务来管理。这个时间服务任务的优先级和栈大小由头文件FreeRTOSConfig.h中configTIMER_TASK_PRIORITY和configTIMER_TASK_STACK_DEPTH属性相应设定。定时器的回调函数调用FreeRTOS的API函数的话会导致时间服务任务管理进入阻塞状态,这种情况是要避免的。
一些FreeRTOS的API函数会使回调函数进入阻塞状态转而执行其他任务导致定时器的实时性不确定,容易产生意外错误或者超时
针对软件定时器的命令如启动定时器,停止定时器和重置定时器等会通过命令队列发送到时间服务任务。队列长度由头文件FreeRTOSConfig.h中configTIMER_QUEUE_LENGTH 值设定。
守护任务
FreeRTOS 中,不在 Tick 中断中执行定时器函数,而是在 RTOS Damemon Task 里执行。
当 FreeRTOS 的配置项 configUSE_TIMERS 被设置为 1 时 ,在启动调度器时,会自动创建 RTOS Damemon Task。
守护任务的调度
当守护任务是当前优先级最高的就绪态任务时,它就可以运行。 它的工作有两类:
- 处理命令:从命令队列里取出命令、处理
- 执行定时器的回调函数
定时器的回调函数的原型如下:
void ATimerCallback( TimerHandle_t xTimer );
定时器的回调函数是在守护任务中被调用的,守护任务不是专为某个定时器服务的,它还要处理其他定时器。
- 回调函数要尽快实行,不能进入阻塞状态
- 不要调用会导致阻塞的 API 函数,比如 vTaskDelay()
- 可以调用 xQueueReceive()之类的函数,但是超时时间要设为 0:即刻返回,不可阻塞
定时器相关API函数
创建定时器
TimerHandle_t xTimerCreate( const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
UBaseType_t uxAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction )
xTimerCreate()函数用于创建定时器
函数参数含义如下
-
pcTimerName 定时器的名称,用于调试用
-
xTimerPeriodInTicks 定时器的周期,dMS_TO_TICKS() 可把时间转成节拍数
-
uxAutoReload 设置为pdTRUE为自动重载定时器,设置为pdFALSE为一次性定时器
-
pvTimerID 定时器的ID,可以用于各种目的
-
pxCallbackFunction 定时器执行的回调函数
-
返回值 返回值为NULL表示创建失败,非NULL表示创建成功
删除定时器
/* 删除定时器
* xTimer: 要删除哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL 表示"删除命令"在 xTicksToWait 个 Tick 内无法写入队列
* pdPASS 表示成功
*/
BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );
启动定时器
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait )
xTimerStart()函数用于启动定时器
函数参数含义如下
-
xTimer 要执行定时器的具柄
-
xTicksToWait 如果命令队列为满时函数进入阻塞状态等待命令队列有空的时间
-
返回值 返回值为pdPASS表示启动定时器成功;返回值为pdFALSE表示失败
设置定时器pvTimerID属性
void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID )
定时器 ID 号,一般情况下每个定时器都有一个回调函数,当定时器定时周期到了以后就会执行这个回调函数。但是 FreeRTOS 也支持多个定时器共用同一个回调函数,在回调函数中根据定时器的 ID 号来处理不同的定时器。
函数参数含义如下
-
xTimer 要执行定时器的具柄
-
pvNewID 设定的pvTimerID的值
获取定时器的pvTimerID 属性
void *pvTimerGetTimerID( TimerHandle_t xTimer );
pvTimerGetTimerID()函数用于获取定时器的pvTimerID 属性
函数参数含义如下
-
xTimer 要执行定时器的具柄
-
返回值 pvTimerID的值
改变定时器的运行周期
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
TickType_t xNewTimerPeriodInTicks,
TickType_t xTicksToWait );
xTimerChangePeriod()函数用于改变定时器的运行周期
函数参数含义如下
-
xTimer 要执行定时器的具柄
-
xNewTimerPeriodInTicks 新的定时器的周期,dMS_TO_TICKS() 可把时间转成节拍数
-
xTicksToWait 如果命令队列为满时函数进入阻塞状态等待命令队列有空的时间
-
返回值 返回值为pdPASS表示成功;返回值为pdFALSE表示失败
重置定时器
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
xTimerReset()函数用于重置定时器,定时器会重新运行并以此计算新的时间间隔
函数参数含义如下
-
xTimer 要执行定时器的具柄
-
xTicksToWait 如果命令队列为满时函数进入阻塞状态等待命令队列有空的时间
-
返回值 返回值为pdPASS表示成功;返回值为pdFALSE表示失败
使用定时器需要配置
/* 1. 工程中 */
添加 timer.c
/* 2. 配置文件 FreeRTOSConfig.h 中 */
##define configUSE_TIMERS 1 /* 使能定时器 */
##define configTIMER_TASK_PRIORITY 31 /* 守护任务的优先级, 尽可能高一些 */
##define configTIMER_QUEUE_LENGTH 5 /* 命令队列长度 */
##define configTIMER_TASK_STACK_DEPTH 32 /* 守护任务的栈大小 */
/* 3. 源码中 */
##include "timers.h"