1. 程式人生 > >FreeRTOS timer定時器原始碼分析

FreeRTOS timer定時器原始碼分析

===============================   部落格點滴積累,部分話語和知識點來源於網路,感謝網路資源的提供者======
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 ) {
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; } /*--------------------------------------------