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

通过SPI实现Arduino从机编码器速度值向主机传输的故障排查

排查SPI传输编码器速度始终为0的问题

咱们先从几个容易踩坑的点入手,一步步解决你的问题:

首先纠正一个关键认知:Arduino的unsigned int是2字节

你提到unsigned int是4字节,但在Arduino(AVR架构,比如Uno/Nano)里,unsigned int是2字节,4字节的是unsigned long。这个数据长度的误解很可能是导致传输错误的根源,得先明确这点。

核心排查方向

1. SPI基础配置与硬件接线检查

  • 接线必须准确:主机的SCK、MOSI、MISO要对应连接从机的同名引脚;主机的SS引脚要连接从机的SS引脚,且主机必须主动拉低SS才能选中从机进行通信。
  • SPI配置完全一致:两边的SPI模式(SPI_MODE0~SPI_MODE3)、时钟分频(比如SPI_CLOCK_DIV16)、数据位长度(默认8位)必须完全匹配。比如主机用SPI.setDataMode(SPI_MODE0),从机也要对应配置。
  • 从机必须启用SPI中断:从机需要通过SPCR |= _BV(SPE)启用从机模式,并且调用SPI.attachInterrupt()来触发数据传输的中断服务函数(ISR)。

2. 数据传输逻辑的问题

SPI是同步通信,主机必须发送一个“dummy字节”来触发从机发送数据。如果是多字节数据(比如2字节的unsigned int),要注意传输顺序和拼接逻辑:

  • 字节序一致性:不管你用union还是手动拆分字节,从机发送字节的顺序(先低字节还是高字节)必须和主机拼接的顺序一致。比如从机先发低字节,主机就要用(高字节 << 8) | 低字节来还原。
  • union的使用是可行的:union确实是转换整数和字节数组的便捷方式,但要确保两边的内存布局一致(AVR架构都是小端模式,所以低字节存在数组的0索引)。示例union:
    union SpeedData {
      unsigned int speed;
      byte bytes[2];
    };
    
  • 从机ISR的正确性:从机的SPI_STC_vect中断函数里,必须把要发送的数据写入SPDR寄存器。比如每次中断发送一个字节,用静态变量记录当前发送的是第几个字节:
    ISR(SPI_STC_vect) {
      static byte byteIndex = 0;
      SPDR = speedData.bytes[byteIndex]; // 发送当前字节
      byteIndex = (byteIndex + 1) % 2; // 循环切换字节索引
    }
    

3. 等待时长的误区

SPI的transfer()函数本身就是阻塞式的,会等待传输完成后再返回结果,所以额外加100ms等待其实没用——问题根本不在处理时间,而是配置或传输逻辑错误。

4. 调试技巧

  • 先隔离编码器问题:在从机里先固定发送一个已知值(比如1234),不要读编码器,看主机能不能接收到这个值。如果能,说明SPI通信正常,问题出在编码器读取逻辑;如果还是0,说明SPI本身有问题。
  • 用串口辅助调试:在从机的loop里打印当前的encoderSpeed,确认编码器读取的数值是正确的;在从机的ISR里也可以加串口打印(注意ISR里串口打印要谨慎,最好用非阻塞方式,或者先测试通信正常后再去掉)。

参考代码框架

主机代码

#include <SPI.h>
#define SS_PIN 10

void setup() {
  Serial.begin(9600);
  SPI.begin();
  pinMode(SS_PIN, OUTPUT);
  digitalWrite(SS_PIN, HIGH); // 默认拉高SS,不选中从机
}

void loop() {
  digitalWrite(SS_PIN, LOW); // 选中从机开始通信
  // 发送两个dummy字节,接收从机返回的两个字节
  byte lowByte = SPI.transfer(0x00);
  byte highByte = SPI.transfer(0x00);
  digitalWrite(SS_PIN, HIGH); // 结束通信,拉高SS

  unsigned int speed = (highByte << 8) | lowByte; // 拼接成unsigned int
  Serial.print("Received speed: ");
  Serial.println(speed);
  delay(100);
}

从机代码

#include <SPI.h>
unsigned int encoderSpeed = 1234; // 先固定一个测试值
union SpeedData {
  unsigned int speed;
  byte bytes[2];
} speedData;

void setup() {
  Serial.begin(9600);
  pinMode(MISO, OUTPUT); // 从机MISO必须设为输出
  SPCR |= _BV(SPE); // 启用SPI从机模式
  SPI.attachInterrupt(); // 启用SPI中断
  speedData.speed = encoderSpeed;
}

ISR(SPI_STC_vect) {
  static byte byteIndex = 0;
  // 读取主机发送的dummy字节(可选,这里不用处理)
  (void)SPDR;
  // 发送当前字节
  SPDR = speedData.bytes[byteIndex];
  byteIndex = (byteIndex + 1) % 2;
}

void loop() {
  // 这里替换成你的编码器读取逻辑,比如:
  // encoderSpeed = readYourEncoder();
  speedData.speed = encoderSpeed;
  // 可选:打印当前速度,确认编码器读取正常
  // Serial.print("Current speed: ");
  // Serial.println(encoderSpeed);
  delay(50);
}

先测试这个固定值的版本,确认SPI能正常传输后,再替换成编码器读取的逻辑,这样就能一步步定位问题啦。

内容的提问来源于stack exchange,提问作者mecheng6769

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:42:45