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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:40:54