在使用STM32F207这一款单片机调试串口的时候,使用两种不同的中断接收方式:帧中断IDLE和字节中断RXNE,会出现一些神奇的事情,只要我们认真地分析是可以揭开BUG后面隐藏的真相。其中IDLE中断也叫作串口空闲中断。
一、问题背景
1.1 硬件连接框图
如图1所示是简化的硬件连接框图,本来有18个SLAVE设备,问题的复现只需要两个所以只画了两个SLAVE设备。MASTER和SLAVE1、SLAVE2使用485总线进行通信,其中MONITOR是一个监听设备,用于监听所有在总线上的数据,监听数据并分析是在调试各类总线型通信时常用的手段,两个120欧电阻是首尾匹配电阻所以485总线的阻抗是60欧。
其中SLAVE1、SLAVE2、MONITOR都是使用STM32F207+ADM2682扩展出来485,接收方式是使用帧中断;MASTER是对方设计的所以不清楚使用的主控芯片、中断方式是帧中断接收还是字节中断接收,通过图6可以分析出来应该是字节中断,因为MASTER反应时间t2是远小于SLAVE的反应时间t1。
双方约定命令主要有两种:①、MASTER会定时20ms向SLAVE发送取数指令,SLAVE向MASTER返回传感器测试值Y;②、如果MASTER解析出来传感器测试值Y在[-X,X]范围内,MASTER会向SLAVE发送清零指令,SLAVE需要将Y值清零,SLAVE不需要返回任何数据。
图1 硬件连接框图
1.2 玄学的BUG
前期自测:因为没有MASTER设备,所以使用USB-485模块模拟MASTER设备,使用XCOM串口软件作为上位机发送取数据指令以及清零指令。单独测试读数据命令时,定时20ms,SLAVE每次都回返回数据无丢帧。
因为MSATER和SLAVE之间的数据交互都是一包数据10个字节的,所以使用了串口的USART_IT_IDLE中断就是帧中断,它是当串口收到一帧数据或者说一包数据后产生的中断。以MASTER发送给SLAVE2的取数据指令包含10个字节:AA 01 52 00 00 00 00 00 52 85,当接收完0xAA时,会触发一次USART_IT_RXNE字节中断,而不会触发USART_IT_IDLE帧中断,当接收完0x85后,会触发USART_IT_RXNE字节中断和USART_IT_IDLE帧中断,所以这个命令会触发10次USART_IT_RXNE字节中断和1次USART_IT_IDLE帧中断
。
中期联调:当SLAVE传感器值Y不在[-X,X]范围时(蓝色部分),无异常现象;当SLAVE传感器值Y在[-X,X]范围时(蓝色部分),预期现象时会出现清零,即Y值会显示为0。但是实测出现了如图3所示的不清零情况,-0.7因为在[-X,X]范围内,所以-0.7会瞬间变成0,但是一直在0.7停留,并且状态灯一直是绿色的,所以通信是没有断掉的【有一次没有返回数据就会绿灯变成红色,并且通过MONITOR记录下的数据发现是每次都会返回数据的,所以MONITOR是很有必要的,用以BUG调试时的数据记录回看】,即MASTER的取数据命令都得到了SLAVE的反应。
图2 传感器值与阈值范围
图3 错误现象
因为出现了玄学现象,为了复现BUG所以通过改变传感器的状态使传感器值反复经过[-X,X]红色线部分看看能不能复现故障。复现的一次情况是SLAVE1设备的角度在1.3度停止不动,并且不会清零。通过在线Debug查看MONITOR接收到的数据,发现一个帧中断接受到了30个数据,前十个字节数据是SLAVE1回的数据(其中解析出来的数据是1.3,属于红色线范围,会被MASTER清零),中间十个字节是SLAVE向SLAVE1发出的清零指令,后面十个字节是MASTER向SLAVE2发出的读角度指令。
这里有个问题为什么不Debug SLAVE1设备呢?其实是Debug了的,只不过没有有效的收获,因为导致这个BUG的原因是SLAVE1+MASTER+SLAVE2一起导致的,如果Debug这三个中的任何一个单个设备,均不能复现这个BUG。
-
BB 00 52 01 66 66 A6 3F 68 EF 是SLAVE1回MASTER设备的数据指令,10个字节
-
AA 09 53 00 00 00 00 00 22 05 是MASTER发送给SLAVE1的清零指令,10个字节
-
AA 01 52 00 00 00 00 00 52 85 是MASTER发送给SLAVE2的取数据指令,10个字节
使用Debug发现了一次帧中断出现了“三包数据”(这里的“三包数据”指的是本应该是三包数据的,但是却被识别成了一包数据,所以这里带引号的三包数据实际上按照一包数据接收的,同理“两包数据”),本来是一包数据就会触发一次帧中断的,也就是说本应该触发三次帧中断的只触发了一次帧中断如图4所示。也有出现一次帧中断接收“两包数据”的,当时只保存了示波器抓到一次帧中断接收“两包数据”的波形,没有Debug时的RS485_RX_BUF截图。
图4 一个帧中断接收“三帧数据”
1.3 帧中断触发条件
中断产生条件:STM32的帧中断是IDLE中断,当接收到一帧数据,就会产生IDLE中断。帧中断常被用于接收不定长度字节数据。
优点:因为IDLE中断是一帧数据/一包数据产生一次中断,然后读取寄存器里面存取的数据可以获得本次帧中断所接收的数据长度与数据内容,即使某一帧由于未知原因增加一个字节或者减小一个字节,可以根据接收到的数据长度和约定的不一致所以不采用这一帧数据,但是下一帧数据是不受前面错误帧数据的影响,这个是下面使用字节中断可能会出现的问题。
缺点:当使用帧中断时,因为判断帧结束的标志需要默认电平持续的时间>发送一个字节所需要的时间,所以对于一帧数据的响应时间一定是大于一个字节数据信号的时间,即下文的 t Byre t_{\text {Byre}} t