STM32G030 SPI从模式接收正常、发送数据重复卡顿问题求助
问题根因分析
你遇到的SPI从机发送数据重复问题是典型的从机时序逻辑错误,核心原因有3个:
- SPI从机没有主动发送数据的权利,所有传输必须由主机提供时钟驱动,你当前写的
spi_send_string函数主动往DR寄存器塞数据,但此时树莓派没有发起时钟,第一个写入的字节会一直存在发送移位寄存器中,后续主机发起读取时每打8个时钟就会把这个字节发一次,就出现了全是重复值的情况 - SPI配置的NSS模式和实际硬件不匹配,且
SPI_Receive_Commands函数中存在语法bug:while(HAL_GPIO_ReadPin(SPI_SS_GPIO_Port, SPI_SS_Pin) == GPIO_PIN_RESET);后面多了个分号,逻辑变成等CS引脚拉高之后才接收数据,时序完全错位 - 混用HAL库API和直接寄存器操作,HAL_SPI_Receive会重置SPI的状态标志位,和你直接读写DR寄存器的操作冲突
可行解决方案
1. 修正SPI配置
如果你的CS引脚连接到了STM32的SPI1_NSS硬件引脚,把NSS模式从SPI_NSS_SOFT改成SPI_NSS_HARD_INPUT,让硬件自动处理CS选中逻辑;如果用的是普通GPIO模拟CS检测,保留软件NSS的同时,每次检测到CS拉低时手动设置SET_BIT(SPI1->CR1, SPI_CR1_SSI),CS拉高时清除该位,否则SPI外设会认为自身未被选中,不响应传输。
// 修正后的SPI配置片段 hspi1.Init.NSS = SPI_NSS_HARD_INPUT; // 硬件CS引脚选这个 // 如果用软件CS,保留SPI_NSS_SOFT,额外加下面的控制逻辑 // if(HAL_GPIO_ReadPin(SPI_SS_GPIO_Port, SPI_SS_Pin) == GPIO_PIN_RESET) SET_BIT(SPI1->CR1, SPI_CR1_SSI); // else CLEAR_BIT(SPI1->CR1, SPI_CR1_SSI);
2. 重写符合从机规则的收发逻辑
SPI是全双工同步总线,每接收1个字节的同时必然会发送1个字节,采用中断方式处理收发最稳定,不要主动批量写发送缓冲区,提前把要发的数据装填好,等主机时钟驱动时逐字节发送:
// 定义全局发送缓冲区和指针 uint8_t tx_buf[32]; uint8_t tx_len = 0; uint8_t tx_ptr = 0; // 开启SPI1 RXNE中断,在MX_SPI1_Init末尾加 __HAL_SPI_ENABLE_IT(&hspi1, SPI_IT_RXNE); HAL_NVIC_EnableIRQ(SPI1_IRQn); // 中断服务函数 void SPI1_IRQHandler(void) { if(__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_RXNE)) { uint8_t rx_data = (uint8_t)SPI1->DR; // 收到读电压指令 if(rx_data == 'v') { // 提前装填ADC数据到发送缓冲区 tx_len = sprintf((char*)tx_buf, "%.2f", get_adc_voltage()); tx_ptr = 0; // 把第一个要发的字节提前放到DR,等主机来取 SPI1->DR = tx_buf[tx_ptr++]; } else { // 后续主机发空字节驱动时钟,逐字节发送数据 if(tx_ptr < tx_len) { SPI1->DR = tx_buf[tx_ptr++]; } else { // 发完后返回结束标志,方便主机判断 SPI1->DR = 0xFF; } } // 清除溢出标志,避免后续收发异常 __HAL_SPI_CLEAR_OVRFLAG(&hspi1); } }
3. 修正树莓派侧的读取逻辑
树莓派发完指令后,需要连续发送N个空字节(N等于你要接收的最大数据长度)来驱动时钟,才能拿到完整数据:
import spidev spi = spidev.SpiDev() spi.open(0, 0) spi.max_speed_hz = 1000000 # 从机模式下不要超过4M,先设1M测试 spi.mode = 0 # 和STM32配置的CPOL=0、CPHA=1EDGE对应 # 发'v'指令,同时读10个字节 resp = spi.xfer2([ord('v')] + [0x00]*10) # 第一个字节是发指令时返回的无效值,从第二个开始是有效数据 print("收到的电压数据:", resp[1:tx_len])
4. 其他优化点
- 删掉
SPI_read函数中多余的while(SPI1->SR & SPI_SR_BSY),RXNE置1时数据已经完成接收,不需要等BSY清零,反而会增加延迟丢包 - 尽量不要混用HAL库API和直接寄存器操作,要么全用HAL的中断收发,要么全用寄存器操作,避免状态位冲突
内容的提问来源于stack exchange,提问作者Nishant Nidaria
相关产品推荐
相关产品推荐

