ESP32 入門到精通 – 使用 xTask


簡介

在 ESP32 IDF(Espressif IoT Development Framework)中,FreeRTOS 是用於管理多任務的即時操作系統核心。任務(Task)是 FreeRTOS 中的核心概念之一,每個任務都是一個獨立的執行緒,可以併發運行。以下是 FreeRTOS 任務在 ESP32 中的作用詳細說明。

xTask & vTask 差別

在 FreeRTOS 中,xTask 和 vTask 前綴用於區分不同類型的 API 函數。理解它們的差別有助於正確地使用這些函數。

1. xTask 函數
xTask 前綴通常用於返回一個值的函數。這個值通常表示函數的執行結果,或者是某些狀態資訊。
返回值類型: 通常是 BaseType_t(表示操作成功或失敗),或者是指標類型(如 TaskHandle_t)。

2. vTask 函數
vTask 前綴用於不返回任何值(void 類型)的函數。這類函數通常執行某種操作,但不需要告訴調用者該操作的結果。
返回值類型: void(不返回值)。

Task 創建

1. 概述
在 FreeRTOS 中,任務是執行程式碼的獨立執行緒。每個任務都有自己的堆疊、優先權、狀態和執行時間片。任務在 FreeRTOS 的調度器下運行,調度器負責在不同任務之間切換。

2. 創建
任務建立使用 xTaskCreate 函數,其基本語法如下:
BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,       
    const char * const pcName,       
    uint16_t usStackDepth,           
    void *pvParameters,              
    UBaseType_t uxPriority,          
    TaskHandle_t *pxCreatedTask
);
各參數的詳細說明:
pvTaskCode: 指向任務函數的指標。這個函數定義了任務的行為,即任務執行的內容。

pcName: 任務的名稱。這個名稱主要用於調試時識別任務,方便查看和追踪任務的執行情況。這個名稱不必唯一。

usStackDepth: 指定任務堆疊的大小,單位是「字」(word),而不是字節(byte)。在大多數平台上,一個字通常是 4 個字節。堆疊大小必須足夠大,以確保任務能夠正常運行。

pvParameters: 傳遞給任務的參數,這個指標在任務啟動時會被傳遞給任務函數。這使得你可以在任務啟動時給它傳遞特定的參數。

uxPriority: 任務的優先級。FreeRTOS 根據這個優先級來調度任務,優先級較高的任務會優先於低優先級任務執行。如果多個任務具有相同的優先級,則按時間片輪轉執行。

pxCreatedTask: 指向任務句柄變數的指標。任務創建成功後,這個變數將保存該任務的句柄。如果你不需要之後再引用這個任務,可以將此參數設為 NULL。

返回值:
這個函數返回 BaseType_t 類型的值。如果任務創建成功,通常返回 pdPASS。如果創建失敗(例如內存不足),則返回 errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 等錯誤代碼。

示例
void taskFunction(void *pvParameter) {
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main() {
    // Create a new task with the following parameters:
    // taskFunction: The function that defines the task's operations.
    // "TaskName": The name of the task for debugging purposes.
    // 2048: The stack size for the task, in words (not bytes).
    // NULL: The parameter passed to the task function (no parameters needed here).
    // 5: The priority of the task (higher numbers indicate higher priority).
    // NULL: Task handle (not needed in this case, so passing NULL).
    xTaskCreate(taskFunction, "TaskName", 2048, NULL, 5, NULL);
}

Task 優先權

任務優先順序決定了任務的執行順序。優先順序較高的任務會比優先順序較低的任務更常獲得 CPU 時間。 FreeRTOS 中的優先權範圍從 0(最低優先權)到 configMAX_PRIORITIES - 1(最高優先權)。
#define tskIDLE_PRIORITY 0    // The priority level assigned to the idle task
#define configMAX_PRIORITIES  5  // The maximum priority level available for tasks

Task Stack

每個任務都有一個堆疊,用於儲存任務執行期間的局部變數和函數呼叫資訊。堆疊的大小在任務建立時指定,必須足夠大以容納任務執行時所需的所有資料。
#define STACK_SIZE 2048 // Task stack size (in words)

void app_main() {
    xTaskCreate(taskFunction, "TaskName", STACK_SIZE, NULL, 5, NULL);
}

Task 調度

FreeRTOS 使用時間片輪轉(輪詢調度)和優先權調度來切換任務。當某個任務的時間片用完,或某個更高優先權的任務變成就緒狀態時,調度器會進行任務切換。

vTaskDelay & vTaskDelayUntil

vTaskDelay 用于使任务休眠一段时间。
vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelayUntil 使任务以固定的时间间隔运行,避免时间漂移。
TickType_t xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));

