串行编程:测量字符之间的时间

时间:2009-11-16 02:57:15

标签: c serial-port timing modbus

我在Linux中通过串行线发送/接收数据,我想找到字符之间的延迟。

Modbus使用3.5个字符的延迟来检测消息帧边界。如果延迟超过1.5个字符,则声明消息帧不完整。

我正在用C编写一个快速程序,基本上是

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
// setup newtio
....
tcsetattr(fd, TCSANOW, &newtio);
for(;;) {
    res = read(fs, buf, 1);
    if (res > 0) {
        // store time in milliseconds?
        //do stuff
    }
}

有没有办法在这里测量时间?或者我是否需要以不同的方式查看从串行线检索数据?

我还尝试连接到SIGIO,以便在有数据的时候获取信号,但我似乎一次只获得8个字节的数据。

(是的,我知道有一些modbus库,但我想在其他应用程序中使用它)

4 个答案:

答案 0 :(得分:4)

简单的答案是...... 你不能(不是没有编写自己的串行驱动程序)!

如果你正在编写一个MODBUS master ,那就有一些希望:你可以通过等待任何的时间来检测从属响应的结束(假设它超过3.5 chars)没有收到任何东西(select(2)可以在这里帮助你),或者通过在阅读时动态解析响应(第二种方法浪费的时间少得多)。在收到对先前请求的响应之后,您还必须小心等待至少 3.5个字符时间,然后才开始发送新请求。 “至少”在这里有效!等待更多并不重要。等待少了。

如果您正在编写MODBUS 奴隶,那么您运气不好。您只是无法从用户空间Linux 可靠地执行此操作。你必须自己编写串口驱动程序。

顺便说一下,这不是Linux的错。这是因为MODBUS的框架方法令人难以置信的愚蠢。

答案 1 :(得分:3)

MODbus就像很多旧协议,真的很讨厌现代硬件。

您一次获得8个字节的原因是: 您的PC在硬件中具有(至少)16字节串行FIFO接收和发送。大多数是64字节或更大。

可以告诉uart设备超时并在多次char时间后发出接收中断。

触发电平可调,但低级驱动程序“智能”设置。尝试使用setserial的低延迟模式) 如果必须,您可以使用串行驱动程序中的代码。谷歌它(成熟内容警告)它不漂亮。

所以例程就像伪代码一样

int actual=read (packet, timeout of 1.5 chars)

look at actual # of received bytes

if less than a packet, has issues, discard.

不太好。

答案 2 :(得分:1)

我认为你这是错误的做法。有一种内置机制可以确保角色齐聚一堂。

基本上,您要使用ioctl()并适当设置VMINVTIME参数。在这种情况下,您似乎希望VMIN(数据包中的最小字符数)为0VTIME(字符之间允许的最短时间为{{} 1}}(它们是十分之几秒)。

一些真的基本示例代码:

15

struct termio t; t.c_cc[ VMIN ] = 0; t.c_cc[ VTIME ] = 15; if (ioctl( fd, TCSETAW, &t ) == -1) { printf( msg, "ioctl(set) failed on port %s. Exiting...", yourPort); exit( 1 ); } 之前但open()之前执行此操作。这里有几个我发现非常有帮助的链接:

Serial Programming Guide

Understanding VMIN and VMAX

我希望至少可以帮助/指出你正确的方向,即使它不是你问题的完美答案。

答案 3 :(得分:1)

您无法使用超时。在较高的波特率上,3.5字符超时意味着几毫秒,甚至几百微秒。这些超时无法在Linux用户空间中处理。

在客户端,由于Modbus不发送异步消息,因此不是什么大问题。因此,您不能在3.5个字符超时内发送2个连续的消息。

在服务器端,问题是如果您的客户响应时间极短且Linux太忙,您就无法编写防弹框架解决方案。 read()函数有可能返回多个数据包。这是(一个有点人为的)例子。

  1. 客户端将数据包写入服务器。超时让我们说20毫秒。

  2. 让我们说Linux目前非常繁忙,因此内核不会在接下来的50毫秒内唤醒你的线程。

  3. 20毫秒后,客户端检测到它没有收到任何响应,因此它会向服务器发送另一个数据包(可能会重新发送前一个数据包)。

  4. 如果Linux在50 ms后唤醒您的读取线程,则read()函数可以获得2个数据包甚至1个半数,具体取决于串行端口驱动程序接收的字节数。

  5. 在我的实现中,我使用一种尝试即时解析字节的简单方法 - 首先检测功能代码,然后尝试读取特定函数的所有剩余字节。如果我得到一个半包,我只解析第一个包,剩下的字节留在缓冲区中。如果在短暂超时内有更多字节,我会添加它们并尝试解析,否则我会丢弃它们。它不是一个完美的解决方案(例如功能8的某些子代码没有固定的大小),但由于MODBUS RTU没有任何STX ETX字符,所以它是我能够弄清楚的最好的一个。