有人使用STM32G4系列的USART,并开启DMA方式的定长接收,DMA工作在Normal模式,但是偶尔会出现接收出错问题。令人头疼的是一旦出错了后面的接收总是错的,出错的位置往往体现在一帧的第一个数据。除非重启系统才能消除接收连续出错问题。下面两幅图是UART接收出错的情形。【为了便于交流,后面的定长接收都是指8个字符的接收。】
4ngw1v5tiji64011005049.png
sgkxdzr12rx64011005149.png
使用场景大致是这样的。某工程师使用STM32G474开发产品,用到某USART的DMA收发。MCU通过USART跟外部传感器进行通信,USART的收发都使用DMA方式,Normal模式。两端的收发都是定长方式,即收发都是基于确定长度的传输。
实际调试过程中,他发现STM32G4芯片的USART的DMA发送倒没啥问题,就是接收偶尔会出错。一旦出错,后面就一直错。通信两端明明使用的定长数据的收发,数据怎么会错乱?即使偶尔出差了要怎样才能及时恢复正常呢?
后来经反复检查确认,他说的定长收发只是理论上的或者说预期的。实际上,在通信过程中偶尔会有额外的噪声窜出来,即传感器端实际发送过来的数据可能多于指定长度。多发的数据虽然没有被当次接收,但留在USART接收FIFO了。下次基于DMA接收时,FIFO数据若没被清掉就会当作下一次的接收数据了。
因为接收这边是DMA定长接收,发送端也是定长发送,但由于FIFO里噪声数据的存在,接收端没法全部接收新的数据,使得FIFO里始终有残留数据,进而导致后续的接收发生错位。
下面截图是STMG4的USART的功能框图,看看有助于理解。
o01mqkalqlm64011005249.png
既然这样,我们可以在每次的USART的定长接收完成之后、开启下一次DMA接收前,将USART的FIFO清理干净。当然这个过程中,要注意溢出问题,发生溢出后会妨碍DMA请求的产生,我们需及时处理溢出标志。
下面简单演示解决上面问题的做法。使用STM32G474的USART2的DMA收发功能,STM32G474的USART每次从PC的串口终端做8个字符的定长接收,多余的将被丢弃并不可以影响下次接收,同时,STM32G474将每次收到的8个字符回显到PC串口终端。开启USART DMA接收的完成中断,每次收到8字符时设置相应的标志【Flag_Rxcpt】,并稍作延时,保证一次性发送过来的字符发送完毕。注意,是保障对方的发送完毕,接收的话每次只接收8个字符。
下面的测试使用STM32G474的USART2的配置如下:
nucuiznvpcq64011005349.png
bt2trohmcla64011005449.png
USART收发的FIFO可开可关,下面做法都适用。另外,这里没有使能USART2的中断响应,使用库函数组织代码的话最好使能它,可以省些事。
解决上面问题的第一种做法就是每次在开启下次DMA接收前将USART重新初始化一下,这个肯定是有效的。没理由重新初始化USART了,其FIFO数据还不失效吧。见下图红框中的代码。
p1thayc4mig64011005549.png
下面是USART DMA接收完成中断的回调处理函数。接收到8个字符后设置接收标志,并设置延时参数,让对方或线路上可能多发或多产生的数据发送完毕。
3traopxucwu64011005649.png
不过这个操作动作有点大,有时可能不太合适。我们可以换个做法,像下面这样,通过查询RXNE标志来读取接收数据寄存器。其实就是将FIFO里的残留清掉,同时预防性地对溢出标志清零。
lmagjsvhc3564011005749.png
如果前面使能了USART的中断响应,基于库函数组织代码时,此处对溢出标志清零就不必要了,可以到中断服务程序里处理。显然第二种做法更有针对性,动作不波及其它。
当然,还有另外一种做法,通过操作特定寄存器清空USART接收FIFO。具体就是对USART_RQR寄存器的RXFRQ位写1,发起对接收FIFO清空的请求。操作代码如下:
s1ekygzfwmh64011005849.png
下面是基于上面几种做法的验证结果。USART接收时DMA每次只搬8个数据,多发过来的数据不予理睬,也不影响后续的再次接收。
5j0dd5hr24z64011005949.png
不管哪种做法,最终目的都一样,即在开启下次的USART接收前先清空接收FIFO。OK,今天的分享就到这里,下次再聊~!
猜你喜欢:
WiFi6+蓝牙+星闪,三合一开发板,真香!
Github上热门 C 语言项目汇总!
嵌入式,可测试性软件设计!
一些低功耗软件设计的要点!
嵌入式 C 保护结构体的方式
实用 | 10分钟教你通过网页点灯
谈谈嵌入式软件的兼容性! |