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

如何使用ESP32DMASPI实现ESP32(从机)向Arduino(主机)传输数据

SPI通信逻辑澄清

首先你对SPI的工作逻辑理解有误,SPI是同步全双工通信总线,不存在「MISO数据等于MOSI数据加从机追加数据」的规则:

  • 所有SPI传输完全由主机发起,主机提供时钟信号,每产生1个时钟脉冲,MOSI和MISO各传输1位数据,收发是同时完成的
  • 主机发N个字节的同时,必然会收到从机返回的N个字节,不存在从机主动向主机发送数据的情况
  • 你说的类似on_request的效果,本质是通过自定义上层协议实现:主机先发送1个指定命令字节告知从机「我要读数据」,随后主机连续发送N个无意义的空字节(通常是0x00)来提供时钟,从机在收到读命令后,把要返回的数据提前放到MISO的发送缓冲区,在后续的时钟周期依次发出即可。

ESP32DMASPI实现从机回传的方法

ESP32DMASPI库本身支持同时配置DMA收发缓冲区,完全可以实现上述功能,具体实现步骤如下:

1. ESP32从机端修改

初始化时同时开辟RX和TX的DMA缓冲区,你现有的代码只处理了RX逻辑,需要补充TX缓冲的预填逻辑:

#include <ESP32DMASPISlave.h>

#define BUFFER_SIZE 32
ESP32DMASPISlave spi(HSPI); // 也可以用VSPI,根据你的引脚配置调整

// 收发缓冲区
uint8_t spi_slave_rx_buf[BUFFER_SIZE] = {0};
uint8_t spi_slave_tx_buf[BUFFER_SIZE] = {0};

void setup() {
  spi.setDMAChannel(1);
  spi.setMOSI(13);
  spi.setMISO(12);
  spi.setSCLK(14);
  spi.setCS(15);
  // 同时配置收发缓冲大小
  spi.begin(BUFFER_SIZE, BUFFER_SIZE);
  // 初始化时先把TX缓冲填默认值,避免返回随机数据
  memset(spi_slave_tx_buf, 0xFF, BUFFER_SIZE);
  spi.queue(spi_slave_tx_buf, BUFFER_SIZE);
}

void loop() {
  while(spi.available()) {
    // 读取主机发过来的数据
    uint8_t data[BUFFER_SIZE] = {0};
    for (size_t i = 0; i < BUFFER_SIZE; ++i) {
      data[i] = spi_slave_rx_buf[i];
      spi_slave_rx_buf[i] = 0;
    }
    // 处理主机命令
    if(data[0] == 0x02) { // 自定义的读数据命令,可自行修改命令值
      // 把要返回的数据填到TX缓冲里,比如替换为你的传感器采集值、状态值等
      spi_slave_tx_buf[0] = 0xAA; // 自定义帧头
      spi_slave_tx_buf[1] = 25; // 温度示例值
      spi_slave_tx_buf[2] = 60; // 湿度示例值
      spi_slave_tx_buf[3] = 0xBB; // 自定义帧尾
    }
    // 把新的TX缓冲提交到队列,下次传输时自动通过MISO发出
    spi.queue(spi_slave_tx_buf, BUFFER_SIZE);
    user_onReceive(data);
    spi.pop();
  }
}

2. Arduino Uno主机端逻辑

主机要按照约定的协议发起读写请求,读数据的示例代码如下:

#include <SPI.h>
#define CS_PIN 10

void setup() {
  SPI.begin();
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH);
  Serial.begin(9600);
}

void loop() {
  uint8_t read_buf[4] = {0};
  digitalWrite(CS_PIN, LOW);
  // 先发读命令0x02,随后发4个空字节获取从机返回数据
  SPI.transfer(0x02);
  read_buf[0] = SPI.transfer(0x00);
  read_buf[1] = SPI.transfer(0x00);
  read_buf[2] = SPI.transfer(0x00);
  read_buf[3] = SPI.transfer(0x00);
  digitalWrite(CS_PIN, HIGH);

  // 打印拿到的从机数据
  Serial.print("从机返回:");
  for(int i=0; i<4; i++) {
    Serial.print(read_buf[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
  delay(1000);
}

注意事项

  • 因为SPI是全双工同步传输,第一次发读命令时拿到的第一个字节,是从机上一次传输存在TX缓冲里的数据,所以如果需要严格对齐数据,要么在协议里预留1个字节的偏移,要么主机连续发两次读命令,取第二次的返回即可。
  • 缓冲区大小要在主从机两端保持一致,避免DMA传输出现截断。

内容的提问来源于stack exchange,提问作者the-dstrp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 16:15:05