任務優先級和Tick
????????在FreeRTOS中,任務的優先級和Tick是兩個關鍵的概念,它們直接影響任務的調度和執行。
任務優先級
????????每個任務都被分配一個優先級,用于決定任務在系統中的調度順序。
????????優先級是一個無符號整數,通常從0開始,數值越小,優先級越高。最高優先級是0,最低是configMAX_PRIORITIES - 1。
????????優先級的取值范圍是:0~(configMAX_PRIORITIES – 1),數值越大優先級越高。
????????FreeRTOS的調度器可以使用2種方法來快速找出優先級最高的、可以運行的任務。使用不同的方法時,configMAX_PRIORITIES 的取值有所不同。
(1)通用方法
????????使用C函數實現,對所有的架構都是同樣的代碼。對configMAX_PRIORITIES的取值沒有限制。但是configMAX_PRIORITIES的取值還是盡量小,因為取值越大越浪費內存,也浪費時間。configUSE_PORT_OPTIMISED_TASK_SELECTION被定義為0、或者未定義時,使用此方法。
(2)架構相關的優化的方法
????????架構相關的匯編指令,可以從一個32位的數里快速地找出為1的最高位。使用這些指令,可以快速找出優先級最高的、可以運行的任務。使用這種方法時,configMAX_PRIORITIES的取值不能超過32。configUSE_PORT_OPTIMISED_TASK_SELECTION被定義為1時,使用此方法。
????????在FreeRTOS中,優先級為0的任務是IDLE任務,用于在沒有其他任務執行時占用CPU。
????????任務的創建時通過指定參數設置任務的優先級,如下所示:
xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &Task1_Handle);
Tick
????????Tick是FreeRTOS內核用于計時和任務調度的基本時間單元。
????????FreeRTOS通過硬件定時器或者軟件定時器,每隔一定時間(即Tick間隔)產生一次中斷。
????????Tick的間隔由configTICK_RATE_HZ定義,它表示每秒鐘產生的Tick數。
關系
????????任務的調度是基于優先級的,具有更高優先級的任務將在具有較低優先級的任務之前執行。
????????Tick間隔決定了系統時鐘的精度,同時也影響了任務的延時和時間控制。
????????任務在等待一定時間或進行延時時,使用vTaskDelay()等函數,參數是以Tick為單位的時間。
vTaskDelay(100 / portTICK_PERIOD_MS); // 暫停任務100ms
????????任務的優先級和Tick的概念結合在一起,形成了FreeRTOS中任務調度和時間控制的基礎。通過調整任務的優先級和配置Tick的間隔,可以靈活地控制系統中任務的執行順序和時間行為。
????????以下是一個基于FreeRTOS的STM32F103芯片的簡單優先級實驗案例代碼。該例子創建了兩個任務,分別以不同的優先級運行,以演示優先級如何影響任務的調度。
#include "stm32f1xx.h"
#include "FreeRTOS.h"
#include "task.h"// 函數原型
void SystemClock_Config(void);
static void MX_GPIO_Init(void);// 任務函數
TaskHandle_t Task1_Handle, Task2_Handle;void vTask1(void *pvParameters) {while (1) {// 任務1的處理邏輯HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切換LED狀態vTaskDelay(1000 / portTICK_PERIOD_MS); // 每隔1秒執行一次}
}void vTask2(void *pvParameters) {while (1) {// 任務2的處理邏輯vTaskDelay(2000 / portTICK_PERIOD_MS); // 每隔2秒執行一次}
}int main(void) {// 硬件初始化HAL_Init();SystemClock_Config();MX_GPIO_Init();// 創建任務1(優先級1)xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1, &Task1_Handle);// 創建任務2(優先級2)xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2, &Task2_Handle);// 啟動調度器vTaskStartScheduler();// 代碼不應該運行到這里,如果運行到這里說明發生了錯誤while (1) {// 處理錯誤}
}// 系統時鐘配置
void SystemClock_Config(void) {
}// GPIO初始化
static void MX_GPIO_Init(void) {
}
????????在這個例子中,任務1和任務2分別在不同的時間間隔內切換LED的狀態。任務1的優先級較高,它每隔1秒執行一次,而任務2的優先級較低,它每隔2秒執行一次。
任務狀態
????????在FreeRTOS中,較高優先級的任務將優先于較低優先級的任務執行。任務優先級是通過任務創建時指定的參數來設置的(在xTaskCreate函數的第5個參數)。在這個案例中,任務1的優先級為1,任務2的優先級為2。
????????FreeRTOS中的任務可以處于不同的狀態,這些狀態反映了任務在系統中的執行階段。任務的狀態是通過eTaskState枚舉類型表示的,定義在task.h頭文件中。以下是任務可能的狀態:
????????eRunning(運行中): 任務正在執行。這是任務處于活動狀態的時候。
????????eReady(就緒): 任務已經準備好執行,但由于有其他高優先級任務在運行,該任務暫時沒有被調度。
????????eBlocked(阻塞): 任務由于等待某些事件而被阻塞,例如等待定時器、消息隊列、信號量等。
????????eSuspended(掛起): 任務被顯式地掛起,不再參與調度。
????????eDeleted(已刪除): 任務已被刪除,但其資源尚未被釋放。
????????eInvalid(無效): 無效狀態,通常用于表示錯誤或未知狀態。
你可以使用eTaskGetState函數來獲取任務的當前狀態。以下是一個示例代碼:
#include "FreeRTOS.h"
#include "task.h"TaskHandle_t xTaskHandle;void vTaskFunction(void *pvParameters) {while (1) {// 任務的處理邏輯}
}int main(void) {// 創建任務xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);// 啟動調度器vTaskStartScheduler();// 代碼不應該運行到這里,如果運行到這里說明發生了錯誤while (1) {// 處理錯誤}
}void vApplicationIdleHook(void) {// 空閑鉤子函數eTaskState taskState = eTaskGetState(xTaskHandle);switch (taskState) {case eRunning:// 任務正在運行break;case eReady:// 任務就緒break;case eBlocked:// 任務被阻塞break;case eSuspended:// 任務被掛起break;case eDeleted:// 任務已刪除break;case eInvalid:// 無效狀態break;}
}
????????在這個例子中,vApplicationIdleHook是一個空閑鉤子函數,用于在系統空閑時檢查任務的狀態。函數eTaskGetState返回任務的當前狀態,然后可以根據任務的狀態進行相應的處理。
阻塞狀態(Blocked State)
????????在FreeRTOS中,任務的阻塞狀態(Blocked State)表示任務由于等待某些事件而無法執行。任務可以因為多種原因而被阻塞,包括等待定時器、等待消息隊列、等待信號量等。當任務處于阻塞狀態時,它將不會被調度執行,直到滿足了其阻塞條件。
????????以下是幾個常見的阻塞狀態的示例:
????????等待定時器:
// 創建定時器
TimerHandle_t xTimer = xTimerCreate("MyTimer", pdMS_TO_TICKS(1000), pdTRUE, 0, vTimerCallback);// 啟動定時器,在任務中使用xTimerPendFunctionCallFromISR等函數
xTimerStart(xTimer, 0);
????????在任務中可能會使用xTimerPendFunctionCallFromISR等函數等待定時器的超時事件。
????????等待消息隊列:
// 創建消息隊列
QueueHandle_t xQueue = xQueueCreate(5, sizeof(int));// 在任務中等待消息隊列
xQueueReceive(xQueue, &data, portMAX_DELAY);
????????任務通過xQueueReceive函數等待消息隊列中的消息,如果隊列為空,任務將被阻塞,直到隊列中有數據。
????????等待信號量:
// 創建信號量
SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();// 在任務中等待信號量
xSemaphoreTake(xSemaphore, portMAX_DELAY);
????????任務通過xSemaphoreTake函數等待信號量,如果信號量被其他任務取得,當前任務將被阻塞,直到信號量可用。
????????在上述示例中,portMAX_DELAY表示任務將一直等待,直到滿足其阻塞條件為止。任務也可以使用超時值來設置阻塞的最大等待時間。一旦阻塞條件得到滿足,任務將被置為就緒狀態,等待調度器調度執行。
暫停狀態(Suspended State)
????????在FreeRTOS中,任務的暫停狀態(Suspended State)表示任務被顯式地掛起,使得該任務不再參與調度,即不會被執行。任務在創建后可以通過調用vTaskSuspend()函數將其掛起,然后通過調用vTaskResume()函數將其恢復執行。
以下是一個簡單的示例代碼,演示了任務的暫停和恢復:
#include "FreeRTOS.h"
#include "task.h"TaskHandle_t xTaskHandle;void vTaskFunction(void *pvParameters) {while (1) {// 任務的處理邏輯}
}int main(void) {// 創建任務xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);// 啟動調度器vTaskStartScheduler();// 代碼不應該運行到這里,如果運行到這里說明發生了錯誤while (1) {// 處理錯誤}
}// 在某個事件或條件下掛起任務
void vSuspendTask(void) {vTaskSuspend(xTaskHandle);
}// 在某個事件或條件下恢復任務
void vResumeTask(void) {vTaskResume(xTaskHandle);
}
????????在這個例子中,vTaskSuspend()函數用于掛起任務,vTaskResume()函數用于恢復任務。通常,這樣的操作可以用于在某些條件滿足或事件發生時掛起任務,然后在其他條件下恢復任務的執行。
????????需要注意的是,掛起任務不會立即停止任務的執行,而是在任務下一次被調度執行時生效。此外,掛起任務并不會釋放任務所占用的資源,因此在使用這些函數時需要謹慎,以免引起資源泄漏。
就緒狀態(Ready)
????????在FreeRTOS中,任務的就緒狀態(Ready State)表示任務已經準備好被調度執行,但由于有其他高優先級的任務正在運行,該任務暫時還未被調度。
????????在FreeRTOS中,任務被創建后,它的狀態一開始就是就緒狀態,等待調度器來選擇合適的時機執行它。任務的就緒狀態可以由以下幾種情況觸發:
????????任務創建: 任務被創建后,會進入就緒狀態,等待調度執行。
xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, &xTaskHandle);
????????任務掛起狀態解除: 任務從掛起狀態(Suspended State)被恢復后,會進入就緒狀態。
vTaskResume(xTaskHandle);
????????等待事件結束: 任務等待某個事件的發生,一旦事件發生,任務從阻塞狀態切換到就緒狀態。
xSemaphoreTake(xSemaphore, portMAX_DELAY);
????????任務等待定時器超時: 任務等待一個定時器超時,一旦超時,任務從阻塞狀態切換到就緒狀態。
xTaskDelay(1000 / portTICK_PERIOD_MS);
????????在以上例子中,xTaskCreate創建了一個任務,vTaskResume恢復了一個掛起的任務,xSemaphoreTake和xTaskDelay是任務等待事件或超時的示例。
????????任務的就緒狀態是由FreeRTOS調度器根據任務的優先級和調度算法來決定的。當調度器判定某個任務處于就緒狀態時,該任務將會被調度器選中,并執行相應的任務函數。