我使用 dotnet SerialPort 类通过 USB 到 RS485 转换器从 RS485 总线读取数据。 有时返回的数据与实际总线数据不符。
有时(平均每 2-5 分钟)SerialPort.Read() 报告的 byte[] 与总线上的实际数据不匹配。
我在另一个程序(HTerm)中使用第二个USB<->RS485转换器验证了实际总线数据,它确实报告了正确的数据。
数据比较(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);
}
我使用的“模式”(基于“超时间隔”;例如 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;
}
}