基于Linux的低延迟串行通信

lzfw57am  于 11个月前  发布在  Linux
关注(0)|答案(8)|浏览(158)

我正在Linux上通过串行端口实现一个协议。该协议基于请求应答方案,因此吞吐量受到将数据包发送到设备并获得应答所需时间的限制。这些设备大多基于arm,运行Linux >= 3.0。我在将往返时间减少到10 ms以下时遇到了麻烦(115200波特,8数据位,无奇偶校验,每条消息7字节)。
什么IO接口会给予我最低的延迟:select、poll、epoll还是使用ioctl手动轮询?阻塞或非阻塞IO会影响延迟吗?
我试着用setserial设置low_latency标志,但似乎没有效果。
还有什么其他的事情我可以尝试减少延迟吗?因为我控制所有的设备,它甚至可以修补内核,但它宁愿不。

  • 编辑-
    串行控制器使用的是16550 A。
wyyhbhjk

wyyhbhjk1#

请求/应答方案往往效率低下,而且在串行端口上显示得很快。如果你对吞吐量感兴趣,可以看看窗口协议,比如kermit文件发送协议。
现在,如果你想坚持你的协议并减少延迟,选择,轮询,读取都会给予你大致相同的延迟,因为正如Andy Ross所说,真实的的延迟是在硬件FIFO处理中。
如果你幸运的话,你可以调整驱动程序的行为而不打补丁,但你仍然需要看看驱动程序代码。然而,让ARM处理10 kHz的中断率肯定不会对整体系统性能有好处。
另一个选项是填充数据包,以便每次都达到FIFO阈值。它还将确认是否存在FIFO阈值问题。
10 msec@115200足以传输100字节(假设为8 N1),因此您看到的可能是因为low_latency标志未设置。尝试

setserial /dev/<tty_name> low_latency

字符串
它将设置low_latency标志,内核在tty层中向上移动数据时使用该标志:

void tty_flip_buffer_push(struct tty_struct *tty)
{
         unsigned long flags;
         spin_lock_irqsave(&tty->buf.lock, flags);
         if (tty->buf.tail != NULL)
                 tty->buf.tail->commit = tty->buf.tail->used;
         spin_unlock_irqrestore(&tty->buf.lock, flags);
 
         if (tty->low_latency)
                 flush_to_ldisc(&tty->buf.work);
         else
                 schedule_work(&tty->buf.work);
}


schedule_work调用可能是造成您观察到的10毫秒延迟的原因。

yquaqz18

yquaqz182#

在与更多的工程师讨论了这个主题后,我得出的结论是,这个问题在用户空间是无法解决的。由于我们需要跨越到内核的桥梁,我们计划实现一个内核模块,它可以谈论我们的协议,并为我们提供<1 ms的延迟。

  • 编辑-
    原来我完全错了。所有需要的是增加内核滴答速率。默认的100滴答增加了10毫秒的延迟。1000 Hz和串行进程的负值给了我想要达到的时间行为。
dfty9e19

dfty9e193#

Linux上的串行端口被“ Package ”到unix风格的终端结构中,这会给你带来1个tick lag,也就是10毫秒的延迟,如果stty -F /dev/ttySx raw low_latency有帮助的话试试,但是不能保证。
在PC上,你可以直接和标准串口对话,发出setserial /dev/ttySx uart none从串口hw解绑Linux驱动程序,并通过inb/outb控制端口到端口寄存器。我试过了,它工作得很好。
缺点是当数据到达时,你不会得到中断,你必须经常轮询寄存器。
你应该能够在arm设备端做同样的事情,在异国情调的串行端口硬件上可能要困难得多。

eyh26e7m

eyh26e7m4#

下面是setserial在端口的文件描述符上设置低延迟的方法:

ioctl(fd, TIOCGSERIAL, &serial);
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial);

字符串

34gzjxbg

34gzjxbg5#

简而言之:使用USB适配器和ASYNC_LOW_LATENCY。

我在Modbus上使用了基于FT232RL的USB适配器,速度为115.2 kbs。
使用ASYNC_LOW_LATENCY,我在总共大约20毫秒的时间内获得了大约5个事务(到4个设备)。这包括到慢戳设备的两个事务(4毫秒响应时间)。
如果没有ASYNC_LOW_LATENCY,总时间约为60 ms。
通过FTDI USB适配器,ASYNC_LOW_LATENCY将芯片本身的字符间计时器设置为1 mS(而不是默认的16 mS)。
我目前使用的是自制的USB适配器,我可以将适配器本身的延迟设置为我想要的任何值。将其设置为200 µS将使20 ms减少一个mS。

zwghvu4y

zwghvu4y6#

这些系统调用都不会对延迟产生影响。如果你想在用户空间中尽可能快地读写一个字节,你真的不会比一个简单的read()/write()对做得更好。尝试用另一个用户空间进程的套接字替换串行流,看看延迟是否有所改善。如果没有,那么你的问题就是CPU速度和硬件限制。
你确定你的硬件可以做到这一点吗?UART的缓冲区设计引入了许多字节的延迟并不罕见。

5tmbdcev

5tmbdcev7#

在这样的线速度下,不管你如何检查准备情况,你都不应该看到那么大的延迟。
您需要确保串行端口处于原始模式(因此您执行“非规范读取”),并且VMIN和VTIME设置正确。您希望确保VTIME为零,以便字符间计时器永远不会启动。我可能会将VMIN设置为1并从那里开始调优。
系统调用的开销与网络上的时间相比微不足道,所以select()与poll()等方法不太可能有什么不同。

6gpjuf90

6gpjuf908#

对于像我这样的人,在这条线上绊倒...
如果您没有FTDI USB设备,low_latency选项不会改变任何内容。处理它已在v3.15(2014年6月)中删除。请参阅提交:a9c3f68f3cd8 tty: Fix low_latency BUG
PS:Ruud Althuizen在这篇评论中已经注意到了删除。

相关问题