我在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库,但我想在其他应用程序中使用它)
答案 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()
并适当设置VMIN
和VTIME
参数。在这种情况下,您似乎希望VMIN
(数据包中的最小字符数)为0
和VTIME
(字符之间允许的最短时间为{{} 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()
之前执行此操作。这里有几个我发现非常有帮助的链接:
我希望至少可以帮助/指出你正确的方向,即使它不是你问题的完美答案。
答案 3 :(得分:1)
您无法使用超时。在较高的波特率上,3.5字符超时意味着几毫秒,甚至几百微秒。这些超时无法在Linux用户空间中处理。
在客户端,由于Modbus不发送异步消息,因此不是什么大问题。因此,您不能在3.5个字符超时内发送2个连续的消息。
在服务器端,问题是如果您的客户响应时间极短且Linux太忙,您就无法编写防弹框架解决方案。 read()函数有可能返回多个数据包。这是(一个有点人为的)例子。
客户端将数据包写入服务器。超时让我们说20毫秒。
让我们说Linux目前非常繁忙,因此内核不会在接下来的50毫秒内唤醒你的线程。
20毫秒后,客户端检测到它没有收到任何响应,因此它会向服务器发送另一个数据包(可能会重新发送前一个数据包)。
如果Linux在50 ms后唤醒您的读取线程,则read()函数可以获得2个数据包甚至1个半数,具体取决于串行端口驱动程序接收的字节数。
在我的实现中,我使用一种尝试即时解析字节的简单方法 - 首先检测功能代码,然后尝试读取特定函数的所有剩余字节。如果我得到一个半包,我只解析第一个包,剩下的字节留在缓冲区中。如果在短暂超时内有更多字节,我会添加它们并尝试解析,否则我会丢弃它们。它不是一个完美的解决方案(例如功能8的某些子代码没有固定的大小),但由于MODBUS RTU没有任何STX ETX字符,所以它是我能够弄清楚的最好的一个。