HAL_SPI_TransmitReceive主从通信应答滞后一个请求周期求助
SPI收发应答滞后问题排查
问题背景
我正在研究HAL_SPI_TransmitReceive的工作特性,编写了主设备程序,每12秒发送1字节数据。从设备侧RX缓冲区数据显示接收序列正常,但通过逻辑分析仪捕捉到异常时序:当从设备收到有效请求(0x5A)时,应答数据会滞后两次传输才被主设备接收到——具体来说,收到0x5A后,预设的应答(0x58)会在第三次请求(发送0x54)时才被主设备读取到。
主设备代码与配置
while循环核心代码
Request = 0x5a; HAL_GPIO_WritePin(GPIOB,"GPIO_PIN_1", GPIO_PIN_RESET); HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)&Request , (uint8_t *)&Responce, 1, 200); HAL_GPIO_WritePin(GPIOB,"GPIO_PIN_1", GPIO_PIN_SET); HAL_Delay(12000); Request = 0x52; HAL_GPIO_WritePin(GPIOB,"GPIO_PIN_1", GPIO_PIN_RESET); HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)&Request , (uint8_t *)&Responce, 1, 200); HAL_GPIO_WritePin(GPIOB,"GPIO_PIN_1", GPIO_PIN_SET); HAL_Delay(12000); Request = 0x54; HAL_GPIO_WritePin(GPIOB,"GPIO_PIN_1", GPIO_PIN_RESET); HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)&Request , (uint8_t *)&Responce, 1, 200); HAL_GPIO_WritePin(GPIOB,"GPIO_PIN_1", GPIO_PIN_SET); HAL_Delay(12000);
SPI配置
hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; 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_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; 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(); }
从设备代码与配置
SPI中断启用代码
NVIC->ISER[1] |= ( 1u<<3 ); //SPI1 Enable SPI1->CR2 |= SPI_CR2_RXNEIE; //SPI_ENABLE SPI1->CR1 |= SPI_CR1_SPE; //SPI_ENABLE //NVIC_EnableIRQ(SPI1_IRQn); //u can this to enable spi interrupt
中断服务程序
SPI_RX_data[SPI_RX_Counter++] = SPI1 -> DR; for(uint8_t i =0 ; i<10 ;i++) { if(SPI_RX_data[i]==0x5a) { SPI1->DR = 0x58; } }
SPI配置
void MX_SPI1_Init(void) { /* USER CODE BEGIN SPI1_Init 0 */ /* USER CODE END SPI1_Init 0 */ /* USER CODE BEGIN SPI1_Init 1 */ /* USER CODE END SPI1_Init 1 */ 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_SOFT; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 7; hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE; hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN SPI1_Init 2 */ /* USER CODE END SPI1_Init 2 */ }
问题原因分析
这个滞后现象的核心是SPI的同步传输机制,结合当前从设备中断逻辑的缺陷导致:
SPI全双工同步传输的本质:主设备每一次
HAL_SPI_TransmitReceive操作,都会在同一组时钟周期内完成双向数据交换——主设备发送1字节的同时,从设备必须已经把要发送的应答数据写入SPI->DR,才能让主设备在本次传输中接收到对应应答。如果在接收完主设备数据后才写入SPI->DR,这个数据只能等待下一次主设备发起传输时才会被发送。中断处理的时序滞后:
- 当主设备发送0x5A时,从设备通过RXNE中断触发并接收数据,此时本次传输已经接近完成,此时写入
SPI->DR的0x58无法在本次传输中被主设备读取,只能留到下一次传输。 - 当主设备发送0x52时,从设备发送的是上一次中断写入的0x58,但主设备此时认为这个数据是对应0x52的应答;而当主设备发送0x54时,从设备可能因为之前的循环逻辑再次写入0x58,最终导致主设备在第三次传输时才接收到对应0x5A的应答,形成“滞后两次传输”的现象。
- 当主设备发送0x5A时,从设备通过RXNE中断触发并接收数据,此时本次传输已经接近完成,此时写入
中断逻辑的冗余干扰:中断服务程序中循环遍历前10个缓冲区元素,每次收到数据都重复检查是否存在0x5A,会导致0x5A被接收后,后续每一次中断都会重复写入0x58到
SPI->DR,进一步打乱应答时序。
修复建议
- 调整应答时机逻辑:如果需要实现“请求-即时应答”,可以让主设备发起两次连续传输:第一次发送请求,第二次专门接收应答;或者在从设备中提前预加载默认应答,收到请求后更新下一次要发送的应答数据。
- 优化中断处理代码:去掉冗余的循环遍历,只针对当前接收的数据判断并写入应答,避免重复操作:
uint8_t received_data = SPI1->DR; SPI_RX_data[SPI_RX_Counter++] = received_data; if(received_data == 0x5a) { SPI1->DR = 0x58; }
内容的提问来源于stack exchange,提问作者shayan ghezelbash
相关产品推荐
相关产品推荐

