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

AVR SPI从机偶发错误传输求助:返回值被右移1位

SPI传输偶发数据右移一位的问题修复

问题根源分析

你遇到的偶发右移1位(100→50),本质是传输未完成就提前读取数据,或者从机SPI标志位未正确清除导致同步错误,核心问题出在两处代码逻辑:

  1. 主机等待SPI传输完成的代码错误,强制置位SPIF标志导致循环提前退出;
  2. 从机未正确处理CS信号和传输完成后的标志位清除。

修复后的主机代码

#define F_CPU 16000000UL
#define CS 2
#define MOSI 3
#define MISO 4
#define SCK 5

#include <avr/io.h>
#include <util/delay.h>
#include <Arduino.h>

uint8_t spiByteReceive;

int main(void){
  DDRB |= (1<<CS)|(1<<MOSI)|(1<<SCK);     //Set CS, MOSI and SCK as outputs
  PORTB |= (1<<CS);     //Set CS pin as default high

  Serial.begin(9600);     //Begin Arduino serial comms

  SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);     //Enable SPI, set as master, set SCK to F_CPU / 128

  while(1){
    PORTB &= ~(1<<CS);      //Pull CS pin low to select slave
    SPDR = 0x00;      //Load dummy byte to start transmission
    while(!(SPSR & (1<<SPIF)));      //Wait for SPI transfer to complete
    spiByteReceive = SPDR;     //Read received data
    
    PORTB |= (1<<CS);     //Pull CS pin high to release slave

    Serial.print(spiByteReceive);      
    Serial.print("\n");

    _delay_ms(200);
  }

  return(0);
}

主机代码修改点:

  • 把while(!(SPSR |= (1<<SPIF)));改为while(!(SPSR & (1<<SPIF)));:原代码用|=会主动把SPIF位置1,导致循环直接结束,此时传输可能只完成了7位,读取到右移后的数据;正确写法是用&检查硬件置位的SPIF标志,确保传输完成。
  • 移除了不必要的_delay_ms(10):SPIF置位就意味着传输已经完成,不需要额外延迟。
  • 补充了#include <Arduino.h>:原代码用Serial.begin()但没包含对应头文件,可能导致编译警告或错误。

修复后的从机代码

#define F_CPU 16000000UL
#define CS 2
#define MOSI 3
#define MISO 4
#define SCK 5

#include <avr/io.h>

uint8_t spiByteSend = 100;

int main(void){
  DDRB |= (1<<MISO);      //Set MISO as output
  DDRB &= ~(1<<CS);       //Set CS as input
  PORTB |= (1<<CS);       //Enable internal pull-up for CS

  SPCR |= (1<<SPE);     //Enable SPI as slave

  while(1){
    // 等待主机拉低CS,才准备发送数据
    while(PINB & (1<<CS));
    
    SPDR = spiByteSend;     //Load byte to send to master
    while(!(SPSR & (1<<SPIF)));      //Wait for transfer to complete
    (void)SPDR; // 读取SPDR以清除SPIF标志,避免下一次传输错误
  }

  return(0);
}

从机代码修改点:

  • 把CS引脚设为输入并启用内部上拉:确保从机能正确感知主机的CS信号,只有当CS拉低时才参与传输。
  • 增加CS等待逻辑:while(PINB & (1<<CS));,避免在CS无效时加载数据到SPDR,导致同步错误。
  • 传输完成后读取SPDR:(void)SPDR;,读取操作会自动清除SPIF标志,防止标志位残留导致下一次传输误判。

额外注意事项

  • 确认接线正确:主机MOSI接从机MOSI,主机MISO接从机MISO,SCK互接,CS主机输出接从机输入,GND共地。
  • SPI模式匹配:主机和从机的CPOL、CPHA默认都是0,不需要额外修改,如果后续改模式要确保两边一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 22:47:45