在C#中未完整读取串口数据

5
我正在使用 .Net Framework 4,在 C# 中创建串口 GUI 应用程序。在某些情况下,SerialPort 对象(port)无法接收所有数据(我将其与混合信号示波器进行比较)。
例如,如果连接的设备发送:
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
我只能接收到:
0x00 0x01 0x02 0x03 0x04 0x08 0x09
我尝试了不同的代码,但仍然存在相同的问题。
  • Use the data received event to fill a buffer:

        private void dataReceived(object sender, SerialDataReceivedEventArgs e) {
            while (port.BytesToRead > 0){
                byte[] newBytes = new byte[port.BytesToRead];
                int LengthRead = port.Read(newBytes, 0, newBytes.Length);
                Array.Resize(ref newBytes, LengthRead);
                System.Buffer.BlockCopy(newBytes, 0, buffer, positionInBuffer, newBytes.Length);
                positionInBuffer += newBytes.Length;
            }
        }
    
  • Looping for an expected number of bytes, in this case causing the TimeoutException:

            while (port.BytesToRead < expectedSize)
        {
            System.Threading.Thread.Sleep(10);
            waitingLoop++;
            if (waitingLoop > TimeOut)             // wait for a 1s timeout
                throw new TimeoutException();
        }
    
        newBytes = new byte[expectedSize];
        LengthRead = port.Read(newBytes, 0, newBytes.Length);
        if (LengthRead != newBytes.Length)
            throw new TimeoutException(); // or any exception, doesn't matter...
    

我尝试改变ReadBufferSize的大小,超时信息等,但都没有起作用。有什么想法吗?

3个回答

4
这段代码存在一些微妙的错误。第一个片段中的Array.Resize()函数没有任何作用,也许需要调整buffer的大小?第二个片段在接收到比expectedSize更多的字节时会出现TimeoutException异常。这是有可能发生的,而不是超时问题。
但我认为真正的问题是你在这段代码之外对数组的处理。通常情况下,调用SerialPort.Read()只能获取几个字节。串口速度很慢,所以你需要一种协议来知道何时接收到完整的响应。一种非常常见(非二进制)的协议就是简单地以换行符终止响应。然后你可以在DataReceived事件处理程序中使用ReadLine()函数。在你的情况下,正确的方法并不明显。
此外,还要实现ErrorReceived事件。它将告诉你发生了什么不好的事情,比如SerialError.Overrun或RXOver,这些错误也会使字节消失。

你说得对,我遇到了SerialError.Overrun错误,情况很糟糕。 如果我理解正确,这个错误是硬件溢出,对吗? Joe提出的握手协议能帮助我解决这种问题吗? 我正在使用FTDI芯片将RS232(没有硬件流控制线)转换为USB,但我不确定在这种情况下是否可以正确使用软件流控制。 - mgross
嗯,为什么缓冲区中有800字节时会出现TimeoutException?这不应该发生。不要调整ReceivedBytesThreshold。是的,如果RTS和DTR线路没有接线,则硬件握手无法工作。 - Hans Passant
我正在使用以下协议:<ID><SIZE><DATA....><CHECKSUM>。因此,在读取SIZE数据后,我知道应该期望的数据数量。最多为800字节,因此我将缓冲区大小设置为4096以确保安全。在我的第二个示例中,我使用SerialPort缓冲区来使我的代码“简约”,并且在读取之前等待接收所有帧。我尝试过双缓冲(使用DataReceived),但结果相同。 - mgross
30兆字节?那是串口能够运行的最快速度,你需要115千波特才能跟上。抛弃你现在在PC上使用的串口设备,你需要一个更好的带有可以跟上的驱动程序的设备。这真的应该用以太网来完成。 - Hans Passant
1
你只处于五个悲痛阶段的第一阶段。祝你好运。 - Hans Passant
显示剩余6条评论

2
尝试在串口上配置软件(XON/XOF)或硬件(RTS/CTS)握手,以匹配传输设备的配置。请保留HTML标签。

0

感谢答案。

实际上,我在串行数据中有某种协议。它类似于:

<ID><SIZE><DATA....><CHECKSUM>

通常我能正确获取ID、SIZE,然后等待预期的“size”数据字节(大小小于1000且串口运行速率为115200,所以应该是很短的时间)。但是当我等待它们时,例如使用第二个代码(等待循环),即使它们确实通过串行线路传递(使用示波器进行了检查),它们也从未出现在我的C#代码中。仍在运行第二个代码时,我得到了第一个TimeOutException(在等待循环上),这意味着我没有在1秒内接收到所有数据(也尝试将其增加到5秒)。因此,在进入SerialPort.Read()函数之前就捕获了错误。
无论如何,我将检查ErrorReceived事件以查看是否捕获到任何内容...
关于Array.Resize(),我写了这个函数以防SerialPort.Read()返回的数据字节数少于预期值。缓冲区已经预先调整为我的串口缓冲区的最大大小(4096)。
关于我代码中的第二个错误,您是完全正确的。我应该使用更合适的异常 :)

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