2012年5月7日 星期一

FreeRTOS程式碼分析 -- 1

此篇心得參考 FreeRTOS_cn.pdf FreeRTOS原始程式整理而成。


資料結構

1.State Machine
FreeRTOS中,每個任務(task)的狀態機有四種狀態,Suspended -> Ready -> Running -> IDLE其實作方式將任務依據其狀態,分別放置於不同的queue PRIVILEGED_DATA static xList pxReadyTasksLists[ configMAX_PRIORITIES ];  /*< Prioritised ready tasks. */
 PRIVILEGED_DATA static xList xDelayedTaskList1;        /*< Delayed tasks. */ PRIVILEGED_DATA static xList xDelayedTaskList2;        /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ PRIVILEGED_DATA static xList * volatile pxDelayedTaskList ;     /*< Points to the delayed task list currently being used. */ PRIVILEGED_DATA static xList * volatile pxOverflowDelayedTaskList;   /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ PRIVILEGED_DATA static xList xPendingReadyList;        /*< Tasks that have been readied while the scheduler was suspended.  They will be moved to the ready queue when the scheduler is resumed. */Suspended(block) state:將任務加入pxDelayedTaskList


註:但是在實作中,對於負責工作排程的Task,只會回應三種狀態值(參考xTaskGetSchedulerState()#define taskSCHEDULER_NOT_STARTED 0#define taskSCHEDULER_RUNNING  1#define taskSCHEDULER_SUSPENDED 2

2. Task Control Block
每個任務都會有一個對應的TCB,用來儲存與任務有關的資訊,例如:任務名稱,執行優先級,在ready/blocked queue中的對應位置。

3. Queue Management
  1. 一個任務可以創建/刪除Queue,並進行Queue的讀寫。
  2. 當多個 Task 需要同時存取 queue 的資料時,Priority高的會先完成,若Priority相同,則等待最久的會先完成。
  3. 預設的 FreeRTOS QueueCreate 會使用 pvPortMalloc。若要減少FreeRTOS程式碼大小,可以改寫malloc,讓程式碼不需要使用記憶體配置,   將所有用到pvPortMalloc 改成直接傳入固定的 buffer


運行的基本規則


1.工作排程
1. Scheduler context switch 的作法
  1. 參考TaskYield(),此函數會產生PendSV中斷,對應的中斷處理程序則是xPortPendSVHandler()
  2.  xPortPendSVHandler(): 儲存 Stack Pointer(psp) current TCB
  3. xPortPendSVHandler(): 呼叫 vTaskSwitchContext(),找出 ready queue highest priority item當成欲執行的Task
  4. xPortPendSVHandler(): 將要執行的 Task 放至 pxCurrentTCB
  5. xPortPendSVHandler(): 此時 pxCurrentTCB 已經指向下一個要執行的 Task
  6. "bx r14" 離開 xPortPendSVHandler()
2. FreeRTOS 要進行scheduling時。其作法參考 vTaskStartScheduler()
  1.  建立一個任務,此任務名稱為 prvIdleTask,此Task具有最低的優先級別 (priority=0),因此不會妨礙其他高優先級的任務。一般可以在Idle Task加上以下功能:CPU設定為低功耗模式,進入省電模式。根據Idel task獲得執行的時間,來判斷系統是否忙碌。
  2. prvSetupTimerInterrupt(),設定 SysTick Interrupt 產生的時間為 ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL,並啟動timer
  3. 產生 SVC 中斷以運行開機後的第一個Task,即IdleTask
  4. 之後,每次當timer expired發生 SysTick interrupt,便會呼叫xPortSysTickHandler(),強制產生PendSV中斷,進行 context switch,進行上述 2.c~2.g 的工作,並且累加xTickCount

2.FreeRTOSDelay()
1.     vTaskDelay() 
設定任務從調用TaskDelay()開始,到離開suspended狀態(進入ReadyState),共需要多少的時間心跳週期,xTimeToWake = xTickCount(此時系統的時間) + xTicksToDelay
2.     vTaskDelayUntil()  
設定任務從上一次離開suspended狀態,到此次離開suspended狀態(進入ReadyState),共需要多少的心跳週期,xTmeToWake = *pxPreviousWakeTime(上一次離開suspended狀態的時間) + xTimeIncrement
3.     此兩個函數主要差異點在於Delay時間的計算,若是需要一個固定執行週期,使用 vTaskDelayUntil() 可以獲得較準確的延遲時間。

參考資料
2.     FreeRTOS程式碼