ESP32基于FreeRTOS开发报xQueueGenericReceive断言失败错误如何解决
我正在开发一个ESP32项目,基于ESP32 Arduino和FreeRTOS实现,核心功能是按照指定频率采样模拟量并存入数组,再通过BLE将数据发送到用户侧应用。目前遇到如下报错无法推进:
/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:1443
(xQueueGenericReceive)- assert failed! abort() was called at PC
0x4008e1d5 on core 1Backtrace: 0x40091b38:0x3ffe0b20 0x40091d69:0x3ffe0b40
0x4008e1d5:0x3ffe0b60 0x400d1a2d:0x3ffe0ba0 0x4008e525:0x3ffe0be0Rebooting... ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0,
SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1 load:0x3fff0018,len:4 load:0x3fff001c,len:1044
load:0x40078000,len:8896 load:0x40080400,len:5816 entry 0x400806ac
已排查发现错误和中断中使用的信号量相关,但无法定位具体原因和解决方案,相关代码如下:
#include <ArduinoJson.h> #include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> #include <BLE2902.h> #if CONFIG_FREERTOS_UNICORE static const BaseType_t app_cpu = 0; #else static const BaseType_t app_cpu = 1; #endif //ADC Related Global Variables static const uint16_t timer_divider = 80; static const uint64_t timer_max_count = 1000; static const int adc_pin = A0; static const int BUF_SIZE = 1000; static int buf[BUF_SIZE]; int Buff_Len = 0; static int Read = 0; static int Write = 0; static int count = 0; static float avg = 0; int i = 0; int BLE_flag = 0; String cmd; static hw_timer_t *timer = NULL; static uint16_t val; static int count1 = 0; static SemaphoreHandle_t bin_sem = NULL; static SemaphoreHandle_t bin_sem2 = NULL; static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; //ADC Related Global Variables //BLE Global Variable char Reading[4]; BLEServer *pServer = NULL; BLECharacteristic *pTxCharacteristic; bool deviceConnected = false; bool oldDeviceConnected = false; //Declaration BLE necessary Classes #define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID #define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" class MyServerCallbacks:public BLEServerCallbacks { void onConnect (BLEServer * pServer) { deviceConnected = true; }; void onDisconnect (BLEServer * pServer) { deviceConnected = false; } }; //BLE Global Variables //Task Section void IRAM_ATTR onTimer () { //sampling xSemaphoreGiveFromISR (bin_sem2, &task_woken); if (task_woken) { portYIELD_FROM_ISR (); } } void move_to_Queue (void *parameters) { while (1) { xSemaphoreTake (bin_sem2, portMAX_DELAY); if (Buff_Len == BUF_SIZE || count1 > 2000) { Serial.println ("Buffer is full"); xSemaphoreGive (bin_sem); } else { // storing the instantaneous sample value to buffer } } } void BLE_Task (void *parameters) { while (1) { xSemaphoreTake (bin_sem, portMAX_DELAY); Serial.println ("BLE"); // sending the data delay (10); // bluetooth stack will go into congestion, if too many packets are sent } } Serial.println (); } } void setup () { // put your setup code here, to run once: Serial.begin (115200); vTaskDelay (1000 / portTICK_PERIOD_MS); //BLE Declarations BLEDevice::init ("UART Service"); pServer = BLEDevice::createServer (); pServer->setCallbacks (new MyServerCallbacks ()); BLEService *pService = pServer->createService (SERVICE_UUID); pTxCharacteristic = pService->createCharacteristic (CHARACTERISTIC_UUID_TX, BLECharacteristic:: PROPERTY_NOTIFY); pTxCharacteristic->addDescriptor (new BLE2902 ()); pService->start (); pServer->getAdvertising ()->start (); Serial.println ("Waiting a client connection to notify..."); //BLE Declaration //ADC Semaphore and Timer Declarations bin_sem = xSemaphoreCreateBinary (); bin_sem2 = xSemaphoreCreateBinary (); if (bin_sem == NULL || bin_sem2 == NULL) { Serial.println ("Could not create semaphore"); ESP.restart (); } xTaskCreatePinnedToCore (move_to_Queue, "move_to_Queue", 1024, NULL, 2, NULL, app_cpu); xTaskCreatePinnedToCore (BLE_Task, "BLE_Task", 2048, NULL, 2, NULL, app_cpu); timer = timerBegin (0, timer_divider, true); // Provide ISR to timer (timer, function, edge) timerAttachInterrupt (timer, &onTimer, true); // At what count should ISR trigger (timer, count, autoreload) timerAlarmWrite (timer, timer_max_count, true); // Allow ISR to trigger timerAlarmEnable (timer); vTaskDelete (NULL); } void loop () { // put your main code here, to run repeatedly: }
- 修复中断函数内的变量未定义问题
你在onTimer中断函数中直接使用了task_woken变量,但没有提前声明,这是触发FreeRTOS断言错误的核心原因。按照FreeRTOS的API规范,使用xSemaphoreGiveFromISR时,pxHigherPriorityTaskWoken参数对应的变量必须在ISR内部声明为BaseType_t类型,初始值设置为pdFALSE,修改后代码如下:
void IRAM_ATTR onTimer () { BaseType_t task_woken = pdFALSE; // 此处添加ADC采样逻辑 xSemaphoreGiveFromISR (bin_sem2, &task_woken); portYIELD_FROM_ISR(task_woken); }
删除多余的无效代码
BLE_Task函数结束后多了两处多余的闭合括号和Serial.println();语句,直接删除这部分无效代码,避免逻辑混乱。临界资源加锁保护
Buff_Len、count1属于在中断和任务中都会访问的共享变量,必须加自旋锁保护避免数据竞争,修改move_to_Queue函数如下:
void move_to_Queue (void *parameters) { while (1) { xSemaphoreTake (bin_sem2, portMAX_DELAY); bool buf_full = false; portENTER_CRITICAL(&spinlock); buf_full = (Buff_Len == BUF_SIZE || count1 > 2000); if (!buf_full) { // 此处写入采样值到buf,更新Buff_Len、count1等变量 } portEXIT_CRITICAL(&spinlock); if (buf_full) { Serial.println ("Buffer is full"); xSemaphoreGive (bin_sem); } } }
- 可选优化:高频中断场景下二进制信号量容易丢事件,建议替换为长度不小于16的队列来传输采样值,可靠性更高。
内容的提问来源于stack exchange,提问作者Eswar Reddy

