【发布时间】:2017-03-05 18:45:05
【问题描述】:
我花了很多团队研究在 C# 中使用串行端口的正确方法,这样您就不会在读取数据时遇到问题。我认为我有一个非常接近功能性的解决方案,但我偶尔会遇到一些我似乎无法弄清楚的故障。
我的目标:从串口读取格式化的二进制消息,并将它们传递给处理器。
消息格式如下所示:
(MSG-HEADER)(MSG-ID)(MSG-LENGTH)(DATA0)(DATA1)(DATA2)...(DATA-N)
数据中的每个“字”都是 16 位(2 字节)。我的基本做法是从“读取消息头”状态开始,每次发生串口数据接收事件时,我都会从串口读取数据,将数据存储在缓冲区中,然后检查是否检测到消息头.如果我检测到消息头,我会进入“读取数据”状态,在此状态下我会一直将数据读取到数据缓冲区中,直到读取完字节。
这似乎工作得很好,除了偶尔我会看到“数据故障”。我最终存储的消息如下所示:
(MSG1-HEADER)(MSG1-ID)(MSG1-LENGTH)(DATA0)(DATA1)(DATA2)(MSG2-HEADER)(MSG2-ID)..etc
基本上,我每隔一段时间就会得到一个正确的消息头、消息 ID、消息长度,然后数据开始(通常大约 200 个字节),在该数据的中间,我看到另一个消息头、消息 ID 和消息长度,并且可能是另一个消息数据部分的开始。而且我似乎无法弄清楚为什么。
这是我正在使用的串口数据接收事件中的代码:
public byte[] headerBuff = new byte[500];
public byte[] dataBuff = new byte[500];
public byte[] tempBuff = new byte[500];
public int bytesRead;
public int dataPos;
public int dataMsgLen;
public int dataBytesRead = 0;
public bool READ_HEADER = true;
ConcurrentQueue<byte[]> serialQ = new ConcurrentQueue<byte[]>();
//private void si_DataReceived(byte[] data)
private void si_DataReceived(object s, EventArgs e)
{
//If we're supposed to be reading the header, read some bytes and look
// For the header identification sequence (0xF989)
if (READ_HEADER)
{
//Read some bytes, save how many we read
bytesRead = comport.Read(headerBuff, 0, comport.BytesToRead);
//Any time we call comport.REad, we automatically log those bytes to a file
using (BinaryWriter writer = new BinaryWriter(File.Open(defDataDir, FileMode.Append)))
writer.Write(headerBuff.Skip(0).Take(bytesRead).ToArray());
//Loop through the bytes we just read and look for sequence
for (int i = 0; i < (bytesRead-1); i++)
{
if (headerBuff[i] == 0xF9 && headerBuff[i + 1] == 0x89)
{
//We have identified a header
// Lets copy it into a new array
dataPos = bytesRead-i;
Array.Copy(headerBuff, i, dataBuff, 0, dataPos);
dataMsgLen = dataBuff[4];
//Now we can switch to message logging
READ_HEADER = !READ_HEADER;
Array.Clear(headerBuff, 0, headerBuff.Length); //clear the buffer for next time
break; // don't need to look for headers anymore
}
}
}
//If we are done reading the header, let's wait until we get
// enough bytes to store the data message
else if (!READ_HEADER)
{
// Read some bytes into temp array
var tempNumBytes = comport.Read(tempBuff, 0, comport.BytesToRead);
//ADD this into data buffer
Array.Copy(tempBuff, 0, dataBuff, dataPos + dataBytesRead, tempNumBytes);
//Increment our counter
dataBytesRead += tempNumBytes;
//Save to stream
using (BinaryWriter writer = new BinaryWriter(File.Open(defDataDir, FileMode.Append)))
writer.Write(tempBuff.Skip(0).Take(tempNumBytes).ToArray());
//Add to FIFO if we have read enough bytes
if (dataBytesRead >= (dataMsgLen * 2))
{
//Debug.Print(BitConverter.ToString(dataBuff));
serialQ.Enqueue(dataBuff.Select(x => x).ToArray()); // Add to queue for processing
READ_HEADER = !READ_HEADER; // Go back to looking for headers
dataBytesRead = 0;
}
}
}
感谢您的帮助,如果您需要任何说明,请告诉我。
提前谢谢你。
【问题讨论】:
-
你在哪里有这个问题 - 在serialQ,日志文件或两者中?此外 - 您无法确定是否读取了完整的标头,因此 databuf[4] 可能会失败。并且不确定 temBuff 是否仅包含 MSG1 的其余部分。它可能有来自 MSG2 的数据。如果我没有忽略某些内容,则在阅读 MSG1 后跳过 tempBuff 的其余部分(忽略下一个标题)。
-
有几个错误。最大的一个是 serialQ.Enqueue(dataBuff),它不断地将相同的 byte[] 数组放入队列中。你不知道它什么时候会出队。但是您的 DataReceived 事件处理程序只是不断写入 dataBuff,破坏了前一帧。一部分是新数据,一部分是旧数据。您必须复制帧或分配新的 dataBuff。
-
@JeffRSon,我在 serialQ 中遇到了这个问题,而不是日志文件(据我所知,这似乎很好)。
-
@HansPassant - 我想我对 Queue 类的观察不够仔细,我认为它在 Queue 中复制了一份,但听起来情况并非如此。我会更仔细地研究它。
-
@HansPassant,你能详细说明我犯的其他错误吗?谢谢。
标签: c# serial-port