串口是MCU最经常使用的外设之一,我遇到过多起串口在使用过程中出现死掉的实际案例。
这种问题在测试阶段如果发现了还好,一旦批量出去在现场发生就更加麻烦了。今天说的死掉,其实真实现象是MCU一直在不停的进串口中断,导致其他代码无法正常执行。
遇到该问题的罪魁祸首是串口Overrun,即溢出所致。Overrun其实很好理解,串口在接收数据的时候,每一字节数据经移位寄存器到数据寄存器,所谓溢出就是指,上一个字节的数据在数据寄存器还没有被读取走,新的1字节数据又已经被移入到移位寄存器的现象。以STM32F030为例,其他家的MCU也类似,Overrun错误标志位位于USART_ISR寄存器中。
Overrun现象我们可以这么产生:在串口中断服务函数刚开始处加一个断点,debug全速运行,通过串口调试助手一下发送2个字节的数据,这时就会出现Overrun,原因就是进入到断点处还没有去读数据,但是新的数据又来了。
手册中描述当此标志位置位时,RDR寄存器中的数据没有丢失,我们可以看到RDR内容就是第一个字节0x31。
默认情况下,Overrun检测功能时开启的,USART_CR3寄存器的OVRDIS位可以把该功能关掉。
当把溢出检测功能给关掉,要注意的是新来的数据会覆盖USART_RDR寄存器中的数据。
我们在代码串口初始化中把溢出功能关闭,同样做之前的实验,这时可以看到RDR的值就不是之前的0x31,而是0x32了,ISR里ORE标志位也不会置位了。
出现死掉的问题是因为来了ORE中断,但是中断服务函数没有去处理所致,所以就会一直进中断。
其实解决该问题的方法也非常简单,就是在中断服务函数里加入该标志位的清除操作即可。或者干脆就把溢出功能关闭不使用也行。
MCU里Overrun这个标志本来目的是为了提醒数据传输过程中出错了,接收端需要通知发送端重传。但是实际应用中大家一般也不这么用,为了保证串口数据通信的正确性,一般都会在应用层上加上数据帧的校验,有了校验其实用不用溢出功能也无所谓了。
最后强调一下:如果MCU开启了Overrun功能,一定要在中断服务函数里加入清溢出标志的操作。不加Overrun标志位处理大部分情况下也许不会出问题,但是一旦溢出可就麻烦大了。