【问题标题】:C# SerialPort communication protocolC# SerialPort 通信协议
【发布时间】:2013-06-29 09:50:57
【问题描述】:

我确实编写了一个小型 C# 应用程序,它从 COM 端口读取 Arduino 板发送的一系列数字。

问题:

如果 Arduino 每 500 毫秒发送一个值,但我的 C# 程序每 1 秒读取一个值,那么 C# 是否会落在 Arduino 后面?如果这是真的,从 Arduino 发送的数据是存储在缓冲区中还是被丢弃?

[编辑]

下面是我用来从 COM 读取的代码


System.Windows.Forms.Timer tCOM;
...

tCOM.Interval = 1000;
tCOM.Tick += new System.EventHandler(this.timer1_Tick);
...

SerialPort port = new SerialPort();
port.PortName = defaultPortName;
port.BaudRate = 9600; 
port.Open();
.....

private void timer1_Tick(object sender, EventArgs e)
{
    log("Time to read from COM");

    //read a string from serial port
    string l;
    if ((l = port.ReadLine()) != null)
    {
    ......

    }

}

【问题讨论】:

  • 亚历克斯,你是如何阅读串行的。发布您的代码。
  • @FeliceM 更新了我的代码

标签: c# arduino port


【解决方案1】:

串行端口通信通常需要流控制。一种让发送器知道接收器已准备好接收数据的方法。这经常被忽视,尤其是在 Arduino 项目中。与最初使用串行端口的机器相比,串行端口非常慢,而现代机器速度非常快。

但很明显,在您的场景中,有些事情会发生爆炸!过了一会儿。当 PC 中的接收缓冲区已满时,您的 Arduino 将导致缓冲区溢出。这会导致无法挽回的数据丢失。侦听这种情况的通知是其他经常被跳过的事情,您必须为 SerialPort.ErrorReceived 事件注册一个事件处理程序。在这种情况下,您会收到 SerialError.Overrun 通知。没有干净的方法可以从这种情况中恢复,需要完全重置协议。

有两种基本的方法可以在串口上实现流控制来避免这个错误。最常见的一种是使用硬件握手,使用 RTS(请求发送)和 CTS(清除发送)信号。由 Handshake.RequestToSend 提供。当接收缓冲区太满时,PC 会自动关闭 RTS 信号。您的 Arduino 必须注意 CTS 信号,并且在关闭时不要发送任何内容。

第二种方式是软件握手,接收方发送一个特殊字节来表示是否准备好接收数据。由 Handshake.XonXoff 提供,它使用标准控制字符 Xon (Ctrl+Q) 和 Xoff (Ctrl+S)。仅当通信协议不在其数据中使用这些控制代码时才适用。换句话说,当您传输文本而不是二进制数据时。

第三种方法是一种完全不同的方法,也很常见,您使设备仅在 PC 要求时才发送任何内容。主从协议。很容易保证接收缓冲区中有足够的空间用于响应。您在协议中指定特定的命令,即 PC 发送来查询特定数据项的命令。

【讨论】:

    【解决方案2】:

    当您打开串行端口进行输入时,会自动创建一个缓冲区(队列)来保存传入数据,直到它被您的程序读取。此缓冲区的大小通常为 4096 字节(尽管可能因 Windows 版本、串行端口驱动程序等而异)。

    4096 字节的缓冲区通常在几乎所有情况下都足够了。在最高标准波特率(115200 波特)下,它对应于超过 300 毫秒的存储 (FIFO) 先进先出,因此只要您的程序每秒至少为串行端口提供 3 次服务,就不会丢失数据。在您的特定情况下,因为您每 1 秒读取一次串行,如果时序和缓冲数据不匹配,您可能会丢失数据。

    但是在特殊情况下,增加串行输入缓冲区的大小可能会很有用。 Windows 提供了请求增加缓冲区大小的方法,但不能保证该请求会被批准。

    我个人更喜欢从 Arduino 获得连续的数据流,并在我的 c# 应用程序中决定如何处理这些数据,但至少我确信我不会因为所涉及的硬件的限制而丢失信息。

    更新: 经常和 Arduino 一起玩,我也同意 Hans 在他的回答中给出的第三个选项。基本上,您的应用程序应该向 Arduino 发送一个命令以打印出(Serial.Print 或 Serial.Println)您需要的数据并准备好读取它。

    【讨论】: