ESP32通过SPI接收数据发送至Android时的数据不匹配问题求助
SPI数据索引不匹配问题排查与修复
问题背景
通过ESP32的SPI从机模式获取数据,再通过WiFi发送到Android设备,但WiFi发送的耗时导致SPI数据出现数组索引不匹配的情况。以下是当前使用的ESP32代码:
/*********************************************************************************/ #include <ESP32SPISlave.h> #include <WiFi.h> ESP32SPISlave slave; #define StatusLED 27 const char* ssid = "YOUR_ID"; const char* password = "YOUR_PASSWORD"; WiFiServer server(80); IPAddress local_IP(192,168,3,14); IPAddress gateway(192,168,3,5); IPAddress subnet(255,255,255,0); static constexpr uint32_t BUFFER_SIZE {10}; uint8_t spi_slave_tx_buf[BUFFER_SIZE]; uint8_t spi_slave_rx_buf[BUFFER_SIZE]; uint8_t spi_slave_rx_Localbuf[BUFFER_SIZE]; uint32_t Client_buf[4]; bool WIFI_Flag; byte count = 0; void setup() { Serial.begin(115200); delay(2000); // LED init pinMode(StatusLED, OUTPUT); // HSPI = CS: 15, CLK: 14, MOSI: 13, MISO: 12 -> default // VSPI = CS: 5, CLK: 18, MOSI: 23, MISO: 19 slave.setDataMode(SPI_MODE0); slave.begin(); // clear buffers memset(spi_slave_tx_buf, 0, BUFFER_SIZE); memset(spi_slave_rx_buf, 0, BUFFER_SIZE); // We start by connecting to a WiFi network Serial.print("Setting AP (Access Point)…"); Serial.println(WiFi.softAPConfig(local_IP,gateway,subnet)?"Ready" : "Failed!"); //*server = start_webserver(); Serial.print("Starting Access Point)…"); Serial.println( WiFi.softAP(ssid, password)?"Ready" : "Failed!"); Serial.print("AP IP address: "); Serial.println(WiFi.softAPIP()); server.begin(); } void loop() { // Switch OFF LED // Switch ON LED digitalWrite(StatusLED, HIGH); // turn the LED on (HIGH is the voltage level) if (slave.remained() == 0) { slave.queue(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE); } // if transaction has completed from master, // available() returns size of results of transaction, // and buffer is automatically updated if (slave.available()) { // Switch ON LED digitalWrite(StatusLED, LOW); // turn the LED off (LOW is the voltage level) if(count != 0) { switch(count) { case 1: {Client_buf[0] = spi_slave_rx_buf[0]; break;} case 2: {Client_buf[0] |= spi_slave_rx_buf[0] << 8; break;} case 3: {Client_buf[1] = spi_slave_rx_buf[0]; break;} case 4: {Client_buf[1] |= spi_slave_rx_buf[0] << 8; break;} case 5: {Client_buf[2] = spi_slave_rx_buf[0]; break;} case 6: {Client_buf[2] |= spi_slave_rx_buf[0]<<8; break;} case 7: {Client_buf[3] = spi_slave_rx_buf[0]; break;} case 8: {Client_buf[3] |= spi_slave_rx_buf[0]<<8; WIFI_Flag = 1; break;} } } count++; slave.pop(); } if(WIFI_Flag) { WiFiClient client = server.available(); client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(""); client.print(Client_buf[0]); client.print(","); client.print(Client_buf[1]); client.print(","); client.print(Client_buf[2]); client.print(","); client.print(Client_buf[3]); WIFI_Flag = 0; count = 0; } } /*********************************************************************************/
问题根源与修复方案
核心问题
- SPI数据被覆盖:WiFi发送耗时阻塞主循环,导致新的SPI传输直接覆盖
spi_slave_rx_buf中未处理的数据,引发索引错位。 - 计数逻辑混乱:
count变量在SPI事务和WiFi发送两个流程中被修改,多任务环境下容易出现计数错误。 - WiFi发送阻塞主循环:直接在主循环中处理WiFi发送,占用大量时间,导致SPI数据处理不及时。
修复步骤
1. 新增缓存区存储SPI数据
在SPI事务完成后立即将数据存入独立缓存,避免被后续传输覆盖:
// 全局变量新增 uint8_t spi_rx_cache[8]; // 对应8次SPI传输的单字节数据 byte cache_index = 0;
在SPI数据处理块中修改为:
if (slave.available()) { digitalWrite(StatusLED, LOW); // 将当前SPI数据存入缓存 if(cache_index < 8){ spi_rx_cache[cache_index] = spi_slave_rx_buf[0]; cache_index++; } // 缓存满8个字节时组装完整数据 if(cache_index == 8){ uint32_t temp_buf[4]; temp_buf[0] = spi_rx_cache[0] | (spi_rx_cache[1] << 8); temp_buf[1] = spi_rx_cache[2] | (spi_rx_cache[3] << 8); temp_buf[2] = spi_rx_cache[4] | (spi_rx_cache[5] << 8); temp_buf[3] = spi_rx_cache[6] | (spi_rx_cache[7] << 8); // 后续将temp_buf发送到WiFi任务 cache_index = 0; } slave.pop(); digitalWrite(StatusLED, HIGH); }
2. 创建独立WiFi发送任务
使用FreeRTOS创建单独的WiFi发送任务,避免阻塞主循环的SPI处理:
// 全局变量新增 QueueHandle_t data_queue; // WiFi发送任务函数 void wifi_send_task(void *pvParameters){ WiFiClient client; uint32_t send_buf[4]; while(1){ // 等待队列中的数据 if(xQueueReceive(data_queue, send_buf, portMAX_DELAY)){ client = server.available(); if(client && client.connected()){ client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(""); client.print(send_buf[0]); client.print(","); client.print(send_buf[1]); client.print(","); client.print(send_buf[2]); client.print(","); client.print(send_buf[3]); client.flush(); client.stop(); } } } } // 在setup()中初始化队列和任务 void setup(){ // ... 原有代码 ... data_queue = xQueueCreate(5, sizeof(uint32_t)*4); // 最多缓存5组数据 xTaskCreate(wifi_send_task, "WiFiSend", 2048, NULL, 1, NULL); }
然后在缓存满时将数据发送到队列:
if(cache_index == 8){ uint32_t temp_buf[4]; temp_buf[0] = spi_rx_cache[0] | (spi_rx_cache[1] << 8); temp_buf[1] = spi_rx_cache[2] | (spi_rx_cache[3] << 8); temp_buf[2] = spi_rx_cache[4] | (spi_rx_cache[5] << 8); temp_buf[3] = spi_rx_cache[6] | (spi_rx_cache[7] << 8); xQueueSend(data_queue, temp_buf, 0); // 非阻塞发送 cache_index = 0; }
3. 移除冗余计数变量
删除原有的count和WIFI_Flag变量,改用cache_index追踪缓存进度,简化逻辑。
完整修复后代码
#include <ESP32SPISlave.h> #include <WiFi.h> #include <freertos/FreeRTOS.h> #include <freertos/queue.h> ESP32SPISlave slave; #define StatusLED 27 const char* ssid = "YOUR_ID"; const char* password = "YOUR_PASSWORD"; WiFiServer server(80); IPAddress local_IP(192,168,3,14); IPAddress gateway(192,168,3,5); IPAddress subnet(255,255,255,0); static constexpr uint32_t BUFFER_SIZE {10}; uint8_t spi_slave_tx_buf[BUFFER_SIZE]; uint8_t spi_slave_rx_buf[BUFFER_SIZE]; uint8_t spi_rx_cache[8]; QueueHandle_t data_queue; byte cache_index = 0; void wifi_send_task(void *pvParameters){ WiFiClient client; uint32_t send_buf[4]; while(1){ if(xQueueReceive(data_queue, send_buf, portMAX_DELAY)){ client = server.available(); if(client && client.connected()){ client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println(""); client.print(send_buf[0]); client.print(","); client.print(send_buf[1]); client.print(","); client.print(send_buf[2]); client.print(","); client.print(send_buf[3]); client.flush(); client.stop(); } } } } void setup() { Serial.begin(115200); delay(2000); pinMode(StatusLED, OUTPUT); slave.setDataMode(SPI_MODE0); slave.begin(); memset(spi_slave_tx_buf, 0, BUFFER_SIZE); memset(spi_slave_rx_buf, 0, BUFFER_SIZE); memset(spi_rx_cache, 0, sizeof(spi_rx_cache)); Serial.print("Setting AP (Access Point)…"); Serial.println(WiFi.softAPConfig(local_IP,gateway,subnet)?"Ready" : "Failed!"); Serial.print("Starting Access Point)…"); Serial.println( WiFi.softAP(ssid, password)?"Ready" : "Failed!"); Serial.print("AP IP address: "); Serial.println(WiFi.softAPIP()); server.begin(); data_queue = xQueueCreate(5, sizeof(uint32_t)*4); xTaskCreate(wifi_send_task, "WiFiSend", 2048, NULL, 1, NULL); } void loop() { digitalWrite(StatusLED, HIGH); // 确保SPI队列始终就绪,不会错过主设备传输 if (slave.remained() == 0) { slave.queue(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE); } if (slave.available()) { digitalWrite(StatusLED, LOW); if(cache_index < 8){ spi_rx_cache[cache_index] = spi_slave_rx_buf[0]; cache_index++; } if(cache_index == 8){ uint32_t temp_buf[4]; temp_buf[0] = spi_rx_cache[0] | (spi_rx_cache[1] << 8); temp_buf[1] = spi_rx_cache[2] | (spi_rx_cache[3] << 8); temp_buf[2] = spi_rx_cache[4] | (spi_rx_cache[5] << 8); temp_buf[3] = spi_rx_cache[6] | (spi_rx_cache[7] << 8); xQueueSend(data_queue, temp_buf, 0); cache_index = 0; } slave.pop(); } }
内容的提问来源于stack exchange,提问作者Venugopal
相关产品推荐
相关产品推荐

