linux内核协议栈丢包统计,记一次linux下串口数据丢包解决过程

项目中两个芯片之间用串口进行通信,由于传输格式中有校验位,在数据量很大的时候总是校验失败。于是花了很长的时间最终解决了这个问题。

首先串口丢数据有两种情况(明显排除发送端发送的数据不对),第一种是信道也就是串口线或者连接口不行,无法承受很高的波特率(我使用的波特率是921600),第二种就是接收端由于某种原因丢数据。通过观察我排除了第一种情况,因为如果是信道承受不了太高的波特率的话那平时的小段小段的数据也可能会丢包,而明显我的情况是只有在一次传输大量数据(大概512字节以上)时才会丢数据。所以问题出在接收端的处理流程。

于是借此机会也了解了一般linux中串口接收数据的处理流程。我们都知道应用层通过select和read来及时的读取串口的数据,而读取的数据其实是内存里的FIFO缓冲区的数据。而串口模块的数据从RXD最终到内存里的FIFO会经过几个流程,大概如下图所示(这里我们仅分析串口接收端即RXD的处理流程):

c4e11ee806fd0767a37bd3dc91798e7b.png

图1 串口接收端处理流程

如图所示,串并转换模块将来自于RXD传输线上的数据转换成对应的一段数据,比如8位数据位一位停止位无校验位的情况下,就是转换成9bit的数据。然后将其中传输的数据部分写入到模块自带的硬件FIFO中(有了硬件FIFO,cpu就不用每接收到一个字节触发一次中断了)。当硬件FIFO达到设定的阈值时触发串口中断,串口中断处理程序通过配置DMA来搬运存在硬FIFO里的数据到内存里开辟的软FIFO里。应用层通过read()等接口读取内存中FIFO的数据。

接收端发生了丢失串口数据的情况,由上图可知有两种情况。第一可能是内存中的软FIFO由于是定长的,应用层读取频次太低导致该FIFO溢出从而导致数据丢失。第二种可能就是该串口模块自身的硬件FIFO(也是定长的)溢出导致数据丢失。

对于第一种情况我在应用层调用了ioctl接口设置软FIFO大小为1M,如下代码所示(虽然还是没解决,不过写在这里提供参考):

#include #include #include #include #include #include int UartBuffSizeSet(char *dev_path,int size) {

int ret;

int fd = open(dev_path, O_RDWR | O_NOCTTY | O_NONBLOCK);

if(fd < 0){

return -1;

}

struct serial_struct serial;

ret = ioctl(fd, TIOCGSERIAL, &serial);

if (ret != 0) {

close(fd);

return -2;

}+

serial.xmit_fifo_size = 1024*1024; //1M

ret = ioctl(fd, TIOCSSERIAL, &serial);

if(ret != 0) {

close(fd);

return -3;

}

close(fd);

return 0;

}

我设置完之后发现丢数据问题还是很明显,大概可以确认是硬件FIFO溢出,后来发现不丢数据总是在发送端一次发送64字节以下的时候,一旦一次传输超过太多比如我这里的512字节就丢数据,而我的设备串口硬FIFO的大小正好是64字节,所以可以确认是硬件FIFO溢出了。

由上面的图1可知串口硬FIFO达到阈值之后会触发中断,然后DMA会把硬FIFO里的数据搬运到内存中。如果FIFO溢出了那么就是DMA没有把数据搬运到内存中,也就是由于串口触发中断时CPU在处理其他中断暂时屏蔽了所有中断,导致没有响应串口的中断,这时串口硬FIFO依然持续增长直到溢出。

由于我的运行环境有摄像头、图像处理、图像输出等单元。CPU可能在这些的中断里停留时间较长。有一个解决的方法就是优化当前系统中的各个中断处理流程。显然这工作量不小而且可能提升不了多少,我的解决方法非常简单,但需要各位和我一样是多处理器的平台。我将串口的中断绑定到了CPU1上就ok了,CPU0默认处理了很多中断。

首先通过指令: cat /proc/interrupts 查看系统中各个设备的中断号:

#cat /proc/interrupts

CPU0 CPU1

29: 991442 4917 GIC 29 arch_timer

30: 0 0 GIC 30 arch_timer

36: 232252 0 GIC 36 uart-pl011

38: 0 453209 GIC 38

41: 0 0 GIC 41 pl022

42: 0 0 GIC 42 pl022

43: 0 0 GIC 43 pl022

44: 0 0 GIC 44 pl022

45: 0 0 GIC 45 himci

51: 0 0 GIC 51 ehci_hcd:usb3

52: 1 0 GIC 52 ohci_hcd:usb4

54: 0 0 GIC 54 xhci-hcd:usb1

55: 1689 0 GIC 55 himci

56: 52 0 GIC 56 himci

57: 253785 0 GIC 57 10050000.ethernet

59: 294156 0 GIC 59

60: 0 0 GIC 60 mipi0_int

61: 0 0 GIC 61 mipi1_int

62: 588957 0 GIC 62 ISP

63: 0 0 GIC 63 ISP

64: 294465 0 GIC 64

66: 19617 0 GIC 66 tde_osr_isr

67: 513076 0 GIC 67

68: 0 0 GIC 68 AIO Interrupt

69: 489697 0 GIC 69

70: 588293 0 GIC 70

71: 588277 0 GIC 71

96: 0 143945 GIC 96 timer

IPI0: 0 0 CPU wakeup interrupts

IPI1: 0 0 Timer broadcast interrupts

IPI2: 413363 612 Rescheduling interrupts

IPI3: 0 0 Function call interrupts

IPI4: 1 2 Single function call interrupts

IPI5: 0 0 CPU stop interrupts

IPI6: 0 0 IRQ work interrupts

IPI7: 0 0 completion interrupts

IPI8: 0 0 CPU backtrace

Err: 0

可以看到我的串口设备中断号是36。

使用如下指令将中断号36唯一绑定到CPU1上:

echo "2" >> /proc/irq/38/smp_affinity

echo 输入的数字的各个bit为1代表使用对应的CPU,比如bit0为1代表使用CPU0,可同时绑定多个CPU。

至此我遇到的串口数据丢失问题就得到了解决,虽然最后解决只是一句指令的事情,但是前面的分析查找原因花了不少功夫。而且如果我的运行平台是单核CPU那还得费脑筋解决,对于单核的CPU我大概想了一下在我这种原因(硬FIFO溢出)导致的串口丢数据的问题的解决方法:

1.尽量优化中断处理流程,拆分一次传输的size。

2.降低串口波特率。

3.找出执行时间较长的中断处理程序,视情况来决定在它屏蔽中断时不屏蔽串口的中断。

4.芯片外接带较大FIFO的串口模块,降低串口中断触发周期。(我自己猜想的,不知道有没有这样的模块)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值