1. 简介
软件定时器用于在未来的某个时间执行某个预先指定的函数,或者以一个固定的频率周期性调度该函数。这个预先指定的函数称为软件定时器回调函数, 它是由软件定时器服务调用的.
软件定时器由FreeRTOS的内核控制,不需要硬件的支持,不与硬件定时器和硬件计数器关联。
2. 软件定时器回调函数
函数原型定义:
typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer );
要求:执行时间尽可能短,不允许进入Block state.
3. 软件定时器的属性和状态
3.1. 软件定时器的间隔(period)
软件定时器的间隔指的是软件定时器被开启到回调函数被执行的时间间隔, 以Tick为单位。
获取period的周期的函数原型:
TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
3.2. One-shot和Auto-reload定时器
one-shot定时器:回调函数被执行一次便失效的定时器类型。可以被手动重启。
auto-reload定时器:每次超时后会自动重启的定时器类型。回调函数将会被多次执行。
3.3. 软件定时器状态
两种状态:
- dormant(休眠的):可以被引用,但没有开始"计时", 所以回调函数不会被调用.
- running:正常执行,超时后会调用回调函数。
状态转换:
-
One-shot定时器
-
Auto-reload定时器
获取定时器状态的函数原型:
BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
返回pdFALSE
则表示定时器处于dormant状态。否则处于active(running)。
该函数会进入临界区,然后判断该定时器是否在Active队列中。因为链表项和链表互联,查看该定时器的xTimerListItem
的Container是否为NULL
,如果不为NULL
,则表明在当前Active或超时队列中,这个操作时间复杂度只有O(1)。
4. 软件定时器的上下文
在FreeROTS的定时器实现中,所有定时器服务都由一个Timer Task来提供, 并通过一个链表来管理Active的定时器。其他任务通过一个定时器命令队列与Timer Task进行交互。
4.1. RTOS Daemon(Timer Service) Task
职责:负责调用回调函数和处理命令队列的消息。
创建:在开启调度器时自动创建。详情可查看任务管理文章中的开启调度器部分.
优先级:configTIMER_TASK_PRIORITY
栈大小:configTIMER_TASK_STACK_DEPTH
4.1.1. 创建与获取RTOS Daemon(Timer Service) Task
创建Timer Task的函数原型如下:
BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION;
该函数主要负责初始化Active定时器队列和定时器命令队列, 并创建Timer task.
在介绍该函数的具体实现之前, 先来看看检查和创建Active定时器队列和定时器命令队列(static void prvCheckForValidListAndQueue( void )
)的实现:
可以看到, Active定时器队列是由两个队列组成的, 分别是当前激活的Active定时器队列(即pxCurrentTimerList
指向的队列)和溢出的Active定时器队列(即pxOverflowTimerList
指向的队列). 当系统Tick溢出后, 需要对这两个指针进行一次切换.
创建Timer service的具体实现过程如下:
这里创建的timer task的入口为prvTimerTask()
。
获取Timer Daemon Task句柄的函数原型:
TaskHandle_t xTimerGetTimerDeamonTaskHandle( void ) PRIVILEGED_FUNCTION;
只有在开启了调度器之后才允许调用该函数。
4.1.2. RTOS Daemon(Timer Service) Task的设计与实现
在创建与获取RTOS Daemon(Timer Service) Task中, 可以看到, RTOS Daemon(Timer Service) Task的任务函数是prvTimerTask()
. 这个函数便是Timer Service的实现. 下面就来看看该函数的实现.
Timer Service的主体是一个无限循环的处理逻辑. 但在进入任务的无限循环之前,FreeRTOS会调用外部实现的钩子函数vApplicationDaemonTaskStartupHook()
,调用代码如下所示:
#if( configUSE_DAEMON_TASK_STARTUP_HOOK == 1)
extern void vApplicationDaemonTaskStartupHook( void );
vApplicationDaemonTaskStartupHook();
#endif
该机制可使得上层应用的程序员编写在Timer Task上下文运行的代码。如果真的需要,甚至可以重载Timer Task的功能。
此后,该任务将进入类似于服务器的监听状态,代码如下:
for( ;; )
{
xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );
prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );
prvProcessReceivedCommands();
}
监听状态的实现代码很简洁,但在详细说明这个流程之前,首先需要理解几个与时刻相关的概念,以及几个关键的行为。
关键概念:
- 发送命令的时刻
xCommandTime
:定时器的所有控制都是通过将命令发送到定时器命令队列的方式来实现的,而命令的实际执行者便是Timer Task本身。因此发送命令的时刻指的就是命令入队的时刻。此时该命令并没有被处理。
- 当前时刻
xTimeNow
:即通过xTaskGetTickCount()
获取的系统Tick。
- 处理命令的时刻:处理的时刻指的是执行命令的时刻。
- 定时器超时时刻
xNextExpiryTime
:它等于发送命令的时刻加上定时器的周期,即xNextExpityTime = xCommandTime + pxTimer->xTimerPeriodInTicks
。
关键的行为:
- 切换Active定时器队列(
static void prvSwitchTimerLists( void )
): 切换队列的原因创建与获取RTOS Daemon(Timer Service) Task所述. 该函数主要负责处理当前已经超时的定时器, 并切换Active定时器队列指针.
- 获取当前的Tick并进行队列切换(
static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListWereSwitched )
): 该函数的主要职责是获取当前的Tick,并根据是否发生了Tick溢出,进而切换当前的Active定时器队列,通过pxTimerListWereSwitched
将结果反馈给调用者。
- 将定时器加入到Active定时队列(
static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime )
): 顾名思义,这个函数的主要任务就是将定时器插入到合适的Active队列。因此区分Tick overflow的情况。另外,它还需要处理一些特殊的情况,即如果被插入的定时器已经超时(pdTRUE
表示已超时), 则不会进行入队. 最终将是否超时的标志反馈给调用者。
- 处理超时的定时器(
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow )
).
在以上说明的基础上,下面将介绍Timer Task的实现,活动图如下:
总体上分为3步:
-
获取最近要超时的时刻(xNextExpireTime = prvGetNextExpireTime(&xListWasEmpty)
):查询Active定时器队列,如果不为空,则获取最近超时的定时器的超时时刻xNextExpireTime
;否则,返回0。时刻记住xNextExpireTime
是有可能小于当前Tick count的。xListWasEmpty
记录了当前Active定时器队列是否为空。
-
处理超时的定时器,又或是进入等待状态(prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty )
):
-
处理定时器命令队列(static void prvProcessReceivedCommands( void )
):
4.2. Timer Command Queue
定时器命令队列对外部是不可见的。
职责:连接用户task和rtos daemon task。用户task每次调用timer相关的函数,自动入队相关的命令。
长度:configTIMER_QUEUE_LENGTH
向该队列发送命令的函数原型:
BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
xTimerGenericCommand()
是定时器操作的核心API。从名字可以知道,它就是向定时器命令队列发送控制命令和参数以实现相应的功能。它没有中断上下文的版本,因为它在内部通过命令类型来判断调用时的上下文。如果是在任务上下文,在给队列发送之前,会判断当前调度器是否挂起,如果被挂起了,xTicksToWait
会被忽略,而强制设置为0,这是因为调度器挂起时,不允许当前任务进入Blocked状态。此时给命令队列的参数类型为xTimerParameter_t
。
具体实现如下:
{
校验入参(configASSERT( xTimer ));
如果定时器命令队列不为空(xTimerQueue != NULL):
{
设置命令的ID(xMessage.xMessageID = xCommandID);
设置命令的定时器消息参数(xMessage.u.xTimerParameters.xMessageValue = xOptionalValue);
设置命令的目标定时器句柄(xMessage.u.xTimerParamters.pxTimer = (Timer_t *) xTimer);
如果命令来自任务上下文(xCommandID < tmrFIRST_FROM_ISR_COMMAND):
{
如果当前调度器未被挂起(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING):
{
将命令入队到定时器命令队列, 允许等待(xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ));
}
否则, 即调度器被挂起:
{
将命令入队到定时器命令队列, 不允许等待(xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ));
}
}
否则, 即命令来自ISR:
{
将命令入队到定时器命令队列(xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ));
}
}
返回xReturn;
}
5. 创建并开启软件定时器
创建定时器的函数原型:需要设置configSUPPORT_DYNAMIC_ALLOCATION = 1
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION;
参数中,pcTimerName
仅用于调试。
xTimerPeriodInTicks
是定时器的周期,单位是Ticks,因此可通过portTICK_PERIOD_MS
转换成现实时间。传入的值必须大于零。
若uxAutoReload
为pdTURE
,定时器将会变成一个周期性的定时器(Auto-Reload),以xTimerPeriodInTicks
为周期;否则为One-Shot定时器,即超时一次后就进入休眠状态。
pvTimerID
是Timer的ID。通常而言,它会作为参数传给定时器的回调函数,用于区分是哪个定时器发生了超时。
pxCallbackFunction
便是定时器回调函数了。
如果调用失败则返回NULL
,否者返回这个定时器的句柄。
该函数创建一个软件定时器实例,并返回其句柄。类似于Task,它需要创建一个内部的数据结构(Timer_t
)来记录该软件定时器的信息。
在介绍具体实现之前, 先来看看如何初始化一个新定时器的原信息(static void prvInitialiseNewTimer( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, Timer_t *pxNewTimer )
):
{
校验定时器的周期(configASSERT( ( xTimerPeriodInTicks > 0 ) ));
如果定时器指针不为空(pxNewTimer != NULL):
{
检查Active定时器队列和定时器命令队列是否已经创建, 如果未创建则创建(prvCheckForValidListAndQueue());
设置定时器的名字(pxNewTimer->pcTimerName = pcTimerName);
设置定时器的周期(pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks);
设置定时器的Reload类型(pxNewTimer->uxAutoReload = uxAutoReload);
设置定时器的ID(pxNewTimer->pvTimerID = pvTimerID);
设置定时器的回调函数(pxNewTimer->pxCallbackFunction = pxCallbackFunction);
初始化定时器的链表项(vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ));
}
}
xTimerCreate
的具体实现如下:
{
申请定时器原信息结构体所需的内存pxNewTimer(pxNewTimer = (Timer_t *) pvPortMalloc( sizeof( Timer_t ) ));
如果申请内存成功(pxNewTimer != NULL):
{
初始化定时器原信息(prvInitialseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ));
(仅开启configSUPPORT_STATIC_ALLOCATION):
{
清空定时器静态创建标志(pxNewTimer->ucStaticallyAllocated = pdFALSE);
}
}
返回pxNewTimer;
}
另外,与Task类似,还提供了静态创建的版本:
TimerHandle_t xTimerCreateStatic( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION;
与动态创建不同的是,静态创建的接口需要使用pxTimerBuffer
来记录该定时器的内部信息。
与任务创建不一样的是,定时器在创建后,处于休眠状态,并不会开始计时。可以通过如下几个接口开启:
xTimerStart()
xTimerReset()
xTimerStartFromISR()
xTimerResetFromISR()
xTimerChangePeriod()
xTimerChangePeriodFromISR()
先看看开启定时器的函数原型:需要设置configUSE_TIMERS = 1
#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
可以看到,它是通过向定时器命令队列发送命令来实现开启定时器的效果的。如果一个定时器已经处于Active状态,那xTimerStart()
的功能等同于复位定时器xTimerReset()
。该函数可以在调度器开启前被调用,但实际上定时器是在开启任务调度器之后才开始运行。
xTicksToWait
是调用者的等待时间。
如果在xTicksToWait
时间内未成功将命令发送到定时器命令队列,则返回pdFAIL
;否则返回pdPASS
。
该函数还提供了中断上下文的版本:
#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U )
由于该操作有可能导致Timer Task被唤醒,所以需要pxHigherPriorityTaskWoken
来记录是否需要进行调度。
6. 定时器ID
每个定时器有一个ID,在创建Timer时由外部赋予(也可后期更改),并且换传递到定时器回调函数中。当多个定时器使用相同的定时器回调函数时,可以通过ID来判断是哪个定时器发生了超时,也可用作内部私有信息。
获取ID的函数原型:
void *pvTimerGetTimerID( const TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
设置ID的函数原型:
void vTimerSetTimerID( const TimerHandler_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION;
7. 修改Timer的Period
FreeRTOS的软件定时器支持在其活动的状态下,修改其超时周期。函数原型如下:需要设置configUSE_TIMERS = 1
#define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) )
该函数还可以在定时器处于dormant状态时使用。需要注意的是,在修改完周期后它将开启该定时器。参数和返回值与其他类似,不在赘述。
FreeRTOS还提供了中断上下文的版本:
#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U )
8. 复位软件Timer
复位Timer指的是重启Timer。函数原型如下:需要设置configUSE_TIMERS = 1
#define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) )
如果此时Timer处于Active,那Timer的计时将会重新开始。如果Timer处于Dormant状态,那其功能类似于xTimerStart()
。
FreeRTOS还提供了中断上下文的版本:
#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken), 0U )
9. PendFunction
PendFunction可以理解为被延后的函数调用。它是一种特殊的定时器回调,与其他定时器回调不同的是,它是将回调函数指针直接发送到Timer命令队列,由RTOS Daemon Task执行调用。
发送设置PendFunction的函数原型:
BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void * pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
pvParameter1
和ulParameter2
是xFunctionToPend
的参数。
由于该函数实际上还是通过Timer命令队列实现,所以需要给出等待时间xTicksToWait
。
如果成功发送到Timer命令队列,则返回pdPASS
;否则返回pdFALSE
。
此时给任务命令队列发送的参数类型为xCallbackParameters_t
。
具体实现如下所示:
{
校验定时器命令队列(configASSERT( xTimerQueue ));
设置命令的类型为PendFunction(xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK);
设置回调函数(xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend);
设置回调函数的参数1(xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1);
设置回调函数的参数2(xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2);
将命令入队到定时器命令队列(xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ));
返回xReturn;
}
该函数还有中断上下文的版本:
BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
10. 停止和删除定时器
停止定时器的函数原型:需要设置configUSE_TIMERS = 1
#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) )
它将会把指定的定时器设置为dormant状态。其参数和返回值与xTimerStart()
相似,这里就不赘述了。
该函数还提供了中断上下文的版本:
#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U )
删除定时器的函数原型:需要设置configUSE_TIMERS = 1
#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) )
11. 其他定时器操作
获取定时器的名字的函数原型:
const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
获取定时器的超时时刻的函数原型:
TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION;
如果此时Timer处于dormant状态,则返回无意义的值。
12. 定时器的实现细节
本节将从定时器的对外接口和内涵分别阐述定时器的实现细节。
12.1. 定时器的接口
12.1.1. 定时器的数据结构接口
12.1.1.1. 定时器句柄
句柄定义:
typedef void * TimerHandle_t;
指代某个具体的定时器,所有定时器操作的对象。在创建定时器时创建。
12.1.1.2. 定时器回调函数指针类型
定义如下:
typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer );
定义了定时器回调函数必须遵循的模板。
12.1.1.3. PendFunction函数指针类型
定义如下:
typedef void (*PendedFunction_t)( void *, uint32_t );
定义了xTimerPendFunctionCallFromISR()
所示用的函数必须遵循的模板。
12.1.2. 定时器的接口宏
12.1.2.1. 定时器的常量
关于Timer命令的定义:
#define tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR ( ( BaseType_t ) -2 )
#define tmrCOMMAND_EXECUTE_CALLBACK ( ( BaseType_t ) -1 )
#define tmrCOMMAND_START_DONT_TRACE ( ( BaseType_t ) 0 )
#define tmrCOMMAND_START ( ( BaseType_t ) 1 )
#define tmrCOMMAND_RESET ( ( BaseType_t ) 2 )
#define tmrCOMMAND_STOP ( ( BaseType_t ) 3 )
#define tmrCOMMAND_CHANGE_PERIOD ( ( BaseType_t ) 4 )
#define tmrCOMMAND_DELETE ( ( BaseType_t ) 5 )
#define tmrFIRST_FROM_ISR_COMMAND ( ( BaseType_t ) 6 )
#define tmrCOMMAND_START_FROM_ISR ( ( BaseType_t ) 7 )
#define tmrCOMMAND_RESET_FROM_ISR ( ( BaseType_t ) 8 )
#define tmrCOMMAND_CHANGE_PERIOD_FROM_ISR ( ( BaseType_t ) 9 )
12.2. 定时器的内涵
12.2.1. 定时器依赖的头文件
标准C库:
#include <stdlib.h>
FreeRTOS头文件:
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
12.2.2. 定时器的私有数据结构
12.2.2.1. 定时器控制信息
软件定时器的实现中,最重要的数据结构为定时器控制信息,定义如下:
typedef struct tmrTimerControl
{
const char *pcTimerName;
ListItem_t xTimerListItem;
TickType_t xTimerPeriodInTicks;
UBaseType_t uxAutoReload;
void *pvTimerID;
TimerCallbackFunction_t pxCallbackFunction;
#if( configUSE_TRACE_FACILITY == 1)
UBaseType_t uxTimerNumber;
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
#endif
} xTIMER;
typedef xTIMER Timer_t;
pcTimerName
是Timer的名字,仅用于调试。
xTimerListItem
是Active定时器队列的链表项。其xItemValue
记录了定时器的超时时刻。
xTimerPeriodInTicks
记录了该定时器的超时周期。
uxAutoReload
用于标识该Timer为One-shot或Auto-Reload定时器。
pvTimerID
记录定时器的私有ID。由外部传入。
pxCallbackFunction
定时器回调函数。
uxTimerNumber
只在使能了profiling功能时使用。
ucStaticallyAllocated
用于标识该定时器的数据是否为静态创建,如果为pdTRUE
则不执行Free动作。
12.2.2.2. Timer命令队列参数结构体
定义如下:
typedef struct tmrTimerParameters
{
TickType_t xMessageValue;
Timer_t * pxTimer;
} TimerParameter_t;
typedef struct tmrCallbackParameters
{
PendedFunction_t pxCallbackFunction;
void *pvParameter1;
uint32_t ulParameter2;
} CallbackParameters_t;
typedef struct tmrTimerQueueMessage
{
BaseType_t xMessageID;
union
{
TimerParameter_t xTimerParameters;
#if( INCLUDE_xTimerPendFunctionCall == 1 )
CallbackParameters_t xCallbackParameters;
#endif
} u;
} DaemonTaskMessage_t;
DaemonTaskMessage_t
是Timer命令队列中的命令结构体. xMessageID
是命令的类型.
而从联合体u
的定义可以看出,一共有两种消息可以被入队:
- 用于实现软件定时的
TimerParameter_t
:其中的xMessageValue
可用作命令的参数;pxTimer
则是目标定时器。
- 用于实现延迟调用的
CallbackParameters_t
:pxCallbackFunction
被延迟调度的函数指针;pvParameter1
和ulParameter2
是函数的参数。
12.2.2.3. Active Timer队列
定义如下:
PRIVILEGED_DATA static List_t xActiveTimerList1;
PRIVILEGED_DATA static List_t xActiveTimerList2;
PRIVILEGED_DATA static List_t *pxCurrentTimerList;
PRIVILEGED_DATA static List_t *pxOverflowTimerList;
与Task的Delayed任务队列类似,FreeRTOS存在两个Active Timer队列和两个指针,其中pxCurrentTimerList
指向正常运行的Timer,而pxOverflowTimerList
指向溢出于当前Tick的队列。
在队列中,队列项按照剩余超时时间升序排列,即最快超时的项排在最前面。
12.2.2.4. Timer命令队列
定义如下:
PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL;
命令队列是基于FreeRTOS的队列实现的。
12.2.3. 定时器的私有宏
12.2.3.1. 定时器的私有常量
杂项:
#define tmrNO_DELAY ( TickType_t ) 0U
Timer Task的任务名:可在FreeRTOSConfig.h
中修改。
#define configTIMER_SERVICE_TASK_NAME "Tmr Svc"