01、前言
客户在项目中使用STM32F750作为SPI从机,将主机下发的数据通过其它通讯接口转发,非常简单的功能,但是在通讯过程中时常会出现数据转发不全的现象,且会出现SPI OVERRUN的错误。
02、原因分析
2.1 代码逻辑
主机要下发数据前,通过一个IO口拉低,触发从机的EXTI下降沿中断,来通知从机准备接收来自主机的数据,从机启动SPI的DMA接收;随后主机开始下发不定长的数据;主机发送完数据后,经过足够的延时后,再将这个IO拉高,使从机产生一个上升沿中断,从而中止SPI的传输。从触发下降沿到开始下发数据,以及从触发上升沿后到下一次的下降沿的时序,都是通过主机的时序控制逻辑来保证预留足够的时间。
代码逻辑如下:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
{
HAL_SPI_Receive_DMA(&hspi1,SPIRevBuff,MAXSPIBUFLEN);
}
else
{
uint16_t nb_remaining_rx_data = (uint16_t)__HAL_DMA_GET_COUNTER(&hdma_spi1_rx);
HAL_SPI_Abort(&hspi1);
}
}从逻辑来看只要主机处理 GPIO 翻转及发送数据的时序能得到保证,那这种接收方式不应该存在数据丢失的问题。
2.2 问题复现
首先,OVERRUN的产生是由于SPI_DR (FIFO)寄存器中的数据未能及时被读取,而主机又发送了更多新数据,但是由于使用了DMA去搬移接收到的数据,理论上很难出现搬移不及时的情况,除非是在某些情况下导致SPI的DMA接收没有被及时启动,由于SPI外设已经使能,只要主机发送CLK,SPI_DR(FIFO)寄存器就会被压入数据,当FIFO满后,自然就会产生OVERRUN,同时这些数据也会被丢掉,导致后续数据转发不完全。

于是我们在上述代码HAL_SPI_Receive_DMA(&hspi1,SPIRevBuff,MAXSPIBUFLEN)以及HAL_SPI_Abort(&hspi1)前后分别增加了IO口翻转进行测试;发现在HAL_SPI_Abort前后的IO翻转偶尔会达到1S,导致代码卡在EXTI上升沿中断中达1S,在此期间,如果主机又有新的数据要下发,就会再次拉低IO来触发从的的下降沿中断,但由于卡在Abort函数中的1S,无法及时响应下降沿的代码处理,也就是未执行这次的HAL_SPI_Receive_DMA,导致的结果就是这一帧都不会收到数据。
2.3 原因
定位到问题所在,我们就可以继续分析为什么调用Abort函数会导致1S多的延时,继续深入函数内部跟踪,发现由于SPI_EndRxTxTransaction函数调用时间长所致,这个函数中会SPI_FLAG_BSY位清零。
查询相关的勘误手册ES0290,发现在Slave模式时,由于CPU内部时钟和来自Master的CLK时钟的匹配问题,会产生BSY位可能会偶尔保持在高的状态。

手册也给出了相关的Workaround,比如通过硬件NSS,接收模式下使用RXNE事件位等方法。

2.4 问题解决
查看用户使用的FW包版本为STM32Cube FW_F7 V1.16.1,而在1.16.2版本开始FW软件就通过workaround的方式规避了超时的问题。

当超时时间超过1个字节时,即使BSY位高也认为已经传输完成。注意这个超时时间需要用户根据传输速度计算并定义,默认定时为1ms。
更新FW后,再次测试,未再出现相同的问题。
03、总结
本文总结了故障发生的现象,以及发生问题的原因分析与测试步骤。建议用户在出现问题时,能够通过代码逻辑定位问题发生点;对于不合逻辑的问题查找勘误手册是否有说明,并能够及时更新最新的固件驱动包。
来源:STM32
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。