一、简介
软件定时器是指具有定时功能的软件,FreeRTOS 提供的软件定时器允许在创建前设置一个
软件定时器定时超时时间,在软件定时器成功创建并启动后,软件定时器开始定时,当软件定
时器的定时时间达到或超过先前设置好的软件定时器定时器超时时间时,软件定时器就处于超
时状态,此时软件定时器就会调用相应的回调函数,一般这个回调函数的处理的事务就是需要
周期处理的事务。
FreeRTOS 提供的软件定时器还能够根据需要设置成单次定时器和周期定时器。当单次定时
器定时超时后,不会自动启动下一个周期的定时,而周期定时器在定时超时后,会自动地启动
下一个周期的定时。
FreeRTOS 提供的软件定时器功能,属于 FreeRTOS 的中可裁剪可配置的功能,如果要使能
软件定时器功能,那需要在 FreeRTOSConfig.h 文件中将 configUSE_TIMERS 配置项配置成 1。
要注意的是,软件定时器的超时回调函数是由软件定时器服务任务调用的,软件定定时器
的超时回调函数本身不是任务,因此不能在该回调函数中使用可能会导致任务阻塞的 API 函数,例如 vTaskDelay()、vTaskDelayUntil()和一些会到时任务阻塞的等到事件函数,这些函数将会导致软件定时器服务任务阻塞,这是不可以出现的。
1.软件定时器服务任务简介
使能了软件定时器功能后,在调用函数 vTaskStartScheduler()开启任务调度器的时候,会创
建一个用于管理软件定时器的任务,这个任务就叫做软件定时器服务任务。软件定时器服务任
务,主要负责软件定时器超时的逻辑判断、调用超时软件定时器的超时回调函数以及处理软件
定时器命令队列。
2.软件定时器命令队列
FreeRTOS 提供了许多软件定时器相关的 API 函数,这些 API 函数,大部分都是往定时器
的队列中写入消息(发送命令),这个队列叫做软件定时器命令队列,是提供给 FreeRTOS 中的软件定时器使用的,用户是不能直接访问的。软件定时器命令队列的操作过程如下图所示:
上图中,左侧的代码为应用程序中用户任务的代码,而右侧的代码为软件定时器服务任务
的代码。当用户任务需要操作软件定时器时,就需要调用软件定时器相关的 API 函数,例如图
中调用了函数 vTaskStart()启动软件定时器的定时,而函数 vTaskStart()实际上会往软件定时器命令队列写入一条消息(发送命令),这条消息就包含了待操作的定时器对象以及操作的命令(启动软件定时器),软件定时器服务任务就会去读取软件定时器命令队列中的消息(接收命令),并处理这些消息(处理命令)。可以看出,用户任务并不会直接操作软件定时器对象,而是发送命令给软件定时器服务任务,软件定时器服务任务接收到命令后,根据命令内容去操作软件定时器。
3.软件定时器的状态
软件定时器可以处于一下两种状态中一种:
1. 休眠态
休眠态软件定时器可以通过其句柄被引用,但是因为没有运行,所以其定时超时回调函数
不会被执行。
2. 运行态
处于运行态或在上次定时超时后再次定时超时的软件定时器,会执行其定时超时回调函数。
4 .单次定时器和周期定时器
FreeRTOS 提供了两种软件定时器,如下:
1. 单次定时器
单次定时器的一旦定时超时,只会执行一次其软件定时器超时回调函数,超时后可以被手
动重新开启,但单次定时器不会自动重新开启定时。
2. 周期定时器
周期定时器的一旦被开启,会在每次超时时,自动地重新启动定时器,从而周期地执行其
软件定时器回调函数。
单次定时器和周期定时器之间的差异如下图所示:
上图展示了单次定时器和周期定时器之间的差异,图中的垂直虚线的间隔时间为一个单位
时间,可以理解为一个系统时钟节拍。其中 Timer1 为周期定时器,定时超时时间为 2 个单位时间,Timer2 为单次定时器,定时超时时间为 1 个单位时间。可以看到,Timer1 在开启后,一直以 2 个时间单位的时间间隔重复执行,为 Timer2 则在第一个超时后就不在执行了。
5 .软件定时器的状态转换图
单次定时器的状态转化图,如下图所示:
周期定时器的状态转换图,如下图所示:
6.复位软件定时器
除了开启和停止软件定时器的定时,还可以对软件定时器进行复位。复位软件定时器会使
软件定时器的重新开启定时,复位后的软件定时器以复位时的时刻作为开启时刻重新定时,软
件定时器的复位示意图如下图所示:
上图展示了软件定时器的复位过程,图中在 t0 时刻创建并启动了一个超时时间为 5 个单位
时间的软件定时器,接着在 t3 时刻对软件定时器进行了复位,复位后软件定时器的超时时刻以
复位时刻为开启时刻重新计算,在 t7 时刻又再次对软件定时器进行了复位,最终计算出软件定
时器的超时时刻为最后一次复位的时刻(t7)加上软件定时器的超时时间(5 个单位时间),于是该软件定时器在 t12 时刻超时,并执行其超时回调函数。
7.软件定时器优缺点
8.软件定时器的特点
二、软件定时器相关配置
FreeRTOSConfig.h 文件中软件定时器相关的配置项说明如下:
1. configUSE_TIMERS
此宏用于使能软件定时器功能,如果要使用软件定时器功能,则需要将该宏定义定义为 1。
开启软件定时器功能后,系统会系统创建软件定时器服务任务。
2. configTIMER_TASK_PRIORITY
此宏用于配置软件定时器服务任务的任务优先级,当使能了软件定时器功能后,需要配置
该宏定义,此宏定义可以配置为 0~(configMAX_PRIORITY-1)的任意值。
3. configTIMER_QUEUE_LENGTH
此宏用于配置软件定时器命令队列的队列长度,当使能了软件定时器功能后,需要配置该
宏定义,若要正常使用软件定时器功能,此宏定义需定义成一个大于 0 的值。
4. configTIMER_TASK_STACK_DEPTH
此宏用于配置软件定时器服务任务的栈大小,当使能了软件定时器功能后,需要配置该宏
定义,由于所有软件定时器的定时器超时回调函数都是由软件定时器服务任务调用的,因此这
些软件定时器超时回调函数运行时使用的都是软件定时器服务任务的栈。
三、相关的API函数
结构体成员
1. 创建软件定时器
FreeRTOS 提供了两种创建软件定时器的方式,分别为动态方式创建软件定时器和静态方
式创建软件定时器,两者的区别在于静态方式创建软件定时器时,需要用户提供创建软件定时
器所需的内存空间,而使用动态方式创建软件定时器时,FreeRTOS 会自动从 FreeRTOS 管理的堆中分配创建软件定时器所需的内存空间。
动态方式创建软件定时器 API 函数的函数原型如下所示:
TimerHandle_t xTimerCreate(
const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction);
静态方式创建软件定时器 API 函数的函数原型如下所示:
TimerHandle_t xTimerCreateStatic(
const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t * pxTimerBuffer);
2. 开启软件定时器定时
FreeRTOS 提供了两个用于开启软件定时器定时的 API 函数,这个两个函数分别用于在任
务和在中断中开启软件定时器定时。
在任务中开启软件定时器定时 API 函数的函数原型如下所示:
BaseType_t xTimerStart( TimerHandle_t xTimer,
const TickType_t xTicksToWait);
在中断中开启软件定时器定时 API 函数的函数原型如下所示:
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,
BaseType_t * const pxHigherPriorityTaskWoken);
3. 停止软件定时器定时
FreeRTOS 提供了两个用于停止软件定时器定时的 API 函数,这个两个函数分别用于在任
务和在中断中停止软件定时器定时。
在任务中停止软件定时器定时 API 函数的函数原型如下所示:
BaseType_t xTimerStop( TimerHandle_t xTimer,
const TickType_t xTicksToWait);
在中断中停止软件定时器定时 API 函数的函数原型如下所示:
BaseType_t xTimerStopFromISR( TimerHandle_t xTimer,
BaseType_t * const pxHigherPriorityTaskWoken);
4. 复位软件定时器定时
FreeRTOS 提供了两个用于复位软件定时器定时的 API 函数,这个两个函数分别用于在任
务和在中断中复位软件定时器定时。
在任务中复位软件定时器定时 API 函数的函数原型如下所示:
BaseType_t xTimerReset( TimerHandle_t xTimer,
const TickType_t xTicksToWait);
在中断中复位软件定时器定时 API 函数的函数原型如下所示:
BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,
BaseType_t * const pxHigherPriorityTaskWoken);
5. 更改软件定时器的定时超时时间
FreeRTOS 提供了两个分别用于任务和中断的更改软件定时器的定时超时时间的 API 函数。
在任务中更改软件定时器的定时超时时间 API 函数的函数原型如下所示:
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
const TickType_t xNewPeriod,
const TickType_t xTicksToWait);
在中断中更改软件定时器的定时超时时间 API 函数的函数原型如下所示:
BaseType_t xTimerChangePeriodFromISR(
TimerHandle_t xTimer,
const TickType_t xNewPeriod,
BaseType_t * const pxHigherPriorityTaskWoken);
6. 删除软件定时器
FreeRTOS 提供了用于删除软件定时器的 API 函数,函数原型如下所示:
BaseType_t xTimerDelete( TimerHandle_t xTimer,
const TickType_t xTicksToWait);