1.7. FreeRTOS 任务管理
ESP-IDF 基于 FreeRTOS,多任务编程是 ESP32 的核心能力。
1.7.1. 任务创建
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// 任务函数原型:void task_name(void *pvParameter)
void blink_task(void *pvParameter)
{
gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);
while (1) {
gpio_set_level(GPIO_NUM_2, 1);
vTaskDelay(500 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_NUM_2, 0);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
// 创建任务
xTaskCreate(
blink_task, // 任务函数
"blink", // 任务名
2048, // 栈大小(字)
NULL, // 参数
5, // 优先级 (0~25)
NULL // 任务句柄
);
}
备注
ESP-IDF 中 FreeRTOS 的优先级范围是 0~25,数字越大优先级越高。 空闲任务优先级为 0,Timer 服务任务优先级为 1。
1.7.2. 多核任务
ESP32 的双核可以分配任务到特定核心:
// 创建任务并绑定到核心 0 (PRO_CPU)
xTaskCreatePinnedToCore(
task_function,
"task_pro",
4096,
NULL,
5,
NULL,
0); // 核心 0
// 绑定到核心 1 (APP_CPU)
xTaskCreatePinnedToCore(
task_function,
"task_app",
4096,
NULL,
5,
NULL,
1); // 核心 1
重要
核心 0 (PRO_CPU):处理 WiFi/BLE 协议栈、中断
核心 1 (APP_CPU):运行用户应用代码
如果没有指定核心,IDF 默认使用核心 1
1.7.3. 队列通信
// 创建队列
QueueHandle_t sensor_queue;
sensor_queue = xQueueCreate(10, sizeof(float));
// 发送方(可在 ISR 中使用)
float temperature = 25.5;
xQueueSend(sensor_queue, &temperature, portMAX_DELAY);
// 接收方
float received_temp;
if (xQueueReceive(sensor_queue, &received_temp,
pdMS_TO_TICKS(1000)) == pdTRUE) {
ESP_LOGI(TAG, "温度: %.1f", received_temp);
}
1.7.4. 信号量
SemaphoreHandle_t xSemaphore;
// 二值信号量(同步)
xSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(xSemaphore); // 释放
xSemaphoreTake(xSemaphore, portMAX_DELAY); // 获取(阻塞)
// 互斥量(带优先级继承)
SemaphoreHandle_t xMutex = xSemaphoreCreateMutex();
void shared_resource_task(void *pvParameter)
{
while (1) {
if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
// 访问共享资源
critical_section_operation();
xSemaphoreGive(xMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
1.7.5. 事件组
EventGroupHandle_t event_group;
const int BIT_SENSOR_READY = BIT0;
const int BIT_WIFI_READY = BIT1;
void event_example(void)
{
event_group = xEventGroupCreate();
// 设置事件位
xEventGroupSetBits(event_group, BIT_SENSOR_READY);
xEventGroupSetBits(event_group, BIT_WIFI_READY);
// 等待多个事件(所有位)
EventBits_t bits = xEventGroupWaitBits(
event_group,
BIT_SENSOR_READY | BIT_WIFI_READY,
pdTRUE, // 清除位
pdTRUE, // 等待所有位
portMAX_DELAY);
}
1.7.6. 任务通知
// 轻量级同步(比信号量更快,更省内存)
TaskHandle_t task_handle;
// 发送通知
xTaskNotifyGive(task_handle);
// 接收通知(在目标任务中)
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// 带值的通知
uint32_t notify_value = 42;
xTaskNotify(task_handle, notify_value, eSetValueWithOverwrite);
// 接收
uint32_t value;
xTaskNotifyWait(0, 0, &value, portMAX_DELAY);
1.7.7. 软件定时器
TimerHandle_t timer;
void timer_callback(TimerHandle_t xTimer)
{
ESP_LOGI(TAG, "定时器触发");
}
void timer_create(void)
{
timer = xTimerCreate(
"my_timer", // 名字
pdMS_TO_TICKS(1000), // 周期
pdTRUE, // 自动重载
NULL, // ID
timer_callback);
xTimerStart(timer, 0); // 启动
// xTimerStop(timer, 0); // 停止
}
1.7.8. 栈溢出检测
// 方法 1:配置 menuconfig
// Component config → FreeRTOS → configCHECK_FOR_STACK_OVERFLOW
// 方法 2:运行时检查
UBaseType_t stack_high_water = uxTaskGetStackHighWaterMark(NULL);
ESP_LOGI(TAG, "剩余栈空间: %d 字", stack_high_water * 4);
// 如果剩余栈空间 < 256 字节,应增加任务栈大小
小技巧
FreeRTOS 使用建议:
栈大小: 任务创建时分配,默认 2048 字 (~8 KB),根据需求调整
优先级:避免同优先级任务长时间占用 CPU(用 vTaskDelay 让出)
ISR:中断服务函数应尽量短,复杂处理通过队列/信号量交给任务
看门狗:长时间循环记得喂狗 esp_task_wdt_reset()
内存:使用 heap_caps_get_free_size(MALLOC_CAP_INTERNAL) 监控内存