FreeRTOS任务延时函数
生活随笔
收集整理的這篇文章主要介紹了
FreeRTOS任务延时函数
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
系統提供了兩個任務延時函數:相對延時函數vTaskDelay()和絕對延時函數vTaskDelayUntil()
相對延時是指:vTaskDelay()開始執行到退出執行的時間固定
/* 相對延時函數 */ void vTaskDelay(const TickType_t xTicksToDelay) {BaseType_t xAlreadyYielded = pdFALSE;/* 延時時間大于0 */if(xTicksToDelay > (TickType_t)0U){configASSERT(uxSchedulerSuspended == 0);/* 調度器掛起 */vTaskSuspendAll();{traceTASK_DELAY();/* 將任務添加到延時列表 */prvAddCurrentTaskToDelayedList(xTicksToDelay, pdFALSE);}/* 解除調度器掛起,解除的時候可能請求調度 */xAlreadyYielded = xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}/* 解除調度器掛起的時候沒有請求調度 */if(xAlreadyYielded == pdFALSE){/* 請求切換任務 */portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();} }?
?
絕對延時是指:相鄰兩次vTaskDelayUntil()喚醒的時間固定
/* 絕對延時 */ void vTaskDelayUntil(TickType_t *const pxPreviousWakeTime, const TickType_t xTimeIncrement) {TickType_t xTimeToWake;BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;configASSERT(pxPreviousWakeTime);configASSERT((xTimeIncrement > 0U));configASSERT(uxSchedulerSuspended == 0);/* 掛起調度器 */vTaskSuspendAll();{const TickType_t xConstTickCount = xTickCount;/* 下一次任務喚醒時間 */xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;/* 前一次喚醒時間大于系統當前節拍,說明系統節拍已經溢出 */if(xConstTickCount < *pxPreviousWakeTime){/* 下一次喚醒時間小于前一次喚醒時間,說明下一次喚醒時間溢出 *//* 下一次喚醒時間大于系統當前節拍 *//* 綜上,下一次喚醒時間在當前系統節拍的后面 */if((xTimeToWake < *pxPreviousWakeTime) && (xTimeToWake > xConstTickCount)){/* 應該延時 */xShouldDelay = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}/* 系統節拍沒有溢出 */else{/* 下一次喚醒時間小于前一次喚醒時間,說明下一次喚醒時間溢出 *//* 或者下一次喚醒時間大于當前系統節拍 *//* 綜上,下一次喚醒時間在當前系統節拍的后面 */if((xTimeToWake < *pxPreviousWakeTime) || (xTimeToWake > xConstTickCount)){/* 應該延時 */xShouldDelay = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}/* 將下一次喚醒時間賦值給pxPreviousWakeTime,留作下一次使用 */*pxPreviousWakeTime = xTimeToWake;/* 需要延時 */if(xShouldDelay != pdFALSE){traceTASK_DELAY_UNTIL(xTimeToWake);/* 將任務添加到延時列表,延時時長為當前節拍到喚醒時間 */prvAddCurrentTaskToDelayedList(xTimeToWake - xConstTickCount, pdFALSE);}else{mtCOVERAGE_TEST_MARKER();}}/* 解除調度器掛起,解除的時候可能切換任務 */xAlreadyYielded = xTaskResumeAll();/* 解除調度器掛起的時候沒有切換任務 */if(xAlreadyYielded == pdFALSE){/* 請求切換任務 */portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();} }?
?
系統節拍采用32位計數,所以最終肯定會溢出,同樣喚醒時間也存在溢出。系統一共維護了兩個延時列表,分別是延時列表和溢出延時列表,兩條列表都是按照喚醒時間的從小到大進行排列。當喚醒時間溢出的時候,就不能和沒溢出的插入同一個列表了。系統將喚醒時間沒有溢出的任務放入延時列表,將喚醒時間溢出的任務放入溢出延時列表。
/* 將任務添加到延時列表 */ static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely) {TickType_t xTimeToWake;const TickType_t xConstTickCount = xTickCount;#if (INCLUDE_xTaskAbortDelay == 1){pxCurrentTCB->ucDelayAborted = pdFALSE;}#endif/* 當前任務肯定掛接在就緒列表中,將其移除。該就緒列表中沒有任何任務 */if(uxListRemove(&(pxCurrentTCB->xStateListItem)) == (UBaseType_t)0){/* 清除任務優先級記錄中的優先級 */portRESET_READY_PRIORITY(pxCurrentTCB->uxPriority, uxTopReadyPriority);}else{mtCOVERAGE_TEST_MARKER();}#if (INCLUDE_vTaskSuspend == 1){/* 延時時間為portMAX_DELAY并且允許無限期阻塞 */if((xTicksToWait == portMAX_DELAY) && (xCanBlockIndefinitely != pdFALSE)){/* 將任務掛接到掛起列表 */vListInsertEnd(&xSuspendedTaskList, &(pxCurrentTCB->xStateListItem));}else{/* 計算喚醒時間 */xTimeToWake = xConstTickCount + xTicksToWait;/* 設置狀態列表項值為喚醒時間 */listSET_LIST_ITEM_VALUE(&(pxCurrentTCB->xStateListItem), xTimeToWake);/* 喚醒時間小于當前節拍說明已經溢出了 */if(xTimeToWake < xConstTickCount){/* 將當前任務掛接到延時溢出列表 */vListInsert(pxOverflowDelayedTaskList, &(pxCurrentTCB->xStateListItem));}/* 喚醒時間大于當前節拍說明沒有溢出 */else{/* 將當前任務掛接到延時列表 */vListInsert(pxDelayedTaskList, &(pxCurrentTCB->xStateListItem));/* 當前任務喚醒時間,小于原先下一個解除阻塞任務的喚醒時間 */if(xTimeToWake < xNextTaskUnblockTime){/* 更新當前任務喚醒時間為下一個解除阻塞任務的喚醒時間 */xNextTaskUnblockTime = xTimeToWake;}else{mtCOVERAGE_TEST_MARKER();}}}}#else{xTimeToWake = xConstTickCount + xTicksToWait;listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );if( xTimeToWake < xConstTickCount ){vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else{vListInsert(pxDelayedTaskList, &(pxCurrentTCB->xStateListItem));if(xTimeToWake < xNextTaskUnblockTime){xNextTaskUnblockTime = xTimeToWake;}else{mtCOVERAGE_TEST_MARKER();}}(void)xCanBlockIndefinitely;}#endif }?
?
當前系統節拍到達最近的喚醒時間時,系統會將應該喚醒的任務從延時列表中清除,并掛接到就緒列表中。
/* 系統節拍加一 */ BaseType_t xTaskIncrementTick(void) {TCB_t *pxTCB;TickType_t xItemValue;BaseType_t xSwitchRequired = pdFALSE;traceTASK_INCREMENT_TICK(xTickCount);/* 調度器沒有被掛起 */if(uxSchedulerSuspended == (UBaseType_t)pdFALSE){/* 系統節拍計數器加一 */const TickType_t xConstTickCount = xTickCount + (TickType_t)1;xTickCount = xConstTickCount;/* 系統節拍溢出 */if(xConstTickCount == (TickType_t)0U){/* 切換延時列表,更新下一次解除阻塞的時間 */taskSWITCH_DELAYED_LISTS();}else{mtCOVERAGE_TEST_MARKER();}/* 處理延時列表中超時的任務 */if(xConstTickCount >= xNextTaskUnblockTime){for(;;){/* 如果延時列表已經為空,則將下一次解除時間設為最大 */if(listLIST_IS_EMPTY(pxDelayedTaskList) != pdFALSE){xNextTaskUnblockTime = portMAX_DELAY;break;}/* 延時列表不是空的 */else{/* 獲取延時列表中需要最先解除的任務 */pxTCB = listGET_OWNER_OF_HEAD_ENTRY(pxDelayedTaskList);xItemValue = listGET_LIST_ITEM_VALUE(&(pxTCB->xStateListItem));/* 最先需要解除的任務都沒有超時 */if(xConstTickCount < xItemValue){/* 更新下一次解除時間 */xNextTaskUnblockTime = xItemValue;break;}else{mtCOVERAGE_TEST_MARKER();}/* 將超時任務從延時列表中移除 */(void)uxListRemove(&(pxTCB->xStateListItem));/* 如果該任務被掛接到某個事件列表,還需要從事件列表中移除 */if(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem)) != NULL){(void)uxListRemove(&(pxTCB->xEventListItem));}else{mtCOVERAGE_TEST_MARKER();}/* 將任務加入就緒列表中 */prvAddTaskToReadyList(pxTCB);......}}}......}/* 調度器被掛起 */else{......}......return xSwitchRequired; }一旦系統節拍出現溢出,系統會對延時列表和溢出延時列表進行切換。由于系統節拍溢出,原來溢出延時列表變成了延時列表,而原來的延時列表里的任務在系統節拍溢出之后肯定已經全部被超時喚醒,之后這條原先的延時列表則被拿來作為溢出延時列表。
/* 切換延時列表 */ #define taskSWITCH_DELAYED_LISTS() \ { \List_t *pxTemp; \\configASSERT((listLIST_IS_EMPTY(pxDelayedTaskList))); /* 延時列表必須為空 */ \\/* 交換延時列表和溢出延時列表 */ \pxTemp = pxDelayedTaskList; \pxDelayedTaskList = pxOverflowDelayedTaskList; \pxOverflowDelayedTaskList = pxTemp; \\xNumOfOverflows++; /* 系統節拍溢出次數加一 */ \prvResetNextTaskUnblockTime(); /* 更新下一個要解除阻塞的時間 */ \ }?
總結
以上是生活随笔為你收集整理的FreeRTOS任务延时函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高效能码农的自我修养:5本书教你怎样科学
- 下一篇: 关于Python爬虫,一条高效的学习路径