ESP32作为一款拥有双核架构(Dual-Core)和集成的Wi-Fi/蓝牙功能的物联网芯片。然而在开发过程中未充分利用ESP32的双核优势的话,将导致性能瓶颈和资源浪费。
ESP32双核架构
双核硬件架构
ESP32采用双核Xtensa LX6架构,两个核心分别为:
核心特性:
-
• Core 0(Protocol Core):通常运行Wi-Fi/蓝牙协议栈、底层驱动和系统任务
-
• Core 1(Application Core):运行用户应用代码、业务逻辑和传感器处理
-
• 共享资源:两个核心共享内存、外设和中断控制器,需要合理的同步机制
双核调度机制
ESP32使用FreeRTOS进行任务调度,每个核心独立运行FreeRTOS调度器:
关键点:
-
• 每个核心有独立的任务队列和调度器
-
• 通过队列、信号量、互斥锁等机制实现核间通信
-
• 合理分配任务到不同核心,避免资源竞争
Wi-Fi/蓝牙协议栈分布
Wi-Fi协议栈架构
ESP32的Wi-Fi协议栈主要运行在Core 0上:
关键组件:
-
• Wi-Fi任务(wifi_task):运行在Core 0,处理Wi-Fi协议栈
-
• 事件处理:通过事件循环机制通知应用层
-
• API调用:应用层通过ESP-IDF API与协议栈交互
蓝牙协议栈架构
ESP32的蓝牙协议栈同样主要运行在Core 0:
协议栈与应用的交互机制
// ESP-IDF中的事件循环机制
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_bt.h"
// 事件循环处理(运行在Core 0)
voidwifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT) {
switch (event_id) {
case WIFI_EVENT_STA_START:
esp_wifi_connect();
break;
case WIFI_EVENT_STA_CONNECTED:
printf("Wi-Fi connected\r\n");
// 通知应用层(通过队列或事件)
xQueueSend(app_event_queue, &event_id, 0);
break;
case WIFI_EVENT_STA_DISCONNECTED:
printf("Wi-Fi disconnected\r\n");
xQueueSend(app_event_queue, &event_id, 0);
break;
}
}
}
// 应用层任务(运行在Core 1)
voidapp_task(void *pvParameters) {
int32_t event_id;
while (1) {
// 从队列接收事件(非阻塞)
if (xQueueReceive(app_event_queue, &event_id, portMAX_DELAY)) {
// 处理Wi-Fi事件
switch (event_id) {
case WIFI_EVENT_STA_CONNECTED:
// 执行业务逻辑
start_data_collection();
break;
case WIFI_EVENT_STA_DISCONNECTED:
// 处理断开逻辑
stop_data_collection();
break;
}
}
}
}
双核协同实现方法
任务绑定到指定核心
ESP32 FreeRTOS提供了将任务绑定到特定核心的API:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// 创建任务并绑定到Core 0
voidcreate_protocol_task(void) {
xTaskCreatePinnedToCore(
wifi_protocol_task, // 任务函数
"wifi_task", // 任务名称
4096, // 栈大小
NULL, // 参数
5, // 优先级
NULL, // 任务句柄
0 // 核心ID:0=Core 0, 1=Core 1
);
}
// 创建任务并绑定到Core 1
voidcreate_app_task(void) {
xTaskCreatePinnedToCore(
app_logic_task, // 任务函数
"app_task", // 任务名称
4096, // 栈大小
NULL, // 参数
5, // 优先级
NULL, // 任务句柄
1 // 核心ID:1=Core 1
);
}
// 不绑定核心(由调度器自动分配)
voidcreate_auto_task(void) {
xTaskCreate(
auto_task, // 任务函数
"auto_task", // 任务名称
4096, // 栈大小
NULL, // 参数
5, // 优先级
NULL // 任务句柄
);
}
核间通信机制
1. 队列(Queue):最常用的核间通信方式
#include "freertos/queue.h"
// 创建队列(用于核间通信)
QueueHandle_t data_queue;
QueueHandle_t cmd_queue;
voidinit_inter_core_communication(void) {
// 创建数据队列(Core 0 -> Core 1)
data_queue = xQueueCreate(10, sizeof(sensor_data_t));
// 创建命令队列(Core 1 -> Core 0)
cmd_queue = xQueueCreate(10, sizeof(cmd_t));
}
// Core 0:发送数据到Core 1
voidwifi_protocol_task(void *pvParameters) {
sensor_data_t data;
while (1) {
// 从Wi-Fi接收数据
if (receive_wifi_data(&data)) {
// 发送到队列(非阻塞)
if (xQueueSend(data_queue, &data, 0) != pdTRUE) {
printf("Queue full, data dropped\r\n");
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// Core 1:从队列接收数据
voidapp_logic_task(void *pvParameters) {
sensor_data_t data;
while (1) {
// 从队列接收数据(阻塞等待)
if (xQueueReceive(data_queue, &data, portMAX_DELAY)) {
// 处理数据
process_sensor_data(&data);
}
}
}
2. 信号量(Semaphore):用于同步和资源控制
#include "freertos/semphr.h"
SemaphoreHandle_t data_ready_sem;
voidinit_semaphore(void) {
// 创建二进制信号量
data_ready_sem = xSemaphoreCreateBinary();
}
// Core 0:数据准备好后释放信号量
voidwifi_protocol_task(void *pvParameters) {
while (1) {
// 处理Wi-Fi数据
process_wifi_data();
// 数据准备好,通知Core 1
xSemaphoreGive(data_ready_sem);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// Core 1:等待信号量
voidapp_logic_task(void *pvParameters) {
while (1) {
// 等待数据准备信号(阻塞)
if (xSemaphoreTake(data_ready_sem, portMAX_DELAY)) {
// 处理数据
handle_ready_data();
}
}
}
3. 互斥锁(Mutex):保护共享资源
#include "freertos/semphr.h"
SemaphoreHandle_t shared_resource_mutex;
// 共享资源
int shared_counter = 0;
voidinit_mutex(void) {
// 创建互斥锁
shared_resource_mutex = xSemaphoreCreateMutex();
}
// Core 0:访问共享资源
voidwifi_protocol_task(void *pvParameters) {
while (1) {
// 获取互斥锁
if (xSemaphoreTake(shared_resource_mutex, portMAX_DELAY)) {
// 安全访问共享资源
shared_counter++;
printf("Core 0: counter = %d\r\n", shared_counter);
// 释放互斥锁
xSemaphoreGive(shared_resource_mutex);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// Core 1:访问共享资源
voidapp_logic_task(void *pvParameters) {
while (1) {
// 获取互斥锁
if (xSemaphoreTake(shared_resource_mutex, portMAX_DELAY)) {
// 安全访问共享资源
shared_counter++;
printf("Core 1: counter = %d\r\n", shared_counter);
// 释放互斥锁
xSemaphoreGive(shared_resource_mutex);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
应用示例
IoT数据采集与上传
需求:传感器数据采集、Wi-Fi上传、本地处理并行进行
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_wifi.h"
#include "esp_http_client.h"
// 数据结构
typedefstruct {
float temperature;
float humidity;
uint32_t timestamp;
} sensor_data_t;
// 全局队列
QueueHandle_t sensor_queue;
QueueHandle_t upload_queue;
// Core 1:传感器数据采集任务
voidsensor_collection_task(void *pvParameters) {
sensor_data_t data;
while (1) {
// 采集传感器数据
data.temperature = read_temperature();
data.humidity = read_humidity();
data.timestamp = esp_timer_get_time() / 1000;
// 发送到队列(供本地处理)
xQueueSend(sensor_queue, &data, 0);
// 发送到上传队列(供Wi-Fi上传)
xQueueSend(upload_queue, &data, 0);
vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒采集一次
}
}
// Core 1:本地数据处理任务
voiddata_processing_task(void *pvParameters) {
sensor_data_t data;
while (1) {
// 从队列接收数据
if (xQueueReceive(sensor_queue, &data, portMAX_DELAY)) {
// 本地处理:计算平均值、存储历史数据等
process_local_data(&data);
// 显示到LCD
display_data(&data);
}
}
}
// Core 0:Wi-Fi上传任务
voidwifi_upload_task(void *pvParameters) {
sensor_data_t data;
char url[256];
while (1) {
// 从队列接收数据
if (xQueueReceive(upload_queue, &data, portMAX_DELAY)) {
// 构建HTTP请求URL
snprintf(url, sizeof(url),
"http://api.example.com/data?"
"temp=%.2f&hum=%.2f&time=%lu",
data.temperature, data.humidity, data.timestamp);
// 发送HTTP请求(运行在Core 0,不阻塞Core 1)
esp_http_client_handle_t client = esp_http_client_init(&(esp_http_client_config_t){
.url = url,
});
esp_http_client_perform(client);
esp_http_client_cleanup(client);
}
}
}
// 主函数:任务创建和核心绑定
voidapp_main(void) {
// 创建队列
sensor_queue = xQueueCreate(10, sizeof(sensor_data_t));
upload_queue = xQueueCreate(10, sizeof(sensor_data_t));
// 初始化Wi-Fi(运行在Core 0)
wifi_init();
// Core 1:传感器采集任务
xTaskCreatePinnedToCore(
sensor_collection_task,
"sensor_task",
4096,
NULL,
5,
NULL,
1// Core 1
);
// Core 1:本地处理任务
xTaskCreatePinnedToCore(
data_processing_task,
"process_task",
4096,
NULL,
5,
NULL,
1// Core 1
);
// Core 0:Wi-Fi上传任务
xTaskCreatePinnedToCore(
wifi_upload_task,
"upload_task",
8192,
NULL,
4,
NULL,
0// Core 0
);
}
总结
ESP32的双核架构为物联网应用提供了强大的性能基础。通过合理利用双核协同:
-
• ✅ 性能提升:网络协议栈和用户逻辑并行执行,系统响应速度大幅提升
-
• ✅ 资源优化:合理分配任务到不同核心,充分利用硬件资源
-
• ✅ 实时性保障:关键任务不受网络协议栈阻塞,实时性更好
-
• ✅ 扩展性强:双核架构为复杂应用提供了足够的计算能力