STM32F411 SPI从机收发数据异常问题排查求助
STM32F411 SPI主从通信数据错乱问题排查
问题背景
使用STM32F411搭建主从机SPI通信系统:主机发送数据,从机负责将数据写入挂载的SD卡以分担主机负载。从机采用轮询方式处理SPI,未使用DMA或中断,两个项目均通过CubeIDE搭建。
当前问题:从机接收的数据存在错乱、移位或丢失字符的情况,且向主机回传的数据为无效dummy值。逻辑分析仪显示主机发送数据正常,单字节收发的sanity check可正常工作,但多字节传输异常。
主机SPI配置
static void MX_SPI3_Init(void) { hspi3.Instance = SPI3; hspi3.Init.Mode = SPI_MODE_MASTER; hspi3.Init.Direction = SPI_DIRECTION_2LINES; hspi3.Init.DataSize = SPI_DATASIZE_8BIT; hspi3.Init.CLKPolarity = SPI_POLARITY_LOW; hspi3.Init.CLKPhase = SPI_PHASE_1EDGE; hspi3.Init.NSS = SPI_NSS_SOFT; hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi3.Init.TIMode = SPI_TIMODE_DISABLE; hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi3.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi3) != HAL_OK) { Error_Handler(); } }
从机SPI配置
static void MX_SPI1_Init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_SLAVE; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_HARD_INPUT; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } }
主机通信逻辑
示例调用:
sd_open_file("Quadcopter.txt", FA_WRITE | FA_READ | FA_OPEN_ALWAYS);
核心函数实现:
uint8_t sd_open_file(const char *file_name, uint8_t instruction){ uint8_t response[1]; uint8_t command = LOGGER_SD_OPEN_FILE; uint16_t file_length = strlen(file_name)+1; uint16_t total_length = file_length+1; uint8_t transmit_length[] = {(total_length >> 8) & 0xFF, total_length & 0xFF}; uint8_t transmit_buffer[total_length]; // string length, \0 and command transmit_buffer[0] = instruction; memcpy(&transmit_buffer[1], file_name, file_length); // Copy the string including null terminator. HAL_StatusTypeDef status; slave_select(); status = HAL_SPI_Transmit(m_device_handle, &command, 1, 5000); // send command status = HAL_SPI_Transmit(m_device_handle, transmit_length, 2, 5000); // send how many bytes the dat will be status = HAL_SPI_Transmit(m_device_handle, transmit_buffer, total_length, 5000); // send data status = HAL_SPI_Receive(m_device_handle, response, 1, 5000); uint8_t data[200]; status = HAL_SPI_Receive(m_device_handle, data, total_length, 1000); slave_deselect(); ... }
从机通信逻辑
HAL_StatusTypeDef spi_slave_result_receive = HAL_SPI_Receive(&hspi1, slave_buffer, 1, 1000); ... HAL_StatusTypeDef status_receive; // We ask how much data should we receive. if((status_receive = HAL_SPI_Receive(&hspi1, slave_buffer, 2, 1000)) != HAL_OK){ break; } uint16_t amount_of_data_to_receive = (slave_buffer[0] << 8) | slave_buffer[1]; // Receive the instruction and file name if( (status_receive = HAL_SPI_Receive(&hspi1, slave_buffer, amount_of_data_to_receive, 1000)) != HAL_OK){ break; } // instruction is byte 0 char* extracted_string = extract_string_from_spi_data_at_index(slave_buffer, SLAVE_BUFFER_SIZE, 1); volatile uint8_t result = sd_open_file(extracted_string, slave_buffer[0]); HAL_SPI_Transmit(&hspi1, &result, 1, 100); HAL_SPI_Transmit(&hspi1, slave_buffer, amount_of_data_to_receive, 100); free(extracted_string);
已尝试的调试手段
- 降低SPI总线频率
- 调整时钟极性(CPOL)和相位(CPHA)
- 改用单一缓冲区接收所有数据
- 单字节收发sanity check正常,但多字节传输仍异常
解决思路与设计缺陷分析
1. NSS信号同步问题
主机使用软件NSS,从机使用硬件NSS,需确保:
- 主机
slave_select()必须在所有传输操作前拉低NSS,slave_deselect()在所有传输(包括接收)完成后再拉高,禁止在HAL_SPI_Transmit/Receive调用之间切换NSS电平,否则从机会误判传输结束,导致同步丢失 - 检查
slave_select()/slave_deselect()的GPIO操作逻辑,确保无延迟或误操作
2. SPI全双工特性误用
SPI是全双工总线,每个时钟周期同时完成收发:
- 主机单独调用
HAL_SPI_Transmit时,从机实际会发送dummy数据;主机单独调用HAL_SPI_Receive时,必须发送dummy字节来产生时钟,从机才能发送有效数据 - 当前逻辑中,从机在接收完数据后调用
HAL_SPI_Transmit,但此时主机如果没有主动发送时钟(即调用HAL_SPI_Receive),从机无法完成发送,导致主机接收到无效dummy值 - 建议改为连续全双工传输:主机将所有要发送的数据(命令+长度+dummy字节用于接收响应)打包,在单次
HAL_SPI_TransmitReceive调用中完成收发,确保时钟连续
3. 传输时序断裂问题
主机将传输拆分为多个独立的HAL_SPI_Transmit/Receive调用,会导致SPI时钟中断:
- 从机在时钟中断期间可能进入空闲状态,丢失同步,导致后续接收数据错乱
- 优化方案:将主机的发送数据(命令+长度+传输数据)和需要接收的响应(结果+回传数据)合并为一个连续的传输流程,用单次全双工操作完成
4. 从机轮询响应延迟
从机在调用sd_open_file时会处理SD卡操作,占用大量时间:
- 此时主机如果开始接收数据,从机无法及时准备有效回传数据,只能发送dummy值
- 调整逻辑:从机先接收完所有主机数据,立即发送响应字节,再处理SD卡操作;或者在处理SD卡操作前,确保主机已经完成所有发送操作,等待从机处理完成后再触发回传
5. 缓冲区与数据解析问题
- 检查从机
slave_buffer的大小,确保amount_of_data_to_receive不会超过缓冲区容量,避免溢出导致数据错乱 - 确认长度字段的端序:主机发送大端模式(高字节在前),从机解析逻辑匹配,但需验证实际接收的字节顺序是否正确
6. 硬件层面排查
- 检查SPI线路接线:SCK、MOSI、MISO、NSS引脚是否连接牢固,有无电磁干扰
- 确认从机NSS引脚配置为硬件输入,未被其他GPIO配置覆盖
- 确保主从机供电稳定,避免电压波动影响SPI通信
内容的提问来源于stack exchange,提问作者Rokas1369
相关产品推荐
相关产品推荐

