飛書文檔https://x509p6c8to.feishu.cn/wiki/EH3owsPahisvl6kL6k3cqaQ3n0g? 在我們學習單片機的時候,main函數入口中一般有一個while大循環在不停輪詢,如果我們需要實現多種不同的業務,就需要用到狀態機,根據不同時刻的要求執行不同的代碼,雖然可以實現需求,但這種情況下代碼是比較臃腫的。試想下,如果我們可以開啟多個while循環,每個while循環負責不同的業務,這樣實現復雜的業務是不是更容易了呢?
void main1(){while (1){//執行某些操作}
}void main2(){while (1){//執行某些操作}
}
在 FreeRTOS 中,任務(Task) 可以通俗地理解為一個獨立的“小程序”或“小工人”,每個任務都有自己的工作內容和優先級,它們共同協作完成整個系統的功能。
1、任務是什么?
任務是一個獨立的執行單元
每個任務就像一個小工人,負責完成特定的工作。例如:
- 一個任務負責讀取傳感器數據。
- 一個任務負責控制 LED 燈。
- 一個任務負責處理網絡通信。
任務有自己的運行空間
每個任務都有自己的棧(Stack),用于存儲臨時變量和函數調用信息。任務之間是相互獨立的,不會互相干擾。
任務可以同時運行
雖然 ESP32 是單核或雙核處理器,但 FreeRTOS 通過快速切換任務,讓多個任務看起來像是同時運行的(這叫做“并發”)。
假設我們有一個智能家居系統,需要完成以下功能:
- 讀取溫度傳感器的數據。
- 根據溫度控制風扇的開關。
- 將溫度數據發送到云端。
我們可以創建 3 個任務:
任務 1:讀取溫度
每隔 1 秒讀取一次溫度傳感器的數據,并將數據發送到隊列中。
任務 2:控制風扇
從隊列中讀取溫度數據,如果溫度超過 30°C,打開風扇;否則關閉風扇。
任務 3:上傳數據
從隊列中讀取溫度數據,并通過 Wi-Fi 上傳到云端。
2、如何實現多任務?
IDF默認是集成了實時操作系統FreeRTOS的,所以FreeRTOS相關接口我們都是可以用的。
但是需要注意的是,IDF的例程中,是不會單獨對FreeRTOS通用的任務創建、任務通信等RTOS相關特性進行講解,這部分我們可以參考FreeRTOS官方文檔或者IDF API文檔進行了解。
IDF freertos api :https://docs.espressif.com/projects/esp-idf/zh_CN/v5.4/esp32s3/api-reference/system/freertos_idf.html
freertos 官方api:https://www.freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/04-API-references/01-Task-creation/01-xTaskCreate
那這節課,我們就來了解下FreeRTOS的多任務實現。
頭文件
#include "freertos/task.h"BaseType_t xTaskCreate(TaskFunction_t pxTaskCode,const char *const pcName,const configSTACK_DEPTH_TYPE usStackDepth,void *const pvParameters,UBaseType_t uxPriority,TaskHandle_t *const pxCreatedTask
);
功能: xTaskCreate 函數用于創建一個新的 FreeRTOS 任務。該函數分配任務所需的內存,并初始化任務控制塊(TCB),然后將任務添加到就緒列表中,等待調度器運行該任務。
參數:
pxTaskCode: 指向任務函數的指針。任務函數的原型必須為 void TaskFunction(void *pvParameters)。
pcName: 任務的名稱,用于調試和日志記錄。長度不能超過 configMAX_TASK_NAME_LEN 個字符。
usStackDepth: 任務堆棧的大小,以字為單位
pvParameters: 傳遞給任務函數的參數。
uxPriority: 任務的優先級。優先級值越高,任務的優先級越高。優先級值的范圍通常是 0 到 configMAX_PRIORITIES - 1。
pxCreatedTask: 用于存儲新創建任務的句柄。如果不需要任務句柄,可以傳遞 NULL。
返回值:
pdPASS: 任務創建成功。
如下方例程,我們在app_main中創建了兩個任務task1和task2,創建成功后,我們可以看到app_main、task1、task2間隔運行的效果。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"static const char *TAG = "MyModule";static void task1_func(void *arg)
{while (1){ESP_LOGI(TAG, "task1 run");vTaskDelay(1000 / portTICK_PERIOD_MS);}
}static void task2_func(void *arg)
{while (1){ESP_LOGI(TAG, "task2 run");vTaskDelay(1000 / portTICK_PERIOD_MS);}
}void app_main(void)
{// 創建任務,任務堆棧大小為 2048 字,優先級為 5xTaskCreate(task1_func,??? // 任務函數"task1_name",? // 任務名稱2048,????????? // 堆棧大小(字)NULL,????????? // 傳遞給任務的參數5,???????????? // 任務優先級NULL?????????? // 任務句柄);xTaskCreate(task2_func,??? // 任務函數"task2_name",? // 任務名稱2048,????????? // 堆棧大小(字)NULL,????????? // 傳遞給任務的參數5,???????????? // 任務優先級NULL?????????? // 任務句柄);int cnt = 0;while (1){ESP_LOGI(TAG, "app_main run: %d", cnt++);vTaskDelay(1000 / portTICK_PERIOD_MS);}
}
編譯燒錄后,運行效果如下: