Lazy loaded image
五、常见异常和处理方法
Words 1798Read Time 5 min
2025-5-6
 
本节介绍串口通讯中常见异常以及解决方法。本节通讯机制基于RXNE+IDEL中断+线程处理+线性缓存,总体来说,通过参考前两节,优化通讯机制来解决这些问题。
⚠️
基于 RXNE+IDEL 中断+线程处理+线性缓存,指 RXNE 接收字节放入线性缓存区中,IDEL 中断认为接收了完整一帧,并通知线程进行数据处理。
数据帧长度字段异常 → 使用线性缓冲区导致
空闲中断异常 → 使用 IDEL 中断当做完整一帧标志导致
缓存资源互斥锁问题 → IDEL 中断+线性缓冲区导致

参考资料

  1. 如何写一个健壮且高效的串口接收程序? (qq.com)

ORE 上溢错误

如果在 RXNE 未复位时(接收寄存器还存在数据)接收到字符,则会发生上溢错误。RXNE 位清零前,数据无法从移位寄存器传送到 RDR 寄存器。ORE 错误出现在如下情况:RXNE 中断发生,有更高优先级中断需要处理,而且处理时间较长,导致当前接收字节没有接收完成就被后续的数据冲掉,即出现ORE(溢出错误)。
出现该错误,需要通过一个软件序列进行清除:首先通过USART_GetFlagStatus读取USART_SR寄存器的值,然后通过USART_ReceiveData函数读取USART_DR的值即可。需要说明,软件序列操作会让串口PE、FE、NE、ORE、IDLE等标志位全部清除。因此若需要获取标志位,通过 USART_GetFlagStatus 读取备份保存。
使用Keil仿真器调试串口寄存器组时(peripheral窗口还是Watch窗口)需要注意,通过MDK查看SR或者DR寄存器数据,可能会影响MCU读取数值。第二个需要注意的是KEIL按序列读取这两个寄存器后会导致读取序列清除。非必要时不要打开该窗口。

数据帧长度字段异常

对于主机发送数据本身异常,可以通过校验帧进行错误检测,并做好错误处理,比如丢失异常数据包,请求重传(协议需制定相应机制)。但需要特别留意数据长度异常。
一般嵌入式设备为了节省内存资源,通讯接收分配的内存小于接收到的数据长度,比如定义数组 buffer[32] 作为接收缓存,由于长度字段错误(比如0x30),或者变长数据超出分配的内存,则会导致数据溢出。由于栈空间向下生长,严重的修改了栈帧中 LR 寄存器值,则会造成 hardfault 错误。
该问题可以使用环形缓冲区作为接收缓存,避免内存溢出异常,且方便监测内存状态。另外在协议解析时,对长度做检查,丢弃长度字段异常帧。

空闲中断异常

RXNE 中断一个字节一个字节存入缓存,IDEL 中断帧结束标志,进行数据处理。但由于 Linux、windows 系统并不是实时系统,当应用程序需要发送一帧数据时,可能造成没有连续发送,因此此种情况下,无法使用IDEL 中断当做一帧数据结束的标志。
该问题可以通过 IDEL中断将数据保存到缓存,缓存通过协议格式使用状态机判断。

数据域指针修改

我们通过指针将数据域内存地址传递到数据域,这样不需要拷贝内存副本,节省了部分RAM空间。这里报道了一个问题:数据发送组包过程,使用数据指针传递数据,计算校验时,数据源发生变化,导致校验失败。
该问题可以使用memcpy() 拷贝一份,保证计算时数据不会变化。也可以使用线程间同步机制,保证数据本地修改和打包发送两个过程不会对数据内存形成抢占访问。

缓存资源互斥锁问题

对于串口通讯机制,接收缓存共享资源会在用户线程和中断中使用。为了保证多线程访问安全问题,可能会通过互斥锁管理接收缓存。
notion image
如上图,ISR和数据处理线程在访问接收缓冲区都会先获得互斥锁,用后释放,实现互斥访问。缓存资源应该在数据处理完成后立即释放,过早和过迟是释放都会带来问题。
  1. 释放过早(早于数据处理,即数据处理尚在使用接收缓存,就释放了互斥锁):这种场景下,若通讯具备重发机制,并且从机数据处理架构设计不合理,可能会造成主机重发,修改了从机接收缓存,从机正在的处理的缓存被修改了。参考下面示意图:
notion image
  1. 释放过迟(使用完缓存后,延迟释放):这种场景下,由于中断服务例程 ISR 获取互斥锁失败,导致主机发过来的数据无法写入缓存,造成丢失。参考下面示意图:
notion image
 
⚠️
采用锁的方式,会影响程序处理效率,线程都在等待锁的释放。如何解决该问题?
改进架构设计,不适用轮询阻塞式机制,数据耗时处理避免放进中断服务例程中处理,加快数据处理速率。不使用互斥锁,引入环形缓冲区,根据应用情况,选择合适的缓冲区大小。通过环形缓冲区进行流控,平衡负载。
当然使用对于突发传输数据量过大,环形缓冲区也会被覆盖。上一节我们通过开启DMA HT/TC中断和IDEL中断,中断处理线程化方式解决该问题。同时环形缓冲区增加缓存状态检测,采取必要的纠正措施,并记录日志。
 

DMA FIFO和突发设置

 
 
上一篇
模板设计模式:让你的代码结构更清晰
下一篇
Guide to Linux System

Comments
Loading...