dotnet SerialPort 从 USB <->RS485 转换器读取错误数据

问题描述 投票:0回答:1

从 dotnet SerialPort 读取错误数据

我使用 dotnet SerialPort 类通过 USB 到 RS485 转换器从 RS485 总线读取数据。 有时返回的数据与实际总线数据不符。

问题

有时(平均每 2-5 分钟)SerialPort.Read() 报告的 byte[] 与总线上的实际数据不匹配。

我在另一个程序(HTerm)中使用第二个USB<->RS485转换器验证了实际总线数据,它确实报告了正确的数据。

数据比较(Hterm 与 C# 输出):

  • ... 35 7B 3F 44 40 98 98(Htermn - 好)
  • ... 35 7B FF 7F 60 26 26(C# - 失败)

目前已测试

  • 当Read()报告的byte[]中的一个字节错误时,其余字节也是错误的。
  • 我不认为这是硬件故障
    • 在 2 台具有不同 RS485 转换器(相同型号)的不同 PC 上进行测试
    • 我还验证了来自 Hterm 的巨大总线转储,以检查转换器有时是否报告不正确的数据,但整个转储是有效的。

C# 实现

周期性(2ms)调用以下函数:

private void readData()
{
  int bytesToRead = port.BytesToRead;
  if (bytesToRead == 0)
    return;

  byte[] data = new byte[bytesToRead];

  port.Read(data, 0, bytesToRead);
  receivedDataEvent.Invoke(data);
}

一般信息

  • 串口版本8.0.0
c# .net serial-port
1个回答
0
投票

我使用的“模式”(基于“超时间隔”;例如 250 毫秒);就是获取该间隔的切片,检查要读取的字节,如果没有任何内容可读取,则休眠一个切片。如果“时间”到了,我自己处理;并避免超时异常。 (这也是通过 USB 的 RS485)

  /// <summary>
  /// Read a server response from the serial port input buffer
  /// and update the bytesRead parameter.
  /// </summary>
  public static bool Read( byte[] buffer, ref int totalBytesRead ) {

     //==============================================================
     // NOTE: To get a complete response will often require
     // multiple reads unless we first "wait".
     //
     // It appears that data can arrive at the serial port input buffer without
     // actually doing a "read"; the read just "empties" the port's input buffer
     // if there is any data within the timeout value.
     //
     // The first read always seems to return a single byte: the slave id;
     // unless we "wait", in which case we get a longer response.
     //
     // Select a proper read timeout value which balances
     // the response for the initial port read against that of subsequent
     // port reads.
     //==============================================================

     // Initialize. Also used as offset into buffer !!!
     totalBytesRead = 0;

     // Update stats.
     _stats.ReadRequests++;

     // Locals.
     int bytesToRead = 0;
     int bytesRead = 0;

     // Used in wait calculations.
     int retryTime = _serialPort.ReadTimeout;
     int timeSlice = Math.Max( retryTime / 10, 1 );

     try {

        // Wait for a response and read until we get a good CRC
        // or time out.
        while ( true ) {

           //-------------------------------------------------
           // Wait for a response.
           // Using a "software" timeout to avoid the overhead
           // of timeout "exceptions".
           //-------------------------------------------------
           for ( int i = 0; i < retryTime; i += timeSlice ) {

              bytesToRead = _serialPort.BytesToRead;

              if ( bytesToRead > 0 ) {
                 // Data in port input buffer.
                 break;
              }

              Thread.Sleep( timeSlice );
           }  // end for.

           if ( bytesToRead == 0 ) {
              // Timeout; no response or incomplete response.

              if ( totalBytesRead == 0 ) {
                 // No response; typically during "discovery".

                 _stats.ReadNoReply++;

                 EventText = "No read response (timed out).";
                 return false;
              }
              else {
                 // Incomplete; let caller generate CRC error.

                 EventText = "Incomplete read response.";
                 return true;
              }
           }

           //------------------------------------------
           // Test for potential buffer overflow.
           //
           // Should never be trying to
           // read more than 256 (buffer.Length).
           //
           // (Has happened with a faulty board / memory chips
           // where temperature recording was not happening).
           //------------------------------------------
           if ( ( totalBytesRead + bytesToRead ) > buffer.Length ) {

              string errorMsg = string.Format(
                 "Read error (256 byte limit exceeded): Read so far {0}; Still to be read {1}+.",
                 totalBytesRead,
                 bytesToRead );

              //throw new Exception( errorMsg );

              EventText = errorMsg;
              return false;
           }

           //------------------------------------
           // Read response.
           //------------------------------------
           _stats.Reads++;

           bytesRead = _serialPort.Read(
              buffer,
              totalBytesRead,
              bytesToRead );

           totalBytesRead += bytesRead;
           _stats.BytesRead += bytesRead;

           //------------------------------------------
           // Checks read response for a valid CRC. If a valid CRC
           // was calculated, assumes this is the end of the transmission.
           // Should speed things up versus waiting for a timeout.
           //------------------------------------------
           if ( ( totalBytesRead >= 3 ) &&
                 FunctionBase.HasValidCRC( buffer, totalBytesRead ) ) {

              // Done.
              return true;
           }

        }  // end while.
     }
     catch ( TimeoutException ex ) {

        // Should not happen; handling timeouts
        // in software above based on BytesToRead.
        // If we got this far, there was a read problem
        // even though BytesToRead > 0.

        _stats.ReadTimeouts++;
        EventText = "Read TimeoutException: " + ex.Message;

        return false;
     }
     catch ( Exception ex ) {

        _stats.ReadErrors++;
        EventText = "Read Exception: " + ex.Message;

        return false;
     }
  }
© www.soinside.com 2019 - 2024. All rights reserved.