ESP32同时驱动PN532(I2C)与SD读卡器(SPI)触发xQueueGenericSend断言失败求助
ESP32同时使用I2C PN532与SPI SD卡触发断言失败重启问题
问题现象
ESP32通过I2C连接PN532 NFC模块,SPI连接SD读卡器:
- 单独测试PN532功能正常
- 同时启用两者后,调用
nfc.readPassiveTargetID必触发报错并强制重启,错误信息:
assert failed: xQueueGenericSend queue.c:873 (!( ( pvItemToQueue == ((void *)0) ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ))
已尝试无效方案
- 添加NFC和SD操作的互斥锁
- 将循环任务绑定到核心0
问题代码
#include <Wire.h> #include <PN532_I2C.h> #include <PN532.h> #include <NfcAdapter.h> #include <WiFi.h> #include <WiFiUdp.h> #include <NTPClient.h> #include <FS.h> #include <SD.h> #include <SPI.h> #include <string.h> const char *ssid = "........"; const char *password = "********"; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "ntp.aliyun.com", 3600 * 8, 60000); PN532_I2C pn532i2c(Wire); PN532 nfc(pn532i2c); File fRace; char RaceFileName[30]; void appendFile(fs::FS &fs, const char *path, const char *message); SemaphoreHandle_t nfcMutex; SemaphoreHandle_t sdMutex; void loop_task(void *pvParameters) { while (true) { myloop(); vTaskDelay(10 / portTICK_PERIOD_MS); } } void setup(void) { nfcMutex = xSemaphoreCreateMutex(); sdMutex = xSemaphoreCreateMutex(); Serial.begin(115200); Serial.println("Hello!"); Serial.print("Connecting to "); Serial.print(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("Connect success."); timeClient.begin(); Serial.println("SD&File begin."); if (!SD.begin()) { Serial.println("Card Mount Failed"); return; } uint8_t cardType = SD.cardType(); if (cardType == CARD_NONE) { Serial.println("No SD card attached"); return; } Serial.print("SD Card Type: "); if (cardType == CARD_MMC) { Serial.println("MMC"); } else if (cardType == CARD_SD) { Serial.println("SDSC"); } else if (cardType == CARD_SDHC) { Serial.println("SDHC"); } else { Serial.println("UNKNOWN"); } uint64_t cardSize = SD.cardSize() / (1024 * 1024); Serial.printf("SD Card Size: %lluMB\n", cardSize); timeClient.update(); sprintf(RaceFileName, "/race%lu.csv", timeClient.getEpochTime() / 10000); Serial.println(timeClient.getEpochTime(), DEC); fRace = SD.open(RaceFileName, FILE_APPEND); if (!fRace) { fRace = SD.open(RaceFileName, FILE_WRITE); fRace.print(""); } fRace.close(); Serial.println("NFC begin."); nfc.begin(); uint32_t versiondata = nfc.getFirmwareVersion(); if (!versiondata) { Serial.print("Didn't find PN53x board"); while (1) ; // halt } Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX); Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC); Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC); nfc.setPassiveActivationRetries(0xFF); nfc.SAMConfig(); xTaskCreatePinnedToCore(loop_task, "loop_task", 4096, NULL, 1, NULL, 0); } void loop(void) {} void myloop(void) { boolean success; uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t uidLength; char tmp[25] = { 0 }; Serial.println("Waiting for an ISO14443A card"); delay(200); if (xSemaphoreTake(nfcMutex, portMAX_DELAY) == pdTRUE) { success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength); xSemaphoreGive(nfcMutex); } if (success) { for (uint8_t i = 0; i < uidLength; i++) { Serial.print(" 0x"); Serial.print(uid[i], HEX); } Serial.println(""); sprintf(tmp, "%lu,%lu", *(unsigned long *)uid, timeClient.getEpochTime()); if (xSemaphoreTake(sdMutex, portMAX_DELAY) == pdTRUE) { appendFile(SD, RaceFileName, tmp); xSemaphoreGive(sdMutex); } } else { Serial.println("Timed out waiting for a card"); } delay(1000); } void appendFile(fs::FS &fs, const char *path, const char *message) { Serial.printf("Appending to file: %s\n", path); File file = fs.open(path, FILE_APPEND); if (!file) { Serial.println("Failed to open file for appending"); return; } if (file.print(message)) { Serial.println("Message appended"); } else { Serial.println("Append failed"); } file.close(); }
解决方案
1. 根源分析
该断言错误是FreeRTOS队列操作时传入空指针,但队列要求非空元素导致的。结合场景,核心原因是I2C与SPI总线的硬件资源竞争,以及单任务混合操作两种外设导致的任务调度冲突。
2. 具体修复步骤
(1)拆分任务到不同核心
将NFC识别任务和SD卡写入任务分离,分别绑定到ESP32的两个核心,避免资源抢占:
QueueHandle_t sdQueue; // NFC专属任务,绑定到核心0 void nfc_task(void *pvParameters) { while (true) { boolean success; uint8_t uid[] = {0,0,0,0,0,0,0,0}; uint8_t uidLength; char tmp[25] = {0}; Serial.println("Waiting for an ISO14443A card"); vTaskDelay(pdMS_TO_TICKS(200)); if (xSemaphoreTake(nfcMutex, portMAX_DELAY) == pdTRUE) { success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength); xSemaphoreGive(nfcMutex); } if (success) { for (uint8_t i = 0; i < uidLength; i++) { Serial.print(" 0x"); Serial.print(uid[i], HEX); } Serial.println(""); sprintf(tmp, "%lu,%lu", *(unsigned long *)uid, timeClient.getEpochTime()); // 将数据发送到SD任务队列,而非直接操作SD xQueueSend(sdQueue, &tmp, portMAX_DELAY); } else { Serial.println("Timed out waiting for a card"); } vTaskDelay(pdMS_TO_TICKS(1000)); } } // SD卡专属任务,绑定到核心1 void sd_write_task(void *pvParameters) { char message[25]; while (true) { if (xQueueReceive(sdQueue, &message, portMAX_DELAY)) { if (xSemaphoreTake(sdMutex, portMAX_DELAY) == pdTRUE) { appendFile(SD, RaceFileName, message); xSemaphoreGive(sdMutex); } } } } // 在setup中创建队列和任务 void setup(void) { // ... 其他初始化代码 ... sdQueue = xQueueCreate(10, sizeof(char[25])); xTaskCreatePinnedToCore(nfc_task, "nfc_task", 4096, NULL, 1, NULL, 0); xTaskCreatePinnedToCore(sd_write_task, "sd_write_task", 4096, NULL, 1, NULL, 1); }
(2)替换delay为vTaskDelay
原代码中的delay()会阻塞当前任务,导致队列超时或资源异常,全部替换为FreeRTOS的vTaskDelay(pdMS_TO_TICKS(毫秒数))。
(3)检查引脚冲突
确认ESP32的I2C引脚(默认SDA=21、SCL=22)与SPI引脚(默认SS=5、MOSI=23、MISO=19、SCK=18)无重叠,若模块有自定义引脚需调整。
(4)固定I2C通信速率
显式指定I2C速率为100kHz,避免与SPI总线速率冲突:
Wire.begin(21,22,100000); // 在nfc.begin()前调用
内容的提问来源于stack exchange,提问作者50han Bill
相关产品推荐
相关产品推荐