Task 删除

自行删除。
void taskFunction(void *pvParameter) {
    // Perform task operations
    vTaskDelete(NULL); // Delete the current task
}
由其他任务删除。
void app_main() {
    TaskHandle_t taskHandle;
    xTaskCreate(taskFunction, "TaskName", 2048, NULL, 5, &taskHandle);
    // Delete the task
    vTaskDelete(taskHandle);
}

安裝 VSCode 和 ESP-IDF VSCode 擴展

確保你已經安裝和配置好了 ESP-IDF 開發環境。您也可以參考 ESP32 入門到精通 – 在 VSCode 安裝 ESP-IDF 插件 這篇文章。

新建 ESP32 專案

利用在 VSCode 的 IDF 插件來新建一個 ESP32 專案可參考 ESP32 入門到精通 – 如何用 VSCode 創建 ESP32 專案

xTask & vTask 範例

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <stdio.h>

// Task to blink an LED
void ledBlinkTask(void *pvParameters) {
    while (1) {
        // Simulate LED on
        printf("LED ON\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 milliseconds

        // Simulate LED off
        printf("LED OFF\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 milliseconds
    }
}

// Task to monitor system status
void monitorTask(void *pvParameters) {
    while (1) {
        // Simulate monitoring
        printf("Monitoring system...\n");
        vTaskDelay(pdMS_TO_TICKS(2000)); // Delay for 2 seconds
    }
}

// Task that runs and then deletes itself
void selfDeletingTask(void *pvParameters) {
    printf("Self-Deleting Task is running...\n");

    // Delay for 3 seconds
    vTaskDelay(pdMS_TO_TICKS(3000));

    printf("Self-Deleting Task will delete itself now.\n");

    // Delete this task
    vTaskDelete(NULL); // NULL means delete the calling task
}

void app_main() {
    // Create LED blink task with priority 3
    xTaskCreate(ledBlinkTask, "LED Blink Task", 2048, NULL, 3, NULL);

    // Create monitoring task with priority 2
    xTaskCreate(monitorTask, "Monitor Task", 2048, NULL, 2, NULL);

    // Create a self-deleting task with priority 1
    xTaskCreate(selfDeletingTask, "Self-Deleting Task", 2048, NULL, 1, NULL);
}

編譯和燒錄

在 VSCode 中找到 "ESP32-IDF : Build, Flash and Monitor" ICON 執行和燒錄。

查看結果

我們可以查看在終端機的列印結果,如下...
LED ON
LED OFF
LED ON
LED OFF
Monitoring system...
LED ON
LED OFF
LED ON
LED OFF
Self-Deleting Task is running...
Monitoring system...
LED ON
LED OFF
LED ON
LED OFF
Self-Deleting Task will delete itself now.
Monitoring system...
LED ON
LED OFF
...

結論

在 ESP32 IDF 中,FreeRTOS 任務是構建複雜、多功能嵌入式應用的基礎。透過任務,你可以有效管理併發操作、提高系統響應速度、優化資源使用,並實現模組化和可維護的代碼結構。FreeRTOS 的靈活性和強大功能使得它成為 ESP32 上進行嵌入式開發的關鍵工具。