【freertos-kernel】timer
文章目录
- 结构体
- Timer_t
- DaemonTaskMessage_t
- prvTimerTask
- prvGetNextExpireTime
- prvProcessTimerOrBlockTask
- prvSampleTimeNow
- prvSwitchTimerLists
- prvProcessExpiredTimer
- prvReloadTimer
- prvInsertTimerInActiveList
- vQueueWaitForMessageRestricted
- prvProcessReceivedCommands
- 其他函数说明
- xTimerCreate
- xTimerGenericCommandFrom(Task/ISR)
结构体
Timer_t
typedef struct tmrTimerControl
{const char * pcTimerName; //调试用的定时器名称ListItem_t xTimerListItem; //插入定时器链表的节点TickType_t xTimerPeriodInTicks; //定时器周期void * pvTimerID; //用户自定义 IDportTIMER_CALLBACK_ATTRIBUTE TimerCallbackFunction_t pxCallbackFunction;//定时器到期时调用的回调函数#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxTimerNumber; //调试用编号#endifuint8_t ucStatus; //状态标志(是否激活、是否周期性等)
} xTIMER;typedef xTIMER Timer_t;
DaemonTaskMessage_t
发给守护任务的消息结构体
typedef struct tmrTimerQueueMessage
{BaseType_t xMessageID; /**< The command being sent to the timer service task. */union{TimerParameter_t xTimerParameters;#if ( INCLUDE_xTimerPendFunctionCall == 1 )CallbackParameters_t xCallbackParameters;#endif /* INCLUDE_xTimerPendFunctionCall */} u;
} DaemonTaskMessage_t;typedef struct tmrTimerParameters
{TickType_t xMessageValue;Timer_t * pxTimer;
} TimerParameter_t;typedef struct tmrCallbackParameters
{portTIMER_CALLBACK_ATTRIBUTEPendedFunction_t pxCallbackFunction;void * pvParameter1;uint32_t ulParameter2;
} CallbackParameters_t;
prvTimerTask
还记得吗,调度器启动的时候会创建一个定时器任务xTimerCreateTimerTask
,这个任务的函数是prvTimerTask
,这是一个守护任务,由内核自动创建和管理,运行在后台,处理所有通过 xTimerStart()、xTimerStop() 等 API 发送的命令,并在定时器到期时调用回调函数。
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters )//宏展开就是static void prvTimerTask(void *pvParameters )
static portTASK_FUNCTION( prvTimerTask, pvParameters )
{TickType_t xNextExpireTime;BaseType_t xListWasEmpty;( void ) pvParameters;#if ( configUSE_DAEMON_TASK_STARTUP_HOOK == 1 ){ //可选的启动钩子函数vApplicationDaemonTaskStartupHook();}#endif /* configUSE_DAEMON_TASK_STARTUP_HOOK */for( ; configCONTROL_INFINITE_LOOP(); ){ //获取下一个定时器到期时间xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );//处理到期定时器或进入阻塞等待prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );//处理接收到的定时器命令prvProcessReceivedCommands();}
}
在真正创建任务之前,prvCheckForValidListAndQueue
初始化了定时器相关的链表,xActiveTimerList1
和xActiveTimerList2
。两个指向链表的指针,pxCurrentTimerList
,当前正在使用的链表,正常情况下使用 pxCurrentTimerList;pxOverflowTimerList
,tick 计数器存储长度是有限的,32位或者64位等等,当发生时间溢出的时候,就用这个链表。
prvGetNextExpireTime
static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty )
{TickType_t xNextExpireTime;*pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList );if( *pxListWasEmpty == pdFALSE ){ //链表中的定时器是按照到期时间升序排的,链表头就是最早到期的那个定时器 。//如果链表不为空就获取第一个定时器的到期时间xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );}else{xNextExpireTime = ( TickType_t ) 0U;}return xNextExpireTime;
}
prvProcessTimerOrBlockTask
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 ) )//判断是否有到期的定时器{( void ) xTaskResumeAll();prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); //处理所有已到期的定时器}else//没有到期定时器则阻塞等待下一次触发{if( xListWasEmpty != pdFALSE ){ //检查溢出链表是否也为空xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList );}vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty );//让定时器服务任务进入阻塞状态 if( xTaskResumeAll() == pdFALSE ){taskYIELD_WITHIN_API();}}}else{( void ) xTaskResumeAll();}}
}
prvSampleTimeNow
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;}else{*pxTimerListsWereSwitched = pdFALSE;}xLastTime = xTimeNow;return xTimeNow;
}
prvSwitchTimerLists
tmrMAX_TIME_BEFORE_OVERFLOW
是(TickType_t) (-1)
,tick计数的最大值。
static void prvSwitchTimerLists( void )
{TickType_t xNextExpireTime;List_t * pxTemp;while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ){ //循环处理主链表中所有到期定时器xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );prvProcessExpiredTimer( xNextExpireTime, tmrMAX_TIME_BEFORE_OVERFLOW ); //执行到期定时器的回调函数}//交换链表pxTemp = pxCurrentTimerList;pxCurrentTimerList = pxOverflowTimerList;pxOverflowTimerList = pxTemp;
}
prvProcessExpiredTimer
从当前链表中取出最早到期的定时器,移除它,并根据其类型(单次/周期)决定是否重新加载,最后调用其回调函数。
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime,const TickType_t xTimeNow )
{Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );( void ) uxListRemove( &( pxTimer->xTimerListItem ) );if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0U ){prvReloadTimer( pxTimer, xNextExpireTime, xTimeNow );}else{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
}
prvReloadTimer
为周期性定时器重新安排下一次触发时间,并在必要时多次调用回调函数,确保不会因为系统延迟而漏掉任何一次触发。
static void prvReloadTimer( Timer_t * const pxTimer,TickType_t xExpiredTime,const TickType_t xTimeNow )
{while( prvInsertTimerInActiveList( pxTimer, ( xExpiredTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xExpiredTime ) != pdFALSE ){xExpiredTime += pxTimer->xTimerPeriodInTicks;pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );}
}
系统可能由于中断延迟、任务调度等原因,导致定时器回调没有及时执行,此时,原本应该在 xExpiredTime + period
到期的定时器,实际上已经被跳过了,所以必须不断累加周期时间,直到找到一个未来的时间点,再把它插入链表,同时,对于每个“已错过”的时间点,都调用一次回调函数(模拟“补发”)
prvInsertTimerInActiveList
将一个定时器插入到当前活动链表或溢出链表中,返回值表示是否已经过期需要立即执行回调
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 ){if( ( ( TickType_t ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks )xProcessTimerNow = pdTRUE;}else{vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );}}else{if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ){xProcessTimerNow = pdTRUE;}else{vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) );}}return xProcessTimerNow;
}
vQueueWaitForMessageRestricted
让任务在无数据可读时进入阻塞状态
void vQueueWaitForMessageRestricted( QueueHandle_t xQueue,TickType_t xTicksToWait,const BaseType_t xWaitIndefinitely )
{Queue_t * const pxQueue = xQueue;prvLockQueue( pxQueue );if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ){vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely );}prvUnlockQueue( pxQueue );
}
prvProcessReceivedCommands
用于处理命令消息
static void prvProcessReceivedCommands( void )
{DaemonTaskMessage_t xMessage = { 0 };Timer_t * pxTimer;BaseType_t xTimerListsWereSwitched;TickType_t xTimeNow;while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ){#if ( INCLUDE_xTimerPendFunctionCall == 1 ){if( xMessage.xMessageID < ( BaseType_t ) 0 ){const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters );configASSERT( pxCallback );pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 );}}#endif /* INCLUDE_xTimerPendFunctionCall */if( xMessage.xMessageID >= ( BaseType_t ) 0 ){pxTimer = xMessage.u.xTimerParameters.pxTimer;if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ){( void ) uxListRemove( &( pxTimer->xTimerListItem ) );}xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );switch( xMessage.xMessageID ){case tmrCOMMAND_START:case tmrCOMMAND_START_FROM_ISR:case tmrCOMMAND_RESET:case tmrCOMMAND_RESET_FROM_ISR:pxTimer->ucStatus |= ( uint8_t ) tmrSTATUS_IS_ACTIVE;if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE ){if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0U ){prvReloadTimer( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow );}else{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );}break;case tmrCOMMAND_STOP:case tmrCOMMAND_STOP_FROM_ISR:pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );break;case tmrCOMMAND_CHANGE_PERIOD:case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR:pxTimer->ucStatus |= ( uint8_t ) tmrSTATUS_IS_ACTIVE;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 ){if( ( pxTimer->ucStatus & tmrSTATUS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) 0 ){vPortFree( pxTimer );}else{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}}#else /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */{pxTimer->ucStatus &= ( ( uint8_t ) ~tmrSTATUS_IS_ACTIVE );}#endif /* configSUPPORT_DYNAMIC_ALLOCATION */break;default:break;}}}
}
其他函数说明
xTimerCreate
初始化一个Timer_t
以及他的链表项xTimerListItem
。具体代码很简单,就不贴了。
xTimerGenericCommandFrom(Task/ISR)
初始化一个DaemonTaskMessage_t
,发送到xTimerQueue
里。
xTimerStart
xTimerStop
等等最上层的函数就是调用xTimerGenericCommandFrom(Task/ISR)
发送不同的命令(上面解析命令里看过)。