FreeRTOS——任務延時列表的實現

在學習本章之前,爲了實現任務的阻塞延時,在任務控制塊中內置了一個延時變量xTicksToDelay。當每次任務需要延時時,就初始化xTicksToDelay需要延時的時間,然後將任務掛起,這裏所說的掛起只是將任務在優先級位圖表uxTopReadyPriority中對應的位清零,並不會將任務從就緒列表中刪除。當每次時基中斷(SysTick中斷)來臨時,就掃描就緒列表中每個任務的xTicksToDelay,如果xTicksToDelay大於0則遞減一次,然後判斷xTicksToDelay是否爲0,如果爲0則表示延時時間到,將該任務就緒(即將任務在優先級位圖表uxTopReadyPriority中對應的位置位),然後進行任務切換。這種延時的缺點是,在每個時基中斷中需要對所有的任務都掃描一遍,費時,優點是易理解。

一、任務延時列表的工作原理

在FreeRTOS中,有一個任務延時列表(實際有兩個,但作用一樣),當任務需要延時時,先將任務掛起,即先將任務從就緒列表中刪除,然後插入到任務延時列表,同時更新下一個任務的解鎖時刻變量:xNextTaskUnblockTime 的值。
xNextTaskUnblockTime 的值等於系統時基計數器的值xTickCount 加上任務需要延時的值xTicksToDelay。當系統時基計數器xTickCount 的值等於xNextTaskUnblockTime 的值時,就表示有任務延時到期了,需要將該任務就緒。
任務延時列表維護着一條雙向鏈表,每個節點代表了正在延時的任務,節點按照延時時間大小做升序排列。當每次時基中斷來臨時,就拿系統時基計數器的值xTickCount 和下一個任務的解鎖時刻變量xNextTaskUnblockTime 的值比較,如果相等,則表示有任務延時到期,需要將該任務就緒,否則只是單純地更新時基計數器xTickCount 的值,然後進行任務切換。

二、FreeRTOS中實現任務延時列表

1、定義任務延時列表

static List_t xDelayedTaskList1; (1)
static List_t xDelayedTaskList2; (2)
static List_t * volatile pxDelayedTaskList; (3)
static List_t * volatile pxOverflowDelayedTaskList; (4)

(1)(2):定義兩個任務延時列表,當系統時基計數器xTickCount 沒有溢出時,用一條列表,當xTickCount 移除後,用另外一條鏈表。
(3):任務延時列表指針,指向xTickCount 沒有溢出時使用的那條列表
(4):任務延時列表指針,指向xTickCount 溢出時使用的那條列表

2、任務延時列表初始化

任務延時列表屬於任務列表的一種,在prvInitialiseTaskLists()函數中初始化。

/* 初始化任務相關的列表 */
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;
}

3、定義xNextTaskUnblockTime

xNextTaskUnblockTime 是一個在 task.c 中定義的靜態變量,用於表示下一個任務的解鎖時刻。 xNextTaskUnblockTime 的值等於系統時基計數器的值 xTickCount 加上任務需要延時值 xTicksToDelay。當系統時基計數器 xTickCount 的值與 xNextTaskUnblockTime 相等時,就表示有任務延時到期了,需要將該任務就緒。

4、定義xNextTaskUnblockTime

xNextTaskUnblockTime 在 vTaskStartScheduler()函數中初始化爲portMAX_DELAY(portMAX_DELAY 是一個 portmacro.h 中定義的宏,默認爲 0xffffffffUL) 。

void vTaskStartScheduler( void )
{
 /*==================創建空閒任務 start=========================*/
 TCB_t *pxIdleTaskTCBBuffer = NULL;
 StackType_t *pxIdleTaskStackBuffer = NULL;
 uint32_t ulIdleTaskStackSize;

 /* 獲取空閒任務的內存:任務棧和任務 TCB */
 vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer,
								&pxIdleTaskStackBuffer,
								&ulIdleTaskStackSize );

 xIdleTaskHandle =
 xTaskCreateStatic( (TaskFunction_t)prvIdleTask,
					(char *)"IDLE",
					(uint32_t)ulIdleTaskStackSize ,
					(void *) NULL,
					(UBaseType_t) tskIDLE_PRIORITY,
					(StackType_t *)pxIdleTaskStackBuffer,
					(TCB_t *)pxIdleTaskTCBBuffer );
 /*======================創建空閒任務 end===================*/

 xNextTaskUnblockTime = portMAX_DELAY;

 xTickCount = ( TickType_t ) 0U;

 /* 啓動調度器 */
 if ( xPortStartScheduler() != pdFALSE )
 {
 /* 調度器啓動成功,則不會返回,即不會來到這裏 */
 }
}

參考:[野火®]《FreeRTOS 內核實現與應用開發實戰—基於STM32》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章