硬件平台:STM32F407 开发平台:MDK
问题描述:
这几天在调试STM32 串口通讯时,突然发现某些发送的数据不对,如下所示:
本来需要发送的是:
24 06 FF 03 00 36 8C 0A AA 55
结果变成了:
24 06 FF 10 00 19 02 0C DD DD
其中,发送的数据变成了下一帧需要发送的数据的一部分。
由于我的串口通讯采用的是DMA发送,模式配置为Normal(每次发送都需要重新启动和清除标志)。
我清除标志的方式采用的是如下方式:
if(DMA_GetFlagStatus(RS485_USART_DMA_STREAM, RS485_USART_DMA_TCIF_FLAG) != RESET)
{
DMA_ClearFlag(RS485_USART_DMA_STREAM, RS485_USART_DMA_TCIF_FLAG);
}
DMA_Enable(RS485_USART_DMA_STREAM, g_RS422TxBuf[FRAME_INDEX_FRAME_LENGTH]);
采用if去判断传输完成标志是否传输完成,然后再启动DMA传输。
这里有一个bug就是:
如果DMA在刚好启动传输的过程中,突然其他任务修改了DMA定义的传输缓存内容,那么传输的内容也会随之而修改。如下图所示:
这样也就能解释为什么上述传输的数据改变了的问题了。
解决方案
由于DMA采用的是单次模式(Normal),因此每次开启DMA传输时都需要清除传输完成标志。因此在清除传输完成标志时采用 while() 超时等待的方式去就解决该问题。
DMASendTimeout = 0xFFFF;
DMA_Enable(RS485_USART_DMA_STREAM, g_RS422TxBuf[FRAME_INDEX_FRAME_LENGTH]);
while(!DMA_GetFlagStatus(RS485_USART_DMA_STREAM, RS485_USART_DMA_TCIF_FLAG))
{
if((DMASendTimeout--) == 0) return;
}
DMA_ClearFlag(RS485_USART_DMA_STREAM, RS485_USART_DMA_TCIF_FLAG);
总结
之前采用 if 的语句判断传输完成标志时是为了避免任务存在while(1) 的死等方式导致卡死系统,就算要使用,必须添加超时溢出机制。
解决该问题的方案有多种,上述方案是最直接的,也是解决源头问题。也可以采用其他方案,例如,将所有设计到修改DMA内存值的代码全部放在一个任务中,由于单片机是单线程,顺序执行的,因此将其放到一个任务中就可以避免该问题的出现。
|