长电缆串行通信超时问题

14

我有一个应用程序,通过RS232读取不同的硬件。它已经经过测试,并且工作得非常完美。对于最终的应用程序,我需要引入几百米长的电缆,这意味着我需要使用RS485转换器。

当我运行我的应用程序来读取硬件时,System.IO.Ports.SerialStream.Read会出现超时错误。我已将超时时间增加到20秒,但不幸的是它没有解决问题。

我尝试了不同的应用程序来读取硬件,它们甚至可以以1秒的读取频率工作。

通信使用的是Modbus协议,在当前阶段我认为这与问题无关,因为我都没有收到任何东西。

我的代码如下所示:

//get the right modbus data structure element
ModBus MB = (ModBus)s[0].sensorData;

//set up the serial port regarding the data structure's data
SerialPort sp = new SerialPort();
sp.PortName = MB.portName;
sp.BaudRate = Convert.ToInt32(MB.baudRate);
sp.DataBits = MB.dataBits;
sp.Parity = MB.parity;
sp.StopBits = MB.stopBits;
//Set time outs 20 sec for now
sp.ReadTimeout = 20000;
sp.WriteTimeout = 20000;

//将端口添加到列表中,以便读取器可以访问 portList.Add(sp); sp.Open();
读取硬件:
//get the right port for com
SerialPort sp = getRightPort();
ModBus MB = getRightModBusStructureelement();
try
   {
     //Clear in/out buffers:
     sp.DiscardOutBuffer();
     sp.DiscardInBuffer();

     //create modbus read message
     byte[] message = createReadModBusMessage();

     try
        {
         sp.Write(message, 0, message.Length);

         // FM.writeErrorLog output included for easier debug
         FM.writeErrorLog(DateTime.Now + ": ModBus Message Sent");
         FM.writeErrorLog(DateTime.Now + ": Read TimeOut = " + sp.ReadTimeout + " Write TimeOut = " + sp.WriteTimeout);

         int offset = 0, bytesRead;
         int bytesExpected = response.Length;

         FM.writeErrorLog(DateTime.Now + ": start read");

         while (bytesExpected > 0 && (bytesRead = sp.Read(response, offset, bytesExpected)) > 0)
            {
              FM.writeErrorLog(DateTime.Now + ": read - " + offset);
              offset += bytesRead;
              bytesExpected -= bytesRead;
            }
        }
        catch (Exception err)
        {
           Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
           FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
         }

  }

尝试应用后,我从ErrorLog.txt中得到了以下输出:

14/01/2016 17:18:17: ModBus Message Sent
14/01/2016 17:18:17: Read TimeOut = 20000 Write TimeOut = 20000
14/01/2016 17:18:18: start read
14/01/2016 17:18:38 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
   at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
   at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
14/01/2016 17:18:38: 0
14/01/2016 17:18:38: 0

为了防止出现相同的错误,我已将超时时间增加到60秒:

