FreeRTOS timer定時器原始碼分析
阿新 • • 發佈:2018-12-31
=============================== 部落格點滴積累,部分話語和知識點來源於網路,感謝網路資源的提供者======
freertos的定時器實現比較簡單,建立一個守護程序,建立一個定時器佇列,當建立一個定時器時,就會將該定時器新增到定時器連結串列中,定時器守護程序迴圈讀取定時器連結串列的定時器,判斷是否到期,到期就執行。 vTaskStartScheduler( ) //tasks.c xTimerCreateTimerTask(); //timers.c /*建立一個後臺守護任務*/ xTaskCreate( prvTimerTask, "Tmr Svc", ( uint16_t ) configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, &xTimerTaskHandle );
分析這個定時器task
for( ;; )
{
/*獲取下一個要執行的地上那個定時器*/
xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );
/ *如果計時器過期了,處理它。否則,阻塞這一任務直到一個計時器到期,或收到一個命令。* /
prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );
/*處理接收到的命令*/
prvProcessReceivedCommands();
}
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
{
/* Ensure the task unblocks when the tick count rolls over. */
xNextExpireTime = ( TickType_t ) 0U;
}
return xNextExpireTime;
}
---------------------------------------------------------------------
這兩個函式在文章的最後面
prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );
prvProcessReceivedCommands();
--------------------------------------
static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched )
{
TickType_t xTimeNow;
PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */
/*獲取當前tick值*/
xTimeNow = xTaskGetTickCount();
/*判斷是否溢位,溢位就切換定時器list*/
if( xTimeNow < xLastTime )
{
prvSwitchTimerLists();
*pxTimerListsWereSwitched = pdTRUE;
}
else
{
*pxTimerListsWereSwitched = pdFALSE;
}
/*更新tick值*/
xLastTime = xTimeNow;
return xTimeNow;
}
/*向定時器佇列xTimerQueue發訊息*/
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;
if( xTimerQueue != NULL )
{
/* Send a command to the timer service task to start the xTimer timer. */
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;
}
/*將定時器插入活躍的連結串列中*/
static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime )
{
freertos的定時器實現比較簡單,建立一個守護程序,建立一個定時器佇列,當建立一個定時器時,就會將該定時器新增到定時器連結串列中,定時器守護程序迴圈讀取定時器連結串列的定時器,判斷是否到期,到期就執行。 vTaskStartScheduler( ) //tasks.c xTimerCreateTimerTask(); //timers.c /*建立一個後臺守護任務*/ xTaskCreate( prvTimerTask, "Tmr Svc", ( uint16_t ) configTIMER_TASK_STACK_DEPTH, NULL, ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, &xTimerTaskHandle );
BaseType_t xProcessTimerNow = pdFALSE;listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime ); listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); if( xNextExpiryTime <= xTimeNow ) {/*定時器溢位了*/ if( ( xTimeNow - xCommandTime ) >= pxTimer->xTimerPeriodInTicks ) {/*當前的時間與要執行的時間已經超過PeriodInTicks,要立即執行*/ xProcessTimerNow = pdTRUE; } else { vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) ); } } else { if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ) { xProcessTimerNow = pdTRUE; } else { vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); } } return xProcessTimerNow; } /*-----------------------------------------------------------*/ static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) {
BaseType_t xResult; /*獲取要執行的定時器list head*/Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*刪除要執行的定時器list item*/ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); traceTIMER_EXPIRED( pxTimer ); /*判斷是否是否自動過載的週期定時器*/ if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) { if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) == pdTRUE ) { xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); configASSERT( xResult ); ( void ) xResult; } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } /* Call the timer callback. */ pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); } static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, const BaseType_t xListWasEmpty ) {
TickType_t xTimeNow; BaseType_t xTimerListsWereSwitched;vTaskSuspendAll(); { /*判斷是否溢位,溢位就切換定時器list*/ xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); if( xTimerListsWereSwitched == pdFALSE ) { /* 定時器沒有溢位,沒有切換定時器連結串列,判斷定時器是否過期,過期立即執行 */ if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) { ( void ) xTaskResumeAll(); prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); } else { vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) ); if( xTaskResumeAll() == pdFALSE ) { portYIELD_WITHIN_API(); } else { mtCOVERAGE_TEST_MARKER(); } } } else { ( void ) xTaskResumeAll(); } } } static void prvProcessReceivedCommands( void ) {
DaemonTaskMessage_t xMessage; Timer_t *pxTimer; BaseType_t xTimerListsWereSwitched, xResult; TickType_t xTimeNow;while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */ { #if ( INCLUDE_xTimerPendFunctionCall == 1 ) { /* Negative commands are pended function calls rather than timer commands. */ if( xMessage.xMessageID < ( BaseType_t ) 0 ) { const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters ); /* The timer uses the xCallbackParameters member to request a callback be executed. Check the callback is not NULL. */ configASSERT( pxCallback ); /* Call the function. */ pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 ); } else { mtCOVERAGE_TEST_MARKER(); } } #endif /* INCLUDE_xTimerPendFunctionCall */ /* Commands that are positive are timer commands rather than pended function calls. */ if( xMessage.xMessageID >= ( BaseType_t ) 0 ) { /* The messages uses the xTimerParameters member to work on a software timer. */ pxTimer = xMessage.u.xTimerParameters.pxTimer; if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) { /* The timer is in a list, remove it. */ ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); } else { mtCOVERAGE_TEST_MARKER(); } traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue ); xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); switch( xMessage.xMessageID ) { case tmrCOMMAND_START : case tmrCOMMAND_START_FROM_ISR : case tmrCOMMAND_RESET : case tmrCOMMAND_RESET_FROM_ISR : case tmrCOMMAND_START_DONT_TRACE : /* Start or restart a timer. */ if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) == pdTRUE ) { 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 : /* The timer has already been removed from the active list. There is nothing to do here. */ 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 : /* The timer has already been removed from the active list, just free up the memory. */ vPortFree( pxTimer ); break; default : /* Don't expect to get here. */ break; } } } } /*建立定時器的函式*/ TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ { Timer_t *pxNewTimer; /* Allocate the timer structure. */ if( xTimerPeriodInTicks == ( TickType_t ) 0U ) { pxNewTimer = NULL; } else { pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); if( pxNewTimer != NULL ) { /* Ensure the infrastructure used by the timer service task has been created/initialised. */ prvCheckForValidListAndQueue(); /* Initialise the timer structure members using the function parameters. */ pxNewTimer->pcTimerName = pcTimerName; pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; pxNewTimer->uxAutoReload = uxAutoReload; pxNewTimer->pvTimerID = pvTimerID; pxNewTimer->pxCallbackFunction = pxCallbackFunction; vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); traceTIMER_CREATE( pxNewTimer ); } else { traceTIMER_CREATE_FAILED(); } } /* 0 is not a valid value for xTimerPeriodInTicks. */ configASSERT( ( xTimerPeriodInTicks > 0 ) ); return ( TimerHandle_t ) pxNewTimer; } /*--------------------------------------------