定时器组
// configTIMER_TASK_PRIORITY 定时器任务的任务优先级,可以为 0~( configMAX_PRIORITIES-1),越高越及时响应
// configTIMER_QUEUE_LENGTH 用来设置定时器命令队列的队列长度。
// configTIMER_TASK_STACK_DEPTH 用来设置定时器任务堆栈大小,单位为字,不是字节!
定时器组的时基基于系统时钟节拍实现的,
定时器的实现不基于任何硬件的定时器(不需要使能读寄存器),并且定时器可以创建很多个
硬件定时器是在定时器中断中实现需要的功能
软件定时器,在创建定时器时指定定时器的回调函数,在回调函数中实现对应的功能(回调函数类似中断处理函数)
定时器创建后,到达时间触发回调函数
定时器的回调函数是在定时器服务函数中执行,所以回调函数中一定不可以调用阻塞任务的函数
RTOS通过 prvTimerTask(也叫守护任务Daemon) 任务管理定时器
启动调度器后自动创建该任务,执行期间检查用户启动的 时间周期溢出的定时器,然后调用回调函数
单次模式:用户创建了定时器并启动后,到达定时时间定时器将不再继续执行
周期模式:即使到达了定时时间,会按照设置的时间周期重复执行
运作机制
在创建定时器时会分配一块内存空间
创建并启动软件定时器时,RTOS根据当前系统时间及用户设置的定时时间确定定时器唤醒时间,将定时器控制块挂入定时器列表
RTOS使用两个定时器列表维护定时器,
pxCurrentTimerList和pxOverflowTimerList两个指针在初始化时分别指向xActiveTimerList1和xActiveTimerList2
PRIVILEGED_DATA static List_t xActiveTimerList1;
PRIVILEGED_DATA static List_t xActiveTimerList2;
PRIVILEGED_DATA static List_t *pxCurrentTimerList; // 系统时间没有溢出,用于管理定时器
PRIVILEGED_DATA static List_t *pxOverflowTimerList; // 系统时间溢出后,用于管理定时器
pxCurrentTimerList
系统新创建并激活的定时器以超时时间升序的方式插入pxCurrentTimerList列表中。
系统在定时器任务中扫描pxCurrentTimerList中的第一个定时器,查看是否超时,超时则调用回调函数。
如果没有超时,则将定时器任务挂起(升序排列,第一个最先溢出,没有溢出,剩余的肯定没有溢出)
定时器可使用消息队列进行通信,利用定时器命令序列,向软件定时器任务发送命令,
任务在接收到命令后处理命令对应的程序(重启定时器,停止定时器)
如果定时器任务处于阻塞态,需要添加软件定时器,使用消息队列命令的方式进行添加(向定时器任务发送命令)
唤醒处于等待状态的定时器任务,将新添加的定时器添加到软件定时器列表中
在定时器启动函数中,RTOS采用队列的方式发送消息给软定时器任务,任务被唤醒从而执行接收的命令
xTimerNow用于记录当前时间片
xTicksToWait用于记录定时器溢出时间
Timer1:xTimerNow 0创建,定时200,溢出时间200
Timer2:xTimerNow 20创建,定时100,溢出时间120
Timer3:xTimerNow 40创建,定时50,溢出时间90
升序排列,最先溢出的排在列表第一个,第一个不溢出则其余的定时器都不会溢出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-beepyHbz-1590904170891)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1587812226091.png)]
每一次硬件中断,xTimeNow都会自增
定时器任务运行时自动获取下一个要唤醒的定时器,比较当前系统时间xTimeNow与xTicksToWait,出现超时情况就会调用回调函数,否则将定时器任务挂起,直到下一个要唤醒的定时器时间到来,或者接收到命令消息
注意
回调函数务必快进快出,不允许使用导致定时器任务挂起或阻塞的api函数,也不允许出现死循环
定时器使用系统的一个队列和任务资源,优先级默认为 configTIMER_TASK_PRIORITY,为更好响应任务优先级应是任务中最高
对于单次定时器,定时器超时执行回调函数后,系统自动删除软件定时器并回收资源
定时器是在定时器任务,或者说 定时器守护任务 中实现
关于定时器的API函数都是通过 定时器命令队列 的队列给定时器守护任务发送命令
应用程序通过API函数,向定时器任务发送命令序列,定时器接收命令序列后悔处理命令
定时器控制块
typedef struct tmrTimerControl
{
const char *pcTimerName; // 定时器名称
ListItem_t xTimerListItem; // 定时器列表项,用于插入定时器列表
TickType_t xTimerPeriodInTicks; // 定时器周期
UBaseType_t uxAutoReload; // 模式,pdFalse为单词模式
void *pvTimerID; // 软件定时器的ID,回调函数中根据ID判定定时器(可多个定时器调一个回调函数)
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;
API函数
创建
软件定时器在创建成功后处于休眠状态,需要使用相关API函数将其激活(类似创建任务)
定时器消息队列
typedef struct tmrTimerQueueMessage
{
BaseType_t xMessageID; // 发送到定时器的消息ID
union
{
TimerParameter_t xTimerParameters;
#if ( INCLUDE_xTimerPendFunctionCall == 1 )
CallbackParameters_t xCallbackParameters; // 定时器回调函数
#endif
} u;
} DaemonTaskMessage_t;
typedef struct tmrTimerControl
{
const char *pcTimerName; // 内核不使用,仅用于调试;
ListItem_t xTimerListItem; // 时间列表项
TickType_t xTimerPeriodInTicks; //
UBaseType_t uxAutoReload; //
void *pvTimerID; //
TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
#if( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
#endif
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*<< Set to pdTRUE if the timer was created statically so no attempt is made to free the memory again if the timer is later deleted. */
#endif
} xTIMER;
typedef xTIMER Timer_t;
typedef struct tmrTimerParameters
{
TickType_t xMessageValue; // 更改周期时用的可选值
Timer_t * pxTimer; //
} TimerParameter_t;
TimerHandle_t xTimerCreate( const char * const pcTimerName, // 定时器名称
const TickType_t xTimerPeriodInTicks, // 定时器周期
const UBaseType_t uxAutoReload, // 定时器模式
void * const pvTimerID, // 定时器ID
TimerCallbackFunction_t pxCallbackFunction )// 定时器回调函数
{
Timer_t *pxNewTimer; // 临时定时器控制块
pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); // 为定时器控制块申请一块内存
if ( pxNewTimer != NULL ) // 内存申请成功则指针不为空
{
/* 内存申请成功,进行初始化软件定时器 */
prvInitialiseNewTimer( pcTimerName,
xTimerPeriodInTicks,
uxAutoReload, /* pdTRUE为周期性软件定时器 */
pvTimerID,
pxCallbackFunction,
pxNewTimer ); // 初始化这个临时控制块的成员,最终返回控制块指针,也就是句柄
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
pxNewTimer->ucStaticallyAllocated = pdFALSE;
}
#endif
}
return pxNewTimer;
}
// 用于初始化定时器控制块,定时器 挂载在两个列表下,pxCurrentTimerList 和 pxOverflowTimerList
static void prvInitialiseNewTimer( const char * const pcTimerName, // 定时器名称
const TickType_t xTimerPeriodInTicks, // 定时器周期
const UBaseType_t uxAutoReload, // 定时器模式
void * const pvTimerID, // 定时器ID
TimerCallbackFunction_t pxCallbackFunction, // 定时器回调函数
Timer_t *pxNewTimer ) // 定时器控制块
{
configASSERT( ( xTimerPeriodInTicks > 0 ) ); // 断言,判断定时器的周期是否大于 0
if ( pxNewTimer != NULL )
{
prvCheckForValidListAndQueue(); // 系统初始化软件定时器列表,创建定时器消息队列
// 所有的API函数都是消息队列发送命令序列
/* 初始化软件定时信息,这些信息保存在软件定时器控制块中 */
pxNewTimer->pcTimerName = pcTimerName; // 定时器名称
pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; // 定时器周期
pxNewTimer->uxAutoReload = uxAutoReload; // 周期模式
pxNewTimer->pvTimerID = pvTimerID; // 定时器ID
pxNewTimer->pxCallbackFunction = pxCallbackFunction; // 定时器回调函数
vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); // pxNewTimer->xTimerListItem 记载定时器挂在哪个列表下,
traceTIMER_CREATE( pxNewTimer );
}
}
static void prvCheckForValidListAndQueue( void ) // 初始化定时器列表,创建定时器命令队列
{
taskENTER_CRITICAL();
{
if( xTimerQueue == NULL )
{
vListInitialise( &xActiveTimerList1 ); // 初始化 pxCurrentTimerList 列表
vListInitialise( &xActiveTimerList2 ); // 初始化 pxOverflowTimerList 列表
pxCurrentTimerList = &xActiveTimerList1;
pxOverflowTimerList = &xActiveTimerList2;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
static StaticQueue_t xStaticTimerQueue; // 静态定时器命令队列分配空间
static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ] // 定义数组,大小为 命令队列深度 * 单个命令大小
xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH,
( UBaseType_t ) sizeof( DaemonTaskMessage_t ),
&( ucStaticTimerQueueStorage[ 0 ] ),
&xStaticTimerQueue ); // 静态 创建命令队列
}
#else
{
xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH,
sizeof( DaemonTaskMessage_t ) );// 动态创建 定时器命令队列
}
#endif
#if ( configQUEUE_REGISTRY_SIZE > 0 )
{
if( xTimerQueue != NULL )
{
vQueueAddToRegistry( xTimerQueue, "TmrQ" );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configQUEUE_REGISTRY_SIZE */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
}
static TimerHandle_t Swtmr1_Handle =NULL; /* 软件定时器句柄 */
/* 周期模式的软件定时器 1,定时器周期 1000(tick),创建定时器*/
Swtmr1_Handle=xTimerCreate( (const char*)"AutoReloadTimer", /* 定时器名称 */
(TickType_t)1000, /* 定时器周期 1000(tick) */
(UBaseType_t)pdTRUE, /* 周期模式 */
(void* )1, /* 为每个计时器分配一个索引的唯一 ID */
(TimerCallbackFunction_t)Swtmr1_Callback); /* 回调函数 */
static void Swtmr1_Callback(void* parameter)
{
/* 软件定时器的回调函数,用户自己实现 */
}
启动
在系统开始运行的时候,系统会自动创建定时器任务,如果没有运行中的定时器,则定时器任务进入阻塞状态。
定时器创建后默认处于休眠状态,需要API函数启动定时器
所谓启动函数是通过 定时器命令队列 向定时器任务发送启动命令,定时器任务获得命令后解除阻塞,执行启动软件定时器命令
定时器任务因为没有没有收到消息而一直阻塞,直到消息队列中有消息,定时器任务被唤醒(最高优先级任务)
唤醒后定时器接收命令并处理命令,处理完毕后再次因为没有消息而阻塞
#define xTimerStart( xTimer, xTicksToWait ) \
xTimerGenericCommand( (xTimer),tmrCOMMAND_START,( xTaskGetTickCount() ),NULL,( xTicksToWait ) )
#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken )\
xTimerGenericCommand( (xTimer), tmrCOMMAND_START_FROM_ISR,( xTaskGetTickCountFromISR() ),( pxHigherPriorityTaskWoken ), 0U )
// xTimer 为定时器句柄
// tmrCOMMAND_START 为定时器启动命令序列
// xTaskGetTickCount 用于获取当前系统时间
// pxHigherPriorityTaskWoken 仅在用于中断函数中才不为null
// xTimerGenericCommand 类似队列,可指定发送的阻塞时间
BaseType_t xTimerGenericCommand(TimerHandle_t xTimer, // 定时器控制块
const BaseType_t xCommandID, // 定时器命令
const TickType_t xOptionalValue, // 当前系统时间
BaseType_t * const pxHigherPriorityTaskWoken, // 仅用于中断
const TickType_t xTicksToWait ) // 最大阻塞时间
{ // 向定时器任务发送命令
BaseType_t xReturn = pdFAIL;
DaemonTaskMessage_t xMessage;
configASSERT( xTimer );
/* 发送命令给定时器任务 */
if ( xTimerQueue != NULL ) // 定时器 命令队列 不为空,为定时器命令队列分配空间成功
{
xMessage.xMessageID = xCommandID; // 定时器命令 结构体初始化
xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; // 记录当前系统时间
xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer; // 获取定时器控制块
if ( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) // 命令是任务而不是中断发送
{
/* 如果调度器已经运行了,就根据用户指定超时时间发送 */
if ( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
{
xReturn = xQueueSendToBack( xTimerQueue,&xMessage,xTicksToWait ); // 向消息队列发送消息
}
else
{
/* 如果调度器还未运行,发送就行了,不需要阻塞 */
xReturn = xQueueSendToBack( xTimerQueue,&xMessage,tmrNO_DELAY ); // 未运行无需阻塞的向消息队列发送消息(超时无效)
}
}
/* 命令是在中断中发出的 */
else
{
/* 调用从中断向 命令队列 发送消息的函数 */
xReturn = xQueueSendToBackFromISR( xTimerQueue,&xMessage,pxHigherPriorityTaskWoken );
}
traceTIMER_COMMAND_SEND( xTimer,xCommandID,xOptionalValue,xReturn );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return xReturn;
}
停止
本质是任务向定时器任务发送停止命令,唤醒软件定时器任务将定时器停止
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xBlockTime );
定时器任务
定时器任务是在系统开始调度,也就是调度器开始的时候被创建的
void vTaskStartScheduler( void )
{
#if ( configUSE_TIMERS == 1 )
{
if ( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask(); // 创建定时器任务
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */
}
// 创建定时器任务实现
BaseType_t xTimerCreateTimerTask( void )
{
BaseType_t xReturn = pdFAIL;
prvCheckForValidListAndQueue(); // 初始化定时器列表,创建定时器 命令队列
if ( xTimerQueue != NULL ) // 定时器 命令队列 创建成功
{
#if( configSUPPORT_STATIC_ALLOCATION == 1 ) // 静态创建任务
{
StaticTask_t *pxTimerTaskTCBBuffer = NULL;
StackType_t *pxTimerTaskStackBuffer = NULL;
uint32_t ulTimerTaskStackSize;
vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, // 设置定时器任务的堆栈大小,任务控制块
&pxTimerTaskStackBuffer, // 静态分配空间
&ulTimerTaskStackSize ); // 堆栈单位,字
// 静态创建 定时器任务
xTimerTaskHandle = xTaskCreateStatic(prvTimerTask, // 任务函数名称
"Tmr Svc", // 任务名
ulTimerTaskStackSize, // 任务堆栈单位
NULL, // 任务函数参数
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, // 任务优先级
pxTimerTaskStackBuffer, // 分配给任务的静态空间,是个数组
pxTimerTaskTCBBuffer ); // 定时器任务 控制块,静态创建任务
if ( xTimerTaskHandle != NULL ) // 定时器任务 创建失败
{
xReturn = pdPASS;
}
}
#else /* 动态创建 定时器任务 */
{
xReturn = xTaskCreate( prvTimerTask, // 任务函数名称
"Tmr Svc", // 任务名称,用于调试
configTIMER_TASK_STACK_DEPTH, // 任务堆栈大小
NULL, // 任务函数参数
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, // 任务优先级
&xTimerTaskHandle ); // 定时器任务 句柄
}
#endif
}
else
{
mtCOVERAGE_TEST_MARKER();
}
configASSERT( xReturn );
return xReturn;
}
static void prvTimerTask( void *pvParameters ) // 定时器任务 函数实现
{
TickType_t xNextExpireTime;
BaseType_t xListWasEmpty;
( void ) pvParameters;
for ( ;; )
{
xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); // 获取下一溢出定时器时间,Timer由俩列表维护,升序,第一个先溢出
prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );// 立即处理定时器溢出任务,或任务阻塞到下一定时器溢出时间处理
prvProcessReceivedCommands(); // 定时器命令队列中读取命令,任务通过命令唤醒定时器任务,或者溢出唤醒
}
}
static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) // 返回值下一到期定时器时间,挂载列表pxCurrentTimerList否
{
TickType_t xNextExpireTime;
*pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList ); // pxCurrentTimerList 列表是否为空,
if( *pxListWasEmpty == pdFALSE ) // pxCurrentTimerList 不为空,获取第一个定时器控制块
{ // 系统时间未溢出,定时器都在 pxCurrentTimerList 下;系统时间溢出,定时器都在 pxOverflowTimerList 下
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); // 第一个定时器时间最先到达
}
else // pxCurrentTimerList 中没有,都在 pxOverflowTimerList
{
xNextExpireTime = ( TickType_t ) 0U; // 设置为0则用不溢出
}
return xNextExpireTime;
}
// 下一到期时间,pxCurrentTimerList 中是否有活动定时器
static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty )
{
TickType_t xTimeNow;
BaseType_t xTimerListsWereSwitched;
vTaskSuspendAll(); // 挂起所有任务,避免定时器列表被影响
{
xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); // 获取当前时钟节拍,判定系统时间是否溢出(溢出要使用溢出列表)
if( xTimerListsWereSwitched == pdFALSE ) // 当前系统时钟节拍没有溢出(溢出使用溢出列表)
{
if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) // 溢出列表无定时器,下一溢出时间比当前时间小
{ // 当前定时器在 pxCurrentTimerList
( void ) xTaskResumeAll(); // 对定时器列表操作完毕,恢复调度器
prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); // 对于周期模式的定时器,更新下一次溢出时间,插入列表
}
else
{
if( xListWasEmpty != pdFALSE ) // pxCurrentTimerList为空,系统时间溢出了
{
xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList ); // pxOverflowTimerList是否有溢出定时器
}
// 定时器时间还没有到,当前任务挂起,阻塞直到定时器到期或定时器收到命令(接收消息命令),阻塞时间为到溢出的时间
vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty );
if( xTaskResumeAll() == pdFALSE ) // 恢复调度器
{
portYIELD_WITHIN_API(); // 任务切换
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
else
{
( void ) xTaskResumeAll();
}
}
}
// 获取当前系统时间,判定定时器在哪个列表中
static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched )
{
TickType_t xTimeNow;
PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U;
xTimeNow = xTaskGetTickCount(); // 获取当前系统时间
if( xTimeNow < xLastTime ) // 判定系统时间是否溢出,当前节拍比上一节拍小,则溢出
{
prvSwitchTimerLists(); // 发生溢出系统要处理未溢出列表上所有定时器,切换所有定时器到定时器溢出列表,并且标记定时器列表切换
// 也就是pxTimerListsWereSwitched标识为pdTRUE
*pxTimerListsWereSwitched = pdTRUE;
}
else
{
*pxTimerListsWereSwitched = pdFALSE; // 系统时间没有溢出则不作处理
}
xLastTime = xTimeNow; // 更新xLastTime的值
return xTimeNow; // 返回当前系统时间,查询 pxCurrentTimerList 列表,使用 pxCurrentTimerList 为pdFALSE
}
static void prvProcessReceivedCommands( void ) // 从命令队列 接收消息
{
DaemonTaskMessage_t xMessage;
Timer_t *pxTimer;
BaseType_t xTimerListsWereSwitched, xResult;
TickType_t xTimeNow;
// 从消息队列中接收消息,
while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) // 接收消息,成功接收到消息
{
if( xMessage.xMessageID >= ( BaseType_t ) 0 ) // 命令有效
{
pxTimer = xMessage.u.xTimerParameters.pxTimer; //
// 定时器是否在列表中
if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE )
{
( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); // 将定时器从列表中移出
}
else
{
mtCOVERAGE_TEST_MARKER(); // 定时器不在列表中
}
traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue );
xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); // 获取当前系统时间,查询系统时间是否溢出
switch( xMessage.xMessageID ) // 根据命令ID进行处理
{
case tmrCOMMAND_START :
case tmrCOMMAND_START_FROM_ISR :
case tmrCOMMAND_RESET :
case tmrCOMMAND_RESET_FROM_ISR :
case tmrCOMMAND_START_DONT_TRACE : // 开始或重启命令,求溢出时间插入定时器列表
if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE )
{
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );// 定时器已溢出,调用回调函数
traceTIMER_EXPIRED( pxTimer );
if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) // 如果是周期模式
{ // 发送命令重新启动定时器
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY );
configASSERT( xResult );
( void ) xResult;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
break;
case tmrCOMMAND_STOP :
case tmrCOMMAND_STOP_FROM_ISR :
// 停止命令则将定时器从列表中移除,之前已进行移除操作
break;
// 更新定时器配置,就是改变周期
case tmrCOMMAND_CHANGE_PERIOD :
case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR :
// 更新定时器周期消息,伴随跟新周期的时间
pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue;
configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) );
( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow ); // 从命令发出,到处理命令,
break;
case tmrCOMMAND_DELETE : // 如果是删除命令,从当前列表中删除,前期已有这个操作
#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
{
vPortFree( pxTimer ); // 动态分配释放内存
}
#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
{
if( pxTimer->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
{
vPortFree( pxTimer );
}
else // 静态分配
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
break;
default :
/* Don't expect to get here. */
break;
}
}
}
}
static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer,
const TickType_t xNextExpiryTime,
const TickType_t xTimeNow,
const TickType_t xCommandTime )
{
BaseType_t xProcessTimerNow = pdFALSE;
listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime );
listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );
if( xNextExpiryTime <= xTimeNow ) // 下一到期时间 <= 当前时间,定时器到期溢出
{ // 当前时间 - 更新周期 >= 当前周期
// ( xTimeNow + pxTimer->xTimerPeriodInTicks ) = xNextExpiryTime
// pxTimer->xTimerPeriodInTicks = xNextExpiryTime - xTimeNow
// ( xTimeNow - xCommandTime ) ) >= ( xNextExpiryTime - xTimeNow )
if( ( ( TickType_t ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks )
{ // 当前时间 >= 上次到期时间 + 命令持续时间,当前已到达时间
xProcessTimerNow = pdTRUE; // 立刻执行
}
else //
{ // 将定时器添加到系统时间后,定时器管理列表
vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );
}
}
else // 下一到期时间 > 当前时间,需要等待
{ // 当前时间 < 要运行的时间,且 下一到期时间 >= 要运行的时间
if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) )
{
xProcessTimerNow = pdTRUE; // 立刻运行
}
else
{ // 插入 pxCurrentTimerList 列表
vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) );
}
}
return xProcessTimerNow;
}
删除
#define xTimerDelete( xTimer, xTicksToWait )\
xTimerGenericCommand( ( xTimer ),tmrCOMMAND_DELETE,0U, NULL, ( xTicksToWait ) )