【问题标题】:How to read full packet data from serialPort in c#如何在c#中从serialPort读取完整的数据包数据
【发布时间】:2016-05-07 00:09:19
【问题描述】:

我正在实现 SAS 协议,其中记录了 SAS 将使用大小为 11 位的数据包进行轮询,其中一个起始位、八个数据位、第九个唤醒位和一个停止位。还记录了当 SAS 发送消息时,它为消息的第一个字节设置唤醒位,之后其他字节消息的唤醒位被清除。 而且我们必须使用奇偶校验位作为唤醒位。

我在接收来自 comport 的数据时遇到问题。 我想得到的数据是:

01 73 1D 00 09 03 00 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 B3 74 A4 02 0E 76

但我得到了

73 1D 00 09 03 00 00 01 02 03 04 05 06

这里的第一个字节是01 没有进入接收到的数据以及06 之后的数据

下面是创建串口对象的代码。

SerialPort _serialPort = new SerialPort("COM1", 19200, Parity.None, 8, StopBits.One);

当我写信给 Comport 时,我将 Parity 设置为 MARK。喜欢:

  byte[] f = Response.ToArray();
  _serialPort.Parity = Parity.Mark;
  _serialPort.Write(f, 0, f.Length);

当我从 comport 阅读时

private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
 {
            SASobj._serialPort.ParityReplace = 0;

            SASobj._serialPort.Parity = Parity.Space;

            byte[] data = new byte[SASobj._serialPort.BytesToRead];
            SASobj._serialPort.Read(data, 0, data.Length);

            InputData = ByteToHex(data);

            this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });


        }

 private void SetText(string text)
        {

            this.txtMessage.Text += text;

        }

【问题讨论】:

  • 该代码永远不会像您期望的那样工作,不能保证您在读取操作时会收到完整的数据包,看看我对这些问题的回答,它们将帮助您了解如何阅读数据正确:stackoverflow.com/questions/35175874/…stackoverflow.com/questions/35636655/…stackoverflow.com/questions/30499210/…
  • DataReceived 事件中更改端口Parity 是没有意义的;决定是Parity.None/.Space还是.Mark开头。我想这就是为什么你在第一个块之后停止接收数据的原因。还要发布ByteToHex 方法,因为它可能有错误。此外,如果您想确保块不会乱序连接,您应该同步(在处理程序内部)将数据排入Queue<byte[]>(甚至是string)。 dispatch 整个队列/string 的 UI 更新,因为 SetText 内部的连接可能会乱序执行。

标签: c# .net serial-port


【解决方案1】:

由于您的协议似乎滥用了奇偶校验位,您应该在开始接收数据之前SerialPort.ParityReplace 设置为零。 SerialPort.Parity 的实际值实际上是无关紧要的,因为无论如何你都会有一半的时间出现奇偶校验错误,但是在传入消息的中间更改这些属性肯定会产生问题。

但是,请注意,奇偶校验错误是使用不同的事件报告的,即SerialPort.ErrorReceived,其中一个参数设置为SerialError.RXParity

另外,正如我在 cmets 中所指出的,调用 BeginInvoke 不是一个好主意,因为它将方法调用排队在后台 (ThreadPool) 线程上,这意味着对 SetText 的多次调用发生的顺序不会'不必是连续的。

正如@Gusman 所建议的,有更好的方法从端口获取数据,但确保您获得有序合并的最简单方法是:

readonly object _lock = new object();
SerialPort _serialPort;
string _text = "";

public void Init()
{
    _serialPort = new SerialPort("COM1", 19200, Parity.Space, 8, StopBits.One);
    _serialPort.ParityReplace = 0;
    _serialPort.DataReceived += DataReceived;
    _serialPort.ErrorReceived += ErrorReceived;
    _serialPort.Open();
}

void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    DumpToHexString();
}

void ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
    if (e.EventType == SerialError.RXParity)
        DumpToHexString();
}

void DumpToHexString()
{
    lock (_lock)
    {
        while (_serialPort.BytesToRead > 0)
        {
            var chunk = new byte[_serialPort.BytesToRead];
            _serialPort.Read(chunk, 0, chunk.Length);
            _text += ByteToHex(chunk);
        }
    }        
    this.BeginInvoke(new Action(() => txtMessage.Text = _text));
}

此外,SerialPort.ErrorReceived 的 MSDN 文档对 rx 奇偶校验错误有注释:

如果流的尾部字节发生奇偶校验错误,则会在输入缓冲区中添加一个额外的字节,值为 126。

我假设这意味着应该丢弃 ErrorReceived 内的值 126 的尾随字节,但我实际上并没有这样的具体情况,所以你可能想检查一下是否真的是这种情况。

【讨论】:

  • 谢谢.. 这很有帮助,但我还有一个问题。实际上 SAS 协议总是用 80 和 81 轮询,当我得到这个结果时,它会在开始和结束时附加 80 和 81。比如“80 {01 73 1D 00 09 03 00 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 B3 74 A4 02 0E 76} 81 80 81 80”我只想要大括号结果如果我将逻辑用于获取价值,那么结果与我得到的方式相同。实际上我怎么知道这是数据包的开始,而这个位是数据包的结束。
  • 这些字节应该总是在开头/结尾吗?此消息是否有 CRC/校验和?
  • 好的,我已经通过在锁定之前将线程休眠 40 毫秒来修复它,并且在写入 comport 之前我将线程休眠 20 毫秒。通过这样做,我得到了数据,我在文档中读到 SAS 在 40 毫秒内向我发送了整个数据,所以我让线程休眠了 40 毫秒。这样做的方法是否正确?
  • @ManishKumar:在 Windows 中休眠通常不是一个好方法,因为不能保证精度。它可能在大多数情况下都有效,但在某些情况下会失败。我不知道你为什么要睡觉。如果我理解正确,您将字节 0x80 0x81 发送到端口,这些字节会附加到传入消息中?我不明白为什么延迟会改变这一点,因为 .NET SerialPort 是系统串行端口的包装器,它有自己的传入缓冲区(无论您的延迟如何都会被填充)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-15
  • 2016-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多