You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的两个核心之间应如何进行正确的通信?


错误原因分析

  1. 外设初始化顺序错误:启动Core 0的读取任务后才初始化I2C设备ads,导致Task1在ads.begin()完成前就调用读取函数,访问未初始化的对象内存,触发LoadProhibited错误。
  2. 共享资源无同步保护:全局变量和ads对象被两个核心的任务直接访问,没有线程同步机制,会引发数据竞争甚至内存访问异常。
  3. 语法冗余: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);

关键改进点

  1. 调整初始化顺序:先完成外设和通信结构的初始化,再启动任务,避免访问未就绪资源。
  2. 使用队列隔离资源:Core 0读取数据后发送到队列,Core 1从队列接收数据,完全消除全局变量共享的同步问题。
  3. 规范FreeRTOS API使用:用vTaskDelay替代delay,更符合实时操作系统调度逻辑。

内容的提问来源于stack exchange,提问作者Your_missing_bracket

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 13:03:23