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

ESP32使用FreeRTOS队列驱动HC-SR04触发Core panic问题求助

ESP32驱动HC-SR04触发Core panic问题排查

问题描述

使用ESP32驱动HC-SR04超声波模块,通过中断获取传感器数据,再通过FreeRTOS队列将数据传递给任务输出。设备初始能正常输出数据,但数秒后触发Core panic并重启;移除入队代码后设备恢复正常。

报错信息

Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
Core  1 register dump:
PC      : 0x40084c90  PS      : 0x00050631  A0      : 0x800d31c4  A1      : 0x3ffbf53c
A2      : 0x00000040  A3      : 0x00018040  A4      : 0x000637ff  A5      : 0x3ffbf50c
A6      : 0x00000008  A7      : 0x00000000  A8      : 0x00000001  A9      : 0x4008bf6e
A10     : 0x00060b23  A11     : 0x3ffc4a14  A12     : 0x3ff44000  A13     : 0xffffff00
A14     : 0x00000000  A15     : 0x00000020  SAR     : 0x0000001d  EXCCAUSE: 0x0000001c
EXCVADDR: 0x800d31d0  LBEG    : 0x400899a4  LEND    : 0x400899ba  LCOUNT  : 0xffffffff
Backtrace: 0x40084c8d:0x3ffbf53c |<-CORRUPTED

相关代码

主程序

radar radar0(25,35);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200); 
  Serial.setDebugOutput(true);
  radar0.start();
} 

radar类定义

class radar
{
private:
    uint8_t txPin,rxPin;
    Ticker loopTrigger; //ticker that use to trigger loop
    Ticker singalEnder; //ticker that use to generate a 1ms pulse
    QueueHandle_t dstQueue; //queue that use to pass dst to coroutine
    float previousDst;
    bool enable;
    unsigned long echoStartTime; //The time the echo start received
    void loop(); 
    void echoStartHandler(); //callback when the echo start
    void echoEndHandler(); //callback when the echo end
    void queueListener();
public:
    radar(uint8_t txPin,uint8_t rxPin);
    void start();
};
radar::radar(uint8_t txPin,uint8_t rxPin){
    this->txPin=txPin;
    this->rxPin=rxPin;
    this->enable=true;
    this->dstQueue = xQueueCreate(4,sizeof(float));
}

void radar::start(){
    //set pin mode
    pinMode(this->txPin,OUTPUT);
    pinMode(this->rxPin,INPUT);
    //low down the txPin
    digitalWrite(this->txPin,LOW);
    //Create a coroutine for listening to the dstQueue
    auto fn = [](void *arg){static_cast<radar*>(arg)->queueListener();};
    xTaskCreate(fn,"dstListener",1024,this,0,NULL);
    //enable the radar
    this->enable=true;
    this->loop();
}

void radar::queueListener(){
    float dst;
    while (this->enable)
    {
        if (xQueueReceive(this->dstQueue,&dst,0)==pdFALSE){
            //sleep 10ms if the queue is empty
            vTaskDelay(10/portTICK_PERIOD_MS);
            continue;
        }
        if (abs(dst-this->previousDst)>5){
            Serial.println(dst);
            this->previousDst=dst;
        }
    }
    vTaskDelete(NULL);
}

void radar::echoStartHandler(){
    this->echoStartTime = micros();
    //Set rxPin pull-down interrupt
    attachInterruptArg(rxPin,[](void* r){static_cast<radar*>(r)->echoEndHandler();},this,FALLING);
}

void radar::echoEndHandler(){
    detachInterrupt(this->rxPin);
    unsigned long echoEndTime = micros();
    float pluseTime;
    if (echoEndTime > this->echoStartTime){ //If the variable does not overflow
        pluseTime = echoEndTime - this->echoStartTime;
    }else{
        pluseTime = echoEndTime + (LONG_MAX - this->echoStartTime);
    }
    float dst = pluseTime * 0.0173;
    //push the dst to queue 
    BaseType_t xHigherPriorityTaskWoken;
    xQueueSendFromISR(this->dstQueue,&dst,&xHigherPriorityTaskWoken);
    if (xHigherPriorityTaskWoken==pdTRUE){
        portYIELD_FROM_ISR();
    }
}

void radar::loop(){
    if (!this->enable){
        return;
    }
    //Set loop() to be triggered after 100ms
    this->loopTrigger.once_ms<radar*>(100,[](radar* r){r->loop();},this);
    //Generate a 1ms pulse at txPin
    digitalWrite(this->txPin,HIGH);
    this->singalEnder.once_ms<uint8_t>(1,[](uint8_t pin){digitalWrite(pin,LOW);},this->txPin);
    //Set rxPin pull-up callback
    attachInterruptArg(rxPin,[](void* r){static_cast<radar*>(r)->echoStartHandler();},this,RISING);
}

问题分析与修复方案

核心问题定位

LoadProhibited错误表示代码尝试访问非法内存地址,结合入队代码移除后恢复正常的现象,问题集中在FreeRTOS队列操作、中断上下文管理和内存相关逻辑上。

具体修复点

  1. 检查队列创建有效性
    xQueueCreate可能因内存不足返回NULL,ISR中使用NULL队列句柄会触发非法内存访问。在构造函数中添加创建失败判断:
radar::radar(uint8_t txPin,uint8_t rxPin){
    this->txPin=txPin;
    this->rxPin=rxPin;
    this->enable=true;
    this->dstQueue = xQueueCreate(4,sizeof(float));
    if(this->dstQueue == NULL){
        Serial.println("Queue creation failed!");
        // 可添加错误处理逻辑,如终止程序或降级处理
    }
}
  1. 增大任务栈空间
    dstListener任务的1024字节栈空间不足以容纳队列操作、串口输出等逻辑的栈需求,易导致栈溢出。修改栈大小为2048字节及以上:
xTaskCreate(fn,"dstListener",2048,this,0,NULL);
  1. 修复micros()溢出计算逻辑
    原代码手动处理溢出时误用LONG_MAX(long类型最大值),与unsigned long类型的echoStartTime不匹配。利用无符号整数减法自动处理溢出的特性简化逻辑:
void radar::echoEndHandler(){
    detachInterrupt(this->rxPin);
    unsigned long echoEndTime = micros();
    // 无符号减法自动处理溢出,无需手动判断
    float pluseTime = echoEndTime - this->echoStartTime;
    float dst = pluseTime * 0.0173;
    // ... 后续队列操作代码
}
  1. 避免重复注册中断
    在echoStartHandler中注册FALLING中断前,未解除之前的RISING中断注册,可能导致中断向量表异常。修改代码:
void radar::echoStartHandler(){
    this->echoStartTime = micros();
    // 先解除之前的中断注册
    detachInterrupt(this->rxPin);
    // 再注册FALLING中断
    attachInterruptArg(rxPin,[](void* r){static_cast<radar*>(r)->echoEndHandler();},this,FALLING);
}
  1. ISR中队列操作的安全检查
    在ISR中调用xQueueSendFromISR前,先验证队列句柄有效性:
if(this->dstQueue != NULL){
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xQueueSendFromISR(this->dstQueue,&dst,&xHigherPriorityTaskWoken);
    if (xHigherPriorityTaskWoken==pdTRUE){
        portYIELD_FROM_ISR();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:23:16