15/01/2016 11:11:51: ModBus Message Sent
15/01/2016 11:11:51: Read TimeOut = 60000 Write TimeOut = 60000
15/01/2016 11:11:51: start read
15/01/2016 11:12:51 - ERROR Modbus Message to serial port ModBus: System.TimeoutException: The operation has timed out.
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count, Int32 timeout)
   at System.IO.Ports.SerialStream.Read(Byte[] array, Int32 offset, Int32 count)
   at System.IO.Ports.SerialPort.Read(Byte[] buffer, Int32 offset, Int32 count)
   at ProbReader.SensorReader.modbusReading(List`1 mm, Int32 spCounter)
15/01/2016 11:12:51: 0
15/01/2016 11:12:51: 0

我已经尝试了几种不同的串口读取方法,我认为当前的方法看起来最好,就是在我的读取代码中使用while循环。

我没有包含其余的代码,因为它会超时,而且我认为这是无关紧要的。


我没有看到任何硬件流控的证据,例如 RTS/CTS。即使在软件流控可能不可靠的情况下,这仍然是我的首选。 - user585968
此外,您上面的代码实际上并没有显示您正在使用哪些值进行奇偶校验、数据位、波特率、停止位等设置。您能否在 MB 中转储这些值? - user585968
你有没有考虑使用COM转IP转换器?这样,你就可以通过TCP套接字接收数据,并且可以处理长距离传输。 - etalon11
5个回答

5
如果您正在使用数百米的串行电缆(这本身就是一个硬件工程问题),那么我强烈建议在电缆的两端都安装适当的收发器。 电缆本身应该是EMC屏蔽和高质量的。 长时间运行的未屏蔽电缆可能会受到感应电压波动的影响,这可能会损坏不适合长电缆运行的设备。
即使使用良好的电缆,仍然会有相当大的电压降和电感/电容效应,这可能会阻止更高波特率的通信。 尽量以最低的波特率运行。

数百米的电缆是不可避免的。这根电缆质量很好,电压下降不应该是问题,因为我已经提到我能够使用其他软件读取硬件。通过取消握手来解决了问题。所谓取消,是指我将其设置为true。 - Daniel

2
假设这不是硬件/长电缆问题,您可能能够在代码中处理该错误:
您需要创建一个“正确”的错误处理程序,就像您创建getRightPort函数一样:
SerialPort sp = getRightPort();

假设里面有一个包含串口项目的“集合”,并且您返回正确的项目,在这种情况下,如果此SerialPort存在错误,请确保使用相同的设置重新创建错误的SerialPort对象:
catch (Exception err)
{
   Console.WriteLine("ERROR Modbus Message to serial port ModBus: " + err);
   FM.writeErrorLog(DateTime.Now + " - " + "ERROR Modbus Message to serial port ModBus: " + err);
   reinitRightPort(sp); //add this
}

而方法reinitRightPort();看起来与您最初的初始化方式相似,但有以下小差异

  1. 您不再需要将其添加到List<SerialPort>
  2. 您不需要在方法中声明SerialPort,您可以从输入参数中获取它。
  3. 最好引入一些输入检查有效性以避免后来已知的错误
  4. 或许您可以关闭之前的连接,以确保该端口可以被新的SerialPortobject使用。

就像这样:

private void reinitRightPort(SerialPort sp){ //get SerialPort from outside of method
    //Add whatever necessary here, to close all the current connections
    //Plus all the error handlings
    if (sp == null) //Read 3.
        return;
    sp.Close(); //Read 4. close the current connections

    //get the right modbus data structure element
    ModBus MB = (ModBus)s[0].sensorData;

    //set up the serial port regarding the data structure's data
    sp = new SerialPort(); //Read 2. sp is from outside the method, but use new keyword still
    sp.PortName = MB.portName;
    sp.BaudRate = Convert.ToInt32(MB.baudRate);
    sp.DataBits = MB.dataBits;
    sp.Parity = MB.parity;
    sp.StopBits = MB.stopBits;
    //Set time outs 20 sec for now
    sp.ReadTimeout = 20000;
    sp.WriteTimeout = 20000;

    //portList.Add(sp); Read 1. no need to add this! It is already there!
    sp.Open();
}

注意:在您完成此操作并确保端口正常工作后,尽可能地,您还可以将上面的reinitRightPort方法与您的实际初始化结合使用,并进行一些小修改。但是,您可能想要做的第一件事情是让您的串行端口在出现错误时正常工作。

但是,如果错误源来自硬件/长电缆问题(例如放置在RS232或RS485中的电缆,或由于长电缆而导致的电压下降,或不兼容的硬件:简而言之,与编码无关),那么,不幸的是,解决方案也不能从代码中得到。您必须找到真正的硬件问题。


2
假设这是硬件问题(我猜是这样,因为我也曾经解决过类似的问题),我建议您将数百米的串行电缆更换为串口服务器(通过以太网连接到串行设备)。串口服务器可以在您的计算机上模拟COM端口,从而使串行电缆保持短距离。
不幸的是,这些服务器比几米长的电缆要贵一些...

1
@MickyD,以长电缆跑为设计目的的是以太网。串行电缆通常不是这样设计的。串口转以太网适配器是解决这个问题的有效方法。距离仍然很长,但由于其双绞线结构并可提供带屏蔽的形式,以太网处理得更好。 - user5069935
@Wossname 哦,我现在明白你们的意思了——串口转以太网转换器,使得设备不知道电缆已被以太网替换。我的错呵呵。这里+1 :) - user585968

2
请注意,RS232和RS485之间的区别在于RS232是全双工,而RS485仅为半双工。由于modbus是请求响应类型的协议,这不是您的问题,但了解这一点很重要。
因此,RS232<->RS485必须知道何时打开其RS485发射器。这可以通过不同的转换器以不同的方式完成,并且也可以进行配置。这可以使用RS232具有但RS485缺少的附加控制线来完成。RTS/CTS。 如果配置错误,则等待响应请求的响应方(即被动方)的发射器可能会打开,然后它将无法接收任何内容。
手册中的一个例子是http://ftc.beijer.se/files/C125728B003AF839/992C59EC02C66E00C12579C60051484E/westermo_ug_6617-2203_mdw-45.pdf 这是瑞典的一种流行型号,它有三种操作模式。它可以通过传入数据自动打开/关闭发射器,由DB9-RS232连接器上的RTS引脚控制,或者始终打开发射器(适用于具有每个方向上的单独电线的RS422) http://screencast.com/t/KrkEw13J8 这很难,因为一些制造商并没有打印出它实际上是如何工作的。而且,由于DCE和DTE不清楚,接线错误也非常容易发生。

1
如我在问题中提到的,我能够用其他软件读取硬件,所以必须是软件错误。经过调查串口设置中我可以操作的所有可能变量,我想到了关闭握手并让其始终被接受的想法。
稍微搜索了一下,我找到了以下代码,增加了写和读超时时间。这解决了我的问题:
                            sp.ReadTimeout = 60000;
                            sp.WriteTimeout = 60000;

                            sp.DtrEnable = true;
                            sp.RtsEnable = true;
                            sp.Handshake = Handshake.None;

我希望这对未来的他人有所帮助,感谢每个人的帮助和努力。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接