FreeRTOS中的任務調度
背景介紹
FreeRTOS是一種輕量級的實時操作系統,被廣泛應用于嵌入式系統中。它提供了多任務管理功能,包括任務創建、任務調度和任務切換。FreeRTOS的調度器根據任務的優先級和狀態自動調度任務,確保系統資源的有效利用和實時性。
在M4處理器上,FreeRTOS能夠實現任務間的自動調度。調度器采用基于優先級的搶占式調度算法,確保系統總是運行最高優先級且處于就緒狀態的任務。如果有多個任務具有相同的優先級,調度器會采用時間片輪轉的方法在這些任務之間切換。
主動調度
盡管FreeRTOS的調度器通常會自動管理任務的調度,但有時我們需要手動干預任務調度。這可以通過以下幾種方式實現:
1. 任務切換
通過調用任務切換函數,可以強制調度器立即進行任務切換。
taskYIELD(); // 手動觸發任務切換
2. 優先級調整
通過調整任務的優先級,可以影響調度器的行為,立即切換到新的最高優先級任務。
vTaskPrioritySet(xTaskHandle, uxNewPriority); // 改變任務優先級
3. 任務通知
通過任務通知機制,可以實現任務之間的同步和通信,從而影響調度器的行為。
xTaskNotifyGive(xTaskHandle); // 通知任務
4. 中斷服務例程(ISR)中的調度
在中斷服務例程中,可以通過設置上下文切換請求標志來強制任務調度。
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // 在ISR中觸發任務調度
示例代碼
以下是一些示例代碼,展示如何通過上述方法主動調度任務。
任務切換
void vTask1(void *pvParameters) {for(;;) {// 任務代碼taskYIELD(); // 主動觸發任務切換}
}void vTask2(void *pvParameters) {for(;;) {// 任務代碼}
}
優先級調整
void vTask1(void *pvParameters) {for(;;) {// 任務代碼vTaskPrioritySet(NULL, uxNewPriority); // 改變自身優先級}
}void vTask2(void *pvParameters) {for(;;) {// 任務代碼}
}
任務通知
void vTask1(void *pvParameters) {for(;;) {// 等待通知ulTaskNotifyTake(pdTRUE, portMAX_DELAY);// 被通知后繼續執行}
}void vTask2(void *pvParameters) {for(;;) {// 任務代碼xTaskNotifyGive(xTask1Handle); // 通知任務1}
}
中斷服務例程中的調度
void vISRHandler(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;// 處理中斷// ...// 觸發任務調度portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
優先級說明
在FreeRTOS中,優先級的值越大,優先級越高。FreeRTOS使用整數值來表示任務的優先級,值越大表示優先級越高。任務的優先級決定了調度器選擇哪個任務來運行。當有多個任務處于就緒狀態時,調度器會選擇優先級最高的任務來運行。
示例說明
例如,如果你創建了三個任務,并設置它們的優先級如下:
- 任務A:優先級為1
- 任務B:優先級為2
- 任務C:優先級為3
在這種情況下,任務C的優先級最高,因此調度器會首先運行任務C。如果任務C阻塞或進入等待狀態,調度器會選擇下一個優先級最高的任務,即任務B來運行。
創建任務時設置優先級
在創建任務時,你可以通過uxPriority參數來設置任務的優先級。以下是一個示例代碼:
#include "FreeRTOS.h"
#include "task.h"void vTaskA(void *pvParameters) {for(;;) {// 任務A的代碼}
}void vTaskB(void *pvParameters) {for(;;) {// 任務B的代碼}
}void vTaskC(void *pvParameters) {for(;;) {// 任務C的代碼}
}int main(void) {// 創建任務A,優先級為1xTaskCreate(vTaskA, "Task A", 1000, NULL, 1, NULL);// 創建任務B,優先級為2xTaskCreate(vTaskB, "Task B", 1000, NULL, 2, NULL);// 創建任務C,優先級為3xTaskCreate(vTaskC, "Task C", 1000, NULL, 3, NULL);// 啟動調度器vTaskStartScheduler();// 由于調度器已經啟動,通常不會運行到這里for(;;);
}