串口通信失败
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了串口通信失败相关的知识,希望对你有一定的参考价值。
1. DMA故障导致串口通信失败
1.1 背景
android应用层通过write方法写串口与MCU通信,如果未收到MCU的ack则重新将之前写入的信息重新写一遍,直至收到MCU的ack;
bug现象是:Android应用层一直未收到ack并不断重写,2分钟之后write方法卡住。
1.2 分析
经过源码分析,认为serial层的write函数有可能导致休眠,其中有一个缓冲区空间为4096字节,上层通过log将已发送的数据进行统计发现,当数据发送累计4102字节时会卡住。
加log进行复现和分析,结果如下:
根据log可知,在write方法卡住时最后一批log显示uart_write方法返回值为0,如果上层应用为阻塞式访问,则会调用schedule方法将当前进程休眠即上层直观感受上的卡死。
参考log分析源码可知,serial层的环形缓冲区空间已满导致bug的出现,serial层数据到硬件TX_FIFO是由DMA来搬运的,所以初步估计DMA有问题,向SoC厂商咨询后对方表明,该SoC的DMA确实有一些缺陷。
继续添加log发现,确实DMA出现了不搬运数据的故障,结果如下:
2. 关闭DMA后导致串口通信失败
2.1 背景
继上次关闭DMA以后,在某特定场景下回出现频率较高的串口通信失败,基本过程就是MCU给SoC发消息,然后有一帧消息会丢失,从而导致Android应用上层校验失败,判断通信失败。
由于各种原因MCU端屏蔽了针对这种情况的重发机制,所以需要SoC端寻找原因和解决方案。
有一个值得关注的现象是:应用层通过不停的读缓冲区发现,当一次性读取到的数据为32字节时,下一帧数据就会出现丢失情况。
2.2 分析
SoC串口的硬件RX_FIFO空间也是32字节,驱动代码中设置的阈值为16字节,即RX_FIFO中超过16字节就会触发中断,提醒CPU搬运数据至tty层,结合应用层发现的现象,初步结论为RX_FIFO溢出。
SoC的串口控制器是有溢出标志位的,在驱动代码中添加log打印,最终确认出现通信失败时RX_FIFO确实溢出了。
基本判断是UART硬件中断优先级太低导致出现中断响应不及时,从而导致RX_FIFO溢出。
2.3 解决办法
2.3.1 调整阈值
调整触发串口中断的阈值,调整之后有些许改善,但是溢出情况的频率依旧较高,故此方法pass掉。
2.3.2 修改中断优先级
修改中断优先级的影响范围太广,且SoC厂商不建议修改中断优先级,建议修改中断亲缘性,故此方法pass掉。
2.3.3 修改中断亲缘性。
方式一:
//1. 查看中断对应的编号
cat /proc/interrupts
······
59: 205 0 GIC 21e8000.serial
//2. 将串口的中断亲缘性从CPU0改到CPU1
echo 2 > /proc/irq/59/smp_affi nity
//十进制2对应二进制0010,如果要改到CPU2,则需要写入0100即十进制4,但是IMX6D只有2个CPU核心,所以设置4是无效的(不生效)
方式二:
在驱动代码中调用irq_set_affinity()将串口中断绑定在某个CPU核心上执行。
retval = request_irq(sport->port.irq, imx_int, 0,
dev_name(port->dev), sport);
irq_set_affinity(sport->port.irq, cpumask_of(2));
通过调整中断亲缘性后也只能是有一定的改善作用,不能100%解决此问题。
2.3.4 添加流控
流控分为软件流控和硬件流控,我个人更加倾向于硬件流控。
以上是关于串口通信失败的主要内容,如果未能解决你的问题,请参考以下文章