FreeRTOS 任務管理
任務函數
- 我們可以按照以上方式定義任務函數,可以更改函數名、變量名。
- 每一個任務都有一個進入點,任務是一個死循環,一旦啓動不會自己停止。
- 注意,任務函數是沒有返回值的,而且一定不能有return。任務可以被顯式刪除。
- 一個任務函數可以創建任意個的任務
任務函數定義如下:
API
創建任務 (Creating Tasks)
The xTaskCreate()
示例1 任務創建
*pvTaskCode 是一個函數指針,也就是任務函數名稱。比如說,創建一個名爲vTask1的函數,注意,一定是個死循環!
void vTask1(void *pvParameters)
{
for(;;)
{
vPrintString( "Hello world!" );
}
}
void vTask2(void *pvParameters)
{
for(;;)
{
// 此處省略
}
}
在main()函數中創建任務,
int main( void )
{
xTaskCreate( vTask1, // 函數指針
"Task 1", // 任務名稱,實際作用不大,可用來Debug
1000, // 堆棧大小,根據需要調整
NULL, // 需要傳遞到任務的參數,沒有就設置爲NULL
1, // 任務優先級
NULL); // 任務句柄
xTaskCreate(vTask2,"Task 2",1000,NULL,1,NULL);
// xTaskCreate()的返回值是 pdPASS/pdFAIL
vTaskStartScheduler();
for(;;); // 我們的main()函數不能返回,開啓調度器後,FreeRTOS會接管系統。
}
如下圖所示:
我們也可以直接在一個任務中創建另一個任務:
示例 2 參數傳遞
我們也可以通過參數傳遞實現兩個不同的任務用同一個任務函數
任務函數:
創建任務:
任務優先級 (Task Priorities)
在創建任務時,FreeRTOS會根據xTaskCreate()中定義的初始值,確定任務優先級。在開啓調度之後,任務的優先級也可以通過vTaskPrioritySet()函數重新設定。
任務的最大優先級定義爲configMAX_PRIORITIES,最小優先級是0,所以選取 (configMAX_PRIORITIES – 1)範圍內的任意一個優先級都是被允許的。而且不同的任務可以擁有相同的優先級。當優先級相同時,調度器將交替執行任務。
FreeRTOS調度器允許以兩種方式調度。
- 通用方式 Generic Method
在這種情況下,FreeRTOS不限制最大優先級的數值,但是還是建議將最大優先級數設置在“夠用”的數值上,減少內存消耗。
可以通過將configUSE_PORT_OPTIMISED_TASK_SELECTION設置爲0啓用這個方式。 - 根據架構優化方式 Architecture Optimized Method
這個方式比通用方式要快,因爲它是用來部分彙編代碼編寫。
在這種情況下,configMAX_PRIORITIES不能超過32,同樣的,建議將最大優先級數設置在“夠用”的數值上,減少內存消耗。
可以通過將configUSE_PORT_OPTIMISED_TASK_SELECTION設置爲1啓用這個模式。但是,並不是所有的FreeRTOS接口都支持這個模式。
時間測量和滴答中斷 (Time Measurement and the Tick Interrupt)
時間片 Time Slice:
時間片作爲最小的執行時間也叫滴答週期(Tick Period),可以通過configTICK_RATE_HZ設置。
爲了選擇下個需要運行的任務,調度器在每個時間片結束時都會執行調度,通過滴答中斷實現。如圖所示:
可以看到,黑色箭頭表示的是,任務結束->觸發滴答中斷->進行調度 的過程。
FreeRTOS通常以滴答週期作爲單位,表示爲 “xx ticks”, 可以通過pdMS_TO_TICKS()函數進行ms到ticks的單位換算。
注意,一般不建議直接用數值指定ticks,考慮到可移植性,使用pdMS_TO_TICKS()函數能避免很多麻煩。
示例
由於Task2的優先級比Task1高,所以只有Task2能執行
非運行狀態的情況 Expanding the ‘Not Running’ State
阻塞態 Blocked State
阻塞態有兩種可能情況:
- 跟時間相關的事件(比如說,延時)
- 同步事件(比如說,任務需要等待數據等)
在FreeRTOS中,隊列,信號量,互斥量,事件組,任務通知都會產生同步事件
掛起態 Suspended State
掛起態的任務不會被調度,唯一可以導致掛起的操作是調用vTaskSuspend()函數,從掛起態跳出可以調用vTaskResume()和xTaskResumeFromISR(),大部分的應用不需要掛起態。
就緒態 Ready State
就緒態的任務處於等待調度上CPU的狀態
狀態機模型
延時函數 vTaskDelay()
示例程序:
vTaskDelay() 的延遲時間是相對的,他是 本次調用到再次調用 所經過的時間。所以,前提是需要調用vTaskDelay() 纔可以,如果這段時間中有別的任務搶佔CPU,就無法保證延遲是固定頻率的。
圖解:
延時函數 vTaskDelayUntil()
vTaskDelayUntil() 的延時時間是個固定值,參數代表了精確的延時時間。這樣,延時的執行頻率是固定的。在想讓任務以固定頻率執行時,應使用這種方式。
示例程序:
圖解:
- t1時刻,Periodic任務剛執行完成,xLastWakeTime記錄下此時的tick count(tick count是從調度開始不斷遞增的一個計數器);Continuous1開始運行
- t5時刻,調度器發現Periodic任務的延時時間已經到了,所以將其設置爲就緒態。同時,由於此任務優先級最高,於是開始執行。執行完成後切換到Continuous1任務。
空閒任務和空閒鉤子 The Idle Task and the Idle Task Hook
空閒任務
空閒任務在開啓調度時會自動被系統創建,空閒任務擁有最低優先級
空閒鉤子 Idle Task Hook Functions
空閒鉤子會被空閒任務自動調用,它常應用於:
- 運行連續處理的功能,後臺功能,比如說狀態指示燈
- 檢測處理器餘量
- 低功耗運行
注意:
- 空閒鉤子不能被阻塞或掛起
- 如果使用vTaskDelete()函數,鉤子函數必須在合理的時間內返回到調用。
示例:
修改任務優先級 Changing the Priority of a Task
設置優先級 vTaskPrioritySet()
如果第一個參數設置爲NULL,表明修改自身優先級
獲取優先級 uxTaskPriorityGet()
示例
圖解:
- Task1提高了Task2優先級,此時Task2搶佔CPU,開始運行
- Task2運行完降低自己的優先級,此時Task1搶佔CPU,開始運行
刪除任務 Deleting a Task
vTaskDelete()
示例程序:
圖解
Task1創建Task2任務,Task2刪除自己,just it~