目錄
之前的章節,FreeRTOS還沒有支持多優先級,只支持兩個任務互相切換,從本章開始,任務中我們開始加入優先級的功能。在FreeRTOS中,數字優先級越小,邏輯優先級也越小,這與隔壁的RT-Thread和μC/OS剛好相反。
1. 如何支持多優先級
就緒列表pxReadyTasksLists[ configMAX_PRIORITIES ]是一個數組,數組裏面存的是就緒任務的TCB(準確來說是TCB裏面的xStateListItem節點),數組的下標對應任務的優先級,優先級越低對應的數組下標越小。空閒任務的優先級最低,對應的是下標爲0的鏈表。圖 演示的是就緒列表中有兩個任務就緒,優先級分別爲1和2,其中空閒任務沒有畫出來,空閒任務自系統啓動後會一直就緒,因爲系統至少得保證有一個任務可以運行。
configMAX_PRIORITIES 爲任務優先級數量,若 配置爲 5,則優先級範圍【0,4】,其餘以此類推。
任務在創建的時候,會根據任務的優先級將任務插入到就緒列表不同的位置。相同優先級的任務插入到就緒列表裏面的同一條鏈表中,這就是我們下一章要講解的支持時間片輪轉。
pxCurrenTCB是一個全局的TCB指針,用於指向優先級最高的就緒任務的TCB,即當前正在運行的TCB。那麼我們要想讓任務支持優先級,即只要解決在任務切換(taskYIELD)的時候,讓pxCurrenTCB指向最高優先級的就緒任務的TCB就可以,前面的章節我們是手動地讓pxCurrenTCB在任務1、任務2和空閒任務中輪轉,現在我們要改成pxCurrenTCB在任務切換的時候指向最高優先級的就緒任務的TCB即可,那問題的關鍵就是:如果找到最高優先級的就緒任務的TCB。
FreeRTOS提供了兩套方法,一套是通用的,一套是根據特定的處理器優化過的,接下來我們重點講解下這兩個方法。
2. 查找最高優先級的就緒任務
尋找最高優先級的就緒任務相關代碼在task.c中定義
/* 查找最高優先級的就緒任務:通用方法 */
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
/* uxTopReadyPriority 存的是就緒任務的最高優先級 */
#define taskRECORD_READY_PRIORITY( uxPriority ) \
{ \
if( ( uxPriority ) > uxTopReadyPriority ) \
{ \
uxTopReadyPriority = ( uxPriority ); \
} \
} /* taskRECORD_READY_PRIORITY */
/*-----------------------------------------------------------*/
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority = uxTopReadyPriority; \
\
/* 尋找包含就緒任務的最高優先級的隊列 */ \
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \
{ \
--uxTopPriority; \
} \
\
/* 獲取優先級最高的就緒任務的TCB,然後更新到pxCurrentTCB */ \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
/* 更新uxTopReadyPriority */ \
uxTopReadyPriority = uxTopPriority; \
} /* taskSELECT_HIGHEST_PRIORITY_TASK */
/*-----------------------------------------------------------*/
/* 這兩個宏定義只有在選擇優化方法時才用,這裏定義爲空 */
#define taskRESET_READY_PRIORITY( uxPriority )
#define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority )
/* 查找最高優先級的就緒任務:根據處理器架構優化後的方法 */
#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
#define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority )
/*-----------------------------------------------------------*/
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
UBaseType_t uxTopPriority; \
\
/* 尋找最高優先級 */ \
portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \
/* 獲取優先級最高的就緒任務的TCB,然後更新到pxCurrentTCB */ \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
} /* taskSELECT_HIGHEST_PRIORITY_TASK() */
/*-----------------------------------------------------------*/
#if 0
#define taskRESET_READY_PRIORITY( uxPriority ) \
{ \
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \
{ \
portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \
} \
}
#else
#define taskRESET_READY_PRIORITY( uxPriority ) \
{ \
portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \
}
#endif
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
找最高優先級的就緒任務有兩種方法,具體由configUSE_PORT_OPTIMISED_TASK_SELECTION這個宏控制,定義爲0選擇通用方法,定義爲1選擇根據處理器優化的方法,該宏默認在portmacro.h中定義爲1,即使用優化過的方法。
2.1 通用方法(普適性)
taskRECORD_READY_PRIORITY()用於更新uxTopReadyPriority的值。uxTopReadyPriority是一個在task.c中定義的靜態變量,用於表示創建的任務的最高優先級,默認初始化爲0,即空閒任務的優先級.
taskSELECT_HIGHEST_PRIORITY_TASK()用於尋找優先級最高的就緒任務,實質就是更新uxTopReadyPriority和pxCurrentTCB的值。
從最高優先級對應的就緒列表數組下標開始尋找當前鏈表下是否有任務存在,如果沒有,則uxTopPriority減一操作,繼續尋找下一個優先級對應的鏈表中是否有任務存在,如果有則跳出while循環,表示找到了最高優先級的就緒任務。之所以可以採用從最高優先級往下搜索,是因爲任務的優先級與就緒列表的下標是一一對應的,優先級越高,對應的就緒列表數組的下標越大。
2.2 優化方法(專業性)
優化的方法,這得益於Cortex-M內核有一個計算前導零的指令CLZ,所謂前導零就是計算一個變量(Cortex-M內核單片機的變量爲32位)從高位開始第一次出現1的位的前面的零的個數。
比如:一個32位的變量uxTopReadyPriority,其位0、位24和位25均置1,其餘位爲0,具體見。那麼使用前導零指令__CLZ (uxTopReadyPriority)可以很快的計算出uxTopReadyPriority的前導零的個數爲6。
如果uxTopReadyPriority的每個位號對應的是任務的優先級,任務就緒時,則將對應的位置1,反之則清零。那麼圖 10-2就表示優先級0、優先級24和優先級25這三個任務就緒,其中優先級爲25的任務優先級最高。利用前導零計算指令可以很快計算出就緒任務中的最高優先級爲:( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) ) = ( 31UL - ( uint32_t ) 6 ) = 25。
故此,正常情況下,對Cotex-M的內核,優先級數最好不要超過32,這樣處理速度相對使用通用方法要快不少。
3. 代碼修改
參照源碼,參考工程例程,很好理解,不做贅述