我们介绍了队列管理的相关知识,在这一章中我们将探讨FreeRTOS的另一个特性--软件定时器(Software 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函数可以在两种状态之间进行切换,其中两种定时器的状态转换图如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAnYldHL0FWby9mZvwFN4ETMfdHLkVGepZ2XtxSZ6l2clJ3LcV2Zh1Wa9M3clN2byBXLzN3btgHL9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iM5cjMyYWZxQDOkNTMhZWNzYzXxQTOxgTMxMzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
自动重载定时器的状态转换图
一次性定时器的状态转换图
软件定时器的实质
软件定时器由一个系统内核调度器自动生成的时间服务任务管理。这个时间服务任务的优先级和栈大小由头文件FreeRTOSConfig.h中configTIMER_TASK_PRIORITY和configTIMER_TASK_STACK_DEPTH属性相应设定。定时器的回调函数调用FreeRTOS的API函数的话会导致时间服务任务管理进入阻塞状态,这种情况是要避免的。
针对软件定时器的命令如启动定时器,停止定时器和重置定时器等会通过命令队列发送到时间服务任务。队列长度由头文件FreeRTOSConfig.h中configTIMER_QUEUE_LENGTH 值设定。
定时器相关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表示创建成功
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait )
xTimerStart()函数用于启动定时器
函数参数含义如下
- xTimer 要执行定时器的具柄
- xTicksToWait 如果命令队列为满时函数进入阻塞状态等待命令队列有空的时间
- 返回值 返回值为pdPASS表示启动定时器成功;返回值为pdFALSE表示失败
void vTimerSetTimerID( const TimerHandle_t xTimer, void *pvNewID )
vTimerSetTimerID()用于设置定时器的pvTimerID 属性
函数参数含义如下
- xTimer 要执行定时器的具柄
- pvNewID 设定的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表示失败