【问题标题】:SerialPort data loss - C#SerialPort 数据丢失 - C#
【发布时间】:2010-05-12 18:21:22
【问题描述】:

我正在开发一个 SerialPort 应用程序,其中一个非常简单的部分就是给我带来问题。我只是想从端口读取恒定的数据流并将其写入二进制文件。问题似乎是速度:我的代码在我的 9600 波特测试设备上运行良好,但是当转移到115200bps 实时设备,我似乎正在丢失数据。发生的事情是在一段可变的时间之后,我错过了 1 个字节,这会丢弃其余的数据。我尝试了一些方法:

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    bwLogger.Write((byte)serialPort1.ReadByte());
}

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    byte[] inc = new byte[serialPort1.BytesToRead];
    serialPort1.Read(inc, 0, inc.Length);

    bwLogger.Write(inc);
}

还有一些变化。我不能使用 ReadLine() 因为我正在处理恒定的数据流(对吗?)。我试过摆弄缓冲区大小(serialPort1.ReadBufferSize 和硬件 FIFO 缓冲区)。理想情况下,出于可用性目的,我会在软件方面处理此问题,而不是让用户必须更改 Windows 驱动程序设置。

有什么想法吗?

【问题讨论】:

  • 这就是为什么通信协议有规则和校验和的原因。您应该针对一种协议进行编码,该协议使您能够灵活地检测此类事件并从中恢复。您可以使用超时来清除缓冲区并根据需要重新发送。您会不时丢失数据。时期。故事结局。您需要让每个终端设备从这种情况中恢复正常(即不“丢弃”其余数据)。
  • 这是很好的建议。由于我的特定设备的独特功能,我能够线性插入数据以填充任何丢失或“坏”的数据包。正如您所提到的,在大多数通信情况下,通过针对适当的协议进行开发,这种类型的事件的处理肯定会更加健壮。对任何从事任何形式交流的人的好建议。
  • 我很抱歉。重新阅读后,我了解到您指的是硬实时要求,而缺少一个意味着丢失了一大块永远不会回来的数据。请参阅我即将发布的关于线程的答案。即使您已经接受了答案,也可能值得研究。

标签: c# serial-port


【解决方案1】:

如果问题似乎是您无法足够快地处理数据,您可以尝试双缓冲数据。

1) 允许一个线程将串口读入一个缓冲区。这可能涉及将数据从端口复制到缓冲区中(我对 .NET 不是很熟悉)。

2) 当您准备好处理传入数据时,(在不同的线程上)让您的程序读入第二个缓冲区,此时您应该将第一个缓冲区写入磁盘。

3) 当第一个缓冲区写入磁盘时,将其交换回串口缓冲区,并将第二个缓冲区写入磁盘。重复过程,不断交换缓冲区。

【讨论】:

  • 在解决了从未真正解决我的问题的各种问题之后 - 事实证明我需要实现双缓冲区。我很高兴我重新审视了这个问题,甚至更多,以便您花时间重新发布另一个很棒的建议。谢谢!值得庆幸的是,.NET 通过简单地将 BufferedStream 包装在标准 FileStream(它有自己的内部缓冲区)周围来处理所有事情。我想在我能够绝对确认这一点之前,我还要进行一些测试,但是这个缓冲逻辑似乎已经解决了一些小问题。
【解决方案2】:

您可以尝试使用 SerialPort 对象的 Handshake 属性启用握手。

您必须在发送者或接收者上都设置它。但是:如果您溢出接收器的 UART 缓冲区(非常小,16 字节 IIRC),可能没有其他方法。如果您无法在发送方上启用握手,您可能必须保持在 9600 或以下。

【讨论】:

  • 似乎是正确的想法。我搞乱了握手设置,但总是让它受软件控制。将驱动程序设置更改为硬件流控制似乎已经解决了这个问题,至少在短期测试中是这样。我必须进行 12 小时以上的测试,看看它是否成立,在这种情况下,我会让你知道并给你其中一个复选标记。好电话,我应该调查一下。
【解决方案3】:

我会尝试以下方法:

  • 将 Buffer-Size 设置为至少 230K 字节
  • 将传入阈值设置为 16K、32K 或 65K
  • 将此固定数据块写入文件

我不确定这是否会有所帮助,但至少应该承受框架的压力来经常触发事件。

【讨论】:

  • 这似乎对这个问题没有帮助,尽管从逻辑上讲我认为它会。无论如何,这是一个很好的建议,由于引发的事件较少,似乎必须减少我的内存使用量。谢谢。
【解决方案4】:
  1. 我会检查 Read(Byte>[], Int32, Int32) 方法返回的读取字节数,并确保它符合您的预期。

  2. 确保您正在侦听端口对象上的 SerialErrorReceivedEventHandler ErrorReceived 事件。 RXOver 错误表明您的缓冲区已满。

  3. 检查输出缓冲区的线程安全性。如果对输出缓冲区的写入不是线程安全的,则第二次写入可能会破坏第一次写入。

【讨论】:

    【解决方案5】:

    您的 bwLogger 是 BinaryWriter 类吗?您可以尝试将它与 BufferedStream 一起使用,以使磁盘 I/O 成为非阻塞。

    另外,如果您的数据包有一个已知的结束字符,您可以设置 SerialPort.NewLine 属性以使您能够使用 ReadLine/WriteLine,尽管我认为这不会产生太大的性能差异。

    【讨论】:

      【解决方案6】:

      我最近使用的机器都发送停止码(在我的例子中是 ASCII 码 3 或 4)。如果你也有这个功能,你可以在你的 SerialPort 对象上使用ReadTo(string)

      【讨论】:

      • 不幸的是,我没有那种奢侈:\ 只是一个恒定的二进制数据流。 ReadTo 方法肯定适用于实用程序的其他地方。感谢您的提醒。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-15
      • 2020-09-13
      • 2013-05-28
      • 1970-01-01
      相关资源
      最近更新 更多