3

我在 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

4 回答 4

6

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

如果您正在编写 MODBUS主站,则有一些希望:您可以通过等待任意时间(假设其长于 3.5 个字符)而不接收任何内容来检测从站响应的结束(select(2) 可以在这里为您提供帮助),或者通过在阅读时动态解析响应(第二种方法浪费的时间要少得多)。在收到对前一个请求的响应之后,您还必须小心等待至少3.5 个字符时间,然后再开始传输新请求。“至少”在这里有效!多等也没关系。等待更少。

如果您正在编写一个 MODBUS从站,那么您就不走运了。您根本无法从用户空间 Linux可靠地做到这一点。您必须编写自己的串行驱动程序。

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

于 2010-05-12T17:33:42.270 回答
4

MODbus 就像许多旧协议一样,非常讨厌现代硬件。

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

可以告诉 uart 设备超时在多个字符时间后发出接收到的中断。

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

所以例程是伪代码

int actual=read(数据包,1.5 个字符的超时)

查看实际收到的字节数

如果小于一个数据包,有问题,丢弃。

不是很好。

于 2009-11-16T03:51:27.587 回答
2

您不能使用超时。在更高的波特率下,3.5 个字符超时意味着几毫秒,甚至几百微秒。Linux 用户空间无法处理此类超时。

在客户端,这没什么大不了的,因为 Modbus 不发送异步消息。因此,您可以在 3.5 个字符超时内不要发送 2 条连续消息。

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

  1. 客户端将数据包写入服务器。超时假设为 20 毫秒。

  2. 假设 Linux 目前非常繁忙,因此内核不会在接下来的 50 毫秒内唤醒您的线程。

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

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

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

于 2016-09-05T17:50:18.397 回答
0

我认为你正在以错误的方式解决这个问题。有一个内置的机制可以确保角色聚集在一起。

基本上,您将需要适当地使用ioctl()和设置VMINVTIME参数。在这种情况下,您似乎希望VMIN(数据包中的最小字符数)0VTIME(字符之间允许的最小时间量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()但在您的read(). 以下是我发现非常有用的几个链接:

串行编程指南

了解 VMIN 和 VMAX

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

于 2009-11-17T23:44:42.660 回答