ESP32双核任务通信触发Guru Meditation Error问题问询
ESP32双核通信报错:LoadProhibited Guru Meditation Error
我尝试从I2C设备读取数据,由Core 0接收后存入全局变量,再由Core 1读取并打印。但Core 0访问这些变量时触发报错:
"guru meditation error core 0 panic'ed (loadprohibited). exception was unhandled"
相关代码如下:
TaskHandle_t Task1; TaskHandle_t Task2; #include <Adafruit_ADS1X15.h> Adafruit_ADS1015 ads; volatile int16_t adc0, adc1, adc2, adc3; volatile float volts0, volts1, volts2, volts3; void setup() { Serial.begin(115200); delay(1000); Serial.println("Hello!"); Serial.println("Getting single-ended readings from AIN0..3"); Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115"); //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0 xTaskCreatePinnedToCore( Task1code, /* Task function. */ "Task1", /* name of task. */ 10000, /* Stack size of task */ NULL, /* parameter of the task */ 1, /* priority of the task */ &Task1, /* Task handle to keep track of created task */ 0); /* pin task to core 0 */ delay(500); //create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1 xTaskCreatePinnedToCore( Task2code, /* Task function. */ "Task2", /* name of task. */ 10000, /* Stack size of task */ NULL, /* parameter of the task */ 2, /* priority of the task */ &Task2, /* Task handle to keep track of created task */ 1); /* pin task to core 1 */ delay(500); if (!ads.begin()) { Serial.println("Failed to initialize ADS."); while (1); } } void Task1code( void * pvParameters ){ for(;;){ Serial.print("Task1 running on core "); Serial.println(xPortGetCoreID()); adc0 = ads.readADC_SingleEnded(0); adc1 = ads.readADC_SingleEnded(1); adc2 = ads.readADC_SingleEnded(2); adc3 = ads.readADC_SingleEnded(3); volts0 = ads.computeVolts(adc0); volts1 = ads.computeVolts(adc1); volts2 = ads.computeVolts(adc2); volts3 = ads.computeVolts(adc3); delay(100); } } void Task2code( void * pvParameters ){ delay(500); Serial.print("Task2 running on core "); Serial.println(xPortGetCoreID()); for(;;){{ Serial.println("-----------------------------------------------------------"); Serial.print("AIN0: "); Serial.print(adc0); Serial.print(" "); Serial.print(volts0); Serial.println("V"); Serial.print("AIN1: "); Serial.print(adc1); Serial.print(" "); Serial.print(volts1); Serial.println("V"); Serial.print("AIN2: "); Serial.print(adc2); Serial.print(" "); Serial.print(volts2); Serial.println("V"); Serial.print("AIN3: "); Serial.print(adc3); Serial.print(" "); Serial.print(volts3); Serial.println("V"); delay(100); } } void loop() { }
请问ESP32的两个核心之间应如何进行正确的通信?
错误原因分析
- 外设初始化顺序错误:启动Core 0的读取任务后才初始化I2C设备
ads,导致Task1在ads.begin()完成前就调用读取函数,访问未初始化的对象内存,触发LoadProhibited错误。 - 共享资源无同步保护:全局变量和
ads对象被两个核心的任务直接访问,没有线程同步机制,会引发数据竞争甚至内存访问异常。 - 语法冗余:Task2code的for循环嵌套了多余的大括号,虽不直接导致崩溃,但影响代码可读性。
ESP32双核正确通信方案
方案1:使用FreeRTOS队列传递数据(推荐)
队列是线程安全的跨核心数据传递结构,通过封装完整数据块避免直接共享内存的问题,逻辑更简洁可靠。
修正后的代码:
#include <Adafruit_ADS1X15.h> // 封装ADC数据的结构体 typedef struct { int16_t adc0, adc1, adc2, adc3; float volts0, volts1, volts2, volts3; } ADCData; Adafruit_ADS1015 ads; QueueHandle_t adcDataQueue; // 跨核心数据队列 void setup() { Serial.begin(115200); delay(1000); Serial.println("Hello!"); Serial.println("Getting single-ended readings from AIN0..3"); Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115"); // 先初始化I2C设备,确保任务启动前外设就绪 if (!ads.begin()) { Serial.println("Failed to initialize ADS."); while (1); } // 创建队列,可存储2个ADC数据块 adcDataQueue = xQueueCreate(2, sizeof(ADCData)); if (adcDataQueue == NULL) { Serial.println("Failed to create queue."); while (1); } // 创建Core 0的ADC读取任务 xTaskCreatePinnedToCore( TaskReadADC, /* 任务函数 */ "ReadADC", /* 任务名称 */ 10000, /* 栈大小 */ NULL, /* 任务参数 */ 1, /* 优先级 */ NULL, /* 任务句柄 */ 0); /* 绑定到Core 0 */ // 创建Core 1的数据打印任务 xTaskCreatePinnedToCore( TaskPrintData, /* 任务函数 */ "PrintData", /* 任务名称 */ 10000, /* 栈大小 */ NULL, /* 任务参数 */ 2, /* 优先级 */ NULL, /* 任务句柄 */ 1); /* 绑定到Core 1 */ } void TaskReadADC( void * pvParameters ){ ADCData data; for(;;){ // 读取ADC数据 data.adc0 = ads.readADC_SingleEnded(0); data.adc1 = ads.readADC_SingleEnded(1); data.adc2 = ads.readADC_SingleEnded(2); data.adc3 = ads.readADC_SingleEnded(3); data.volts0 = ads.computeVolts(data.adc0); data.volts1 = ads.computeVolts(data.adc1); data.volts2 = ads.computeVolts(data.adc2); data.volts3 = ads.computeVolts(data.adc3); // 发送数据到队列,超时10ms xQueueSend(adcDataQueue, &data, pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(100)); // 用FreeRTOS延迟替代delay,更符合调度逻辑 } } void TaskPrintData( void * pvParameters ){ ADCData data; Serial.print("Print task running on core "); Serial.println(xPortGetCoreID()); for(;;){ // 从队列接收数据,阻塞等待 if(xQueueReceive(adcDataQueue, &data, portMAX_DELAY)){ Serial.println("-----------------------------------------------------------"); Serial.print("AIN0: "); Serial.print(data.adc0); Serial.print(" "); Serial.print(data.volts0); Serial.println("V"); Serial.print("AIN1: "); Serial.print(data.adc1); Serial.print(" "); Serial.print(data.volts1); Serial.println("V"); Serial.print("AIN2: "); Serial.print(data.adc2); Serial.print(" "); Serial.print(data.volts2); Serial.println("V"); Serial.print("AIN3: "); Serial.print(data.adc3); Serial.print(" "); Serial.print(data.volts3); Serial.println("V"); } } } void loop() { // 闲置loop,任务由FreeRTOS调度 }
方案2:使用互斥锁保护共享资源
如果需要保留全局变量,可通过FreeRTOS互斥锁确保同一时间只有一个核心访问共享资源:
// 新增互斥锁 SemaphoreHandle_t dataMutex; // setup中初始化互斥锁 dataMutex = xSemaphoreCreateMutex(); // TaskReadADC中更新全局变量时加锁 xSemaphoreTake(dataMutex, portMAX_DELAY); adc0 = ads.readADC_SingleEnded(0); // ... 其他变量赋值 xSemaphoreGive(dataMutex); // TaskPrintData中读取全局变量时加锁 xSemaphoreTake(dataMutex, portMAX_DELAY); Serial.print("AIN0: "); Serial.print(adc0); // ... 打印逻辑 xSemaphoreGive(dataMutex);
关键改进点
- 调整初始化顺序:先完成外设和通信结构的初始化,再启动任务,避免访问未就绪资源。
- 使用队列隔离资源:Core 0读取数据后发送到队列,Core 1从队列接收数据,完全消除全局变量共享的同步问题。
- 规范FreeRTOS API使用:用
vTaskDelay替代delay,更符合实时操作系统调度逻辑。
内容的提问来源于stack exchange,提问作者Your_missing_bracket
相关产品推荐
相关产品推荐

