AVR SPI从机偶发错误传输求助:返回值被右移1位
SPI传输偶发数据右移一位的问题修复
问题根源分析
你遇到的偶发右移1位(100→50),本质是传输未完成就提前读取数据,或者从机SPI标志位未正确清除导致同步错误,核心问题出在两处代码逻辑:
- 主机等待SPI传输完成的代码错误,强制置位SPIF标志导致循环提前退出;
- 从机未正确处理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
相关产品推荐
相关产品推荐

