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队列操作、中断上下文管理和内存相关逻辑上。
具体修复点
- 检查队列创建有效性
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!"); // 可添加错误处理逻辑,如终止程序或降级处理 } }
- 增大任务栈空间
dstListener任务的1024字节栈空间不足以容纳队列操作、串口输出等逻辑的栈需求,易导致栈溢出。修改栈大小为2048字节及以上:
xTaskCreate(fn,"dstListener",2048,this,0,NULL);
- 修复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; // ... 后续队列操作代码 }
- 避免重复注册中断
在echoStartHandler中注册FALLING中断前,未解除之前的RISING中断注册,可能导致中断向量表异常。修改代码:
void radar::echoStartHandler(){ this->echoStartTime = micros(); // 先解除之前的中断注册 detachInterrupt(this->rxPin); // 再注册FALLING中断 attachInterruptArg(rxPin,[](void* r){static_cast<radar*>(r)->echoEndHandler();},this,FALLING); }
- 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
相关产品推荐
相关产品推荐

