????????在《劉火良FreeRTOS內核實現與應用學習之6——多優先級》的基礎上:關鍵是添加了全局變量:xNextTaskUnblockTime? ,與延時列表(xDelayedTaskList1、xDelayedTaskList2)來高效率的實現延時。
? ? ? ? 以前需要在掃描就緒列表中所有任務的xTicksToDelay值,進行任務就緒與否的操作;現在修改之后只需根據xNextTaskUnblockTime 值判斷延時列表中的任務是否就緒,進行相應的操作;
重要數據結構及數據
0.? 全局變量? xNextTaskUnblockTime?? ??? ?
位置:在task.c文件中定義;靜態變量;
static volatile TickType_t xNextTaskUnblockTime?? ??? ?= ( TickType_t ) 0U;
1.? 全局變量 任務延時列表
位置:在task.c文件中定義;靜態變量;
static List_t xDelayedTaskList1;? ?//xTickCount沒有溢出時,使用一個列表
static List_t xDelayedTaskList2;? ?//xTickCount? ? ? ?溢出時,使用另外一個列表
static List_t * volatile pxDelayedTaskList;???//xTickCount沒有溢出時,使用一個列表
static List_t * volatile pxOverflowDelayedTaskList;? ?//xTickCount? ? ? ?溢出時,使用另外一個列表
a. 列表中的每一個節點代表了正在延時的任務;
b. 且節點按照延時時間大小做升序排列;
c. 當時基中斷(SysTick中斷)來臨時,就用系統時基計數器的值xTickCount 與xNextTaskUnblockTime? ?進行比較:相等則任務就緒;否則,更新系統時基計數器xTickCount,任務切換;
有關任務操作重要函數
1.? 任務延時列表初始化
/* 初始化任務相關的列表 */
void prvInitialiseTaskLists( void )
{
? ? UBaseType_t uxPriority;
? ??
? ? /* 初始化就緒列表 */
? ? for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
?? ?{
?? ??? ?vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
?? ?}
? ??
? ? vListInitialise( &xDelayedTaskList1 );
?? ?vListInitialise( &xDelayedTaskList2 );
? ??
? ? pxDelayedTaskList = &xDelayedTaskList1;
?? ?pxOverflowDelayedTaskList = &xDelayedTaskList2;
}
2. 修改延時函數
void vTaskDelay( const TickType_t xTicksToDelay )
{
? ? TCB_t *pxTCB = NULL;
? ??
? ? /* 獲取當前任務的TCB */
? ? pxTCB = pxCurrentTCB;
? ??
? ? /* 設置延時時間 */
? ? //pxTCB->xTicksToDelay = xTicksToDelay;
? ??
? ? /* 將任務插入到延時列表 */
? ? prvAddCurrentTaskToDelayedList( xTicksToDelay );
? ??
? ? /* 任務切換 */
? ? taskYIELD();
}
實現將任務插入延時列表;
在?prvAddCurrentTaskToDelayedList函數中實現
a. 把當前任務從就緒列表中移除;
? ? /* 將任務從就緒列表中移除 */
?? ?if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
?? ?{
?? ??? ?/* 將任務在優先級位圖中對應的位清除 */
? ? ? ? portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
?? ?}
b. 計算xNextTaskUnblockTime? 值:
? ? /* 計算延時到期時,系統時基計數器xTickCount的值是多少 */
? ? xTimeToWake= xConstTickCount + xTicksToWait;
c. 把延時任務插入延時列表(xDelayedTaskList1 或者 xDelayedTaskList2)中
i. 由于《節點按照延時時間大小做升序排列》,在插入前需要設置節點的xItemValue值:用于幫助節點做順序排列;
?/* 將延時到期的值設置為節點的排序值 */
? ? listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );
ii. 根據剛才計算的?xTimeToWake是否溢出,決定把延時任務是放到延時列表(非溢出xDelayedTaskList1 或者 溢出xDelayedTaskList2)中;
? ? /* 溢出 */
? ? if( xTimeToWake < xConstTickCount )
? ? {
? ? ? ? vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
? ? }
? ? else /* 沒有溢出 */
? ? {? ? ? ? vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
? ? ? ? /* 更新下一個任務解鎖時刻變量xNextTaskUnblockTime的值 */
? ? ? ? if( xTimeToWake < xNextTaskUnblockTime )
? ? ? ? {
? ? ? ? ? ? xNextTaskUnblockTime = xTimeToWake;
? ? ? ? }
? ? }
?iii. 更新xNextTaskUnblockTime值:
? ? ? ? /* 更新下一個任務解鎖時刻變量xNextTaskUnblockTime的值 */
? ? ? ? if( xTimeToWake < xNextTaskUnblockTime )
? ? ? ? {
? ? ? ? ? ? xNextTaskUnblockTime = xTimeToWake;
? ? ? ? }
重要函數 :prvAddCurrentTaskToDelayedList
static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait )
{TickType_t xTimeToWake;/* 獲取系統時基計數器xTickCount的值 */const TickType_t xConstTickCount = xTickCount;/* 將任務從就緒列表中移除 */if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){/* 將任務在優先級位圖中對應的位清除 */portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );}/* 計算延時到期時,系統時基計數器xTickCount的值是多少 */xTimeToWake = xConstTickCount + xTicksToWait;/* 將延時到期的值設置為節點的排序值 */listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );/* 溢出 */if( xTimeToWake < xConstTickCount ){vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else /* 沒有溢出 */{vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );/* 更新下一個任務解鎖時刻變量xNextTaskUnblockTime的值 */if( xTimeToWake < xNextTaskUnblockTime ){xNextTaskUnblockTime = xTimeToWake;}}
}
3. 修改xTaskIncrementTick函數
實現如下功能:
a. 更新xTickCount 值;為何不直接加一?
?? ?const TickType_t xConstTickCount = xTickCount + 1;
?? ?xTickCount = xConstTickCount;
b. 如果 xConstTickCount溢出,則切換延時列表;
?? ?/* 如果xConstTickCount溢出,則切換延時列表 */
?? ?if( xConstTickCount == ( TickType_t ) 0U )
?? ?{
?? ??? ?taskSWITCH_DELAYED_LISTS();
?? ?}
重要宏代碼?taskSWITCH_DELAYED_LISTS
/*?
?* 當系統時基計數器溢出的時候,延時列表pxDelayedTaskList 和
?* pxOverflowDelayedTaskList要互相切換
?*/
#define taskSWITCH_DELAYED_LISTS()\
{\
?? ?List_t *pxTemp;\
?? ?pxTemp = pxDelayedTaskList;\
?? ?pxDelayedTaskList = pxOverflowDelayedTaskList;\
?? ?pxOverflowDelayedTaskList = pxTemp;\
?? ?xNumOfOverflows++;\
?? ?prvResetNextTaskUnblockTime();\
}
當前的延時列表指針切換到溢出延時列表中;
?c. 最近的延時任務延時到期:
直接通過比較xConstTickCount與xNextTaskUnblockTime,大于則進去看誰到期了,無需遍歷所有的;
/* 最近的延時任務延時到期 */
?? ?if( xConstTickCount >= xNextTaskUnblockTime )
?? ?{
?? ??? ?for( ;; )
?? ??? ?{
?? ??? ??? ?if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
?? ??? ??? ?{
?? ??? ??? ??? ?/* 延時列表為空,設置xNextTaskUnblockTime為可能的最大值 */
?? ??? ??? ??? ?xNextTaskUnblockTime = portMAX_DELAY;
?? ??? ??? ??? ?break;
?? ??? ??? ?}
?? ??? ??? ?else /* 延時列表不為空 */
?? ??? ??? ?{
?? ??? ??? ??? ?pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
?? ??? ??? ??? ?xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );?? ??? ??? ??? ?/* 直到將延時列表中所有延時到期的任務移除才跳出for循環 */
? ? ? ? ? ? ? ? if( xConstTickCount < xItemValue )
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?xNextTaskUnblockTime = xItemValue;
?? ??? ??? ??? ??? ?break;
?? ??? ??? ??? ?}?? ??? ??? ??? ?/* 將任務從延時列表移除,消除等待狀態 */
?? ??? ??? ??? ?( void ) uxListRemove( &( pxTCB->xStateListItem ) );?? ??? ??? ??? ?/* 將解除等待的任務添加到就緒列表 */
?? ??? ??? ??? ?prvAddTaskToReadyList( pxTCB );
?? ??? ??? ?}
?? ??? ?}
?? ?}/* xConstTickCount >= xNextTaskUnblockTime */
代碼框架為死循環<用于部分遍歷延時到期任務>,設置條件跳出;
i.? 根據列表中的節點計數器判斷,延時列表是否為空,空則跳出;
ii. 編列列表中的任務節點的xItemValue(節點此值是升序的),若xConstTickCount < xItemValue:?表示此任務延時未到,跳出死循環;
若xConstTickCount > xItemValue:?表示此任務延時到,進行如下操作:
- 從當前延時任務從延時列表中移除;
- 將當前延時任務加到就緒列表中;
d. 任務切換;
xTaskIncrementTick函數由時基函數調用:
時基函數SysTick中斷服務函數:xPortSysTickHandler
位置:在port.c文件中定義;
/*
*************************************************************************
* SysTick中斷服務函數
*************************************************************************
*/
void xPortSysTickHandler( void )
{/* 關中斷 */vPortRaiseBASEPRI();/* 更新系統時基 */xTaskIncrementTick();/* 開中斷 */vPortClearBASEPRIFromISR();
}