在C#中从串口读取数据

12

我尝试使用Readline()但数据被丢失,我尝试使用Read()但不确定如何进行错误处理,因为我可能会连续收到多个数据包,并且无法知道是否还有其他数据包要到来。在数据包之间,BytesToRead为0,因此我无法使用它。当向缓冲区读取数据时,您需要计时器或将线程休眠以等待所有数据包到达吗?

我感到迷茫。不知道接下来该尝试什么。

我应该提到,我无法保证从串口传输的字符串以\n或\r或\r\n结尾。 我只需要一种绝对可靠的方法来读取用户在衡器上按PRINT时产生的全部数据包。

有人在这里提出了我喜欢的想法-等待一定时间获取所有数据包,但他们删除了回答。 你可以重新发布这个想法吗?

3个回答

22

你尝试过监听SerialPort类的DataReceived事件吗?

public class MySerialReader : IDisposable
{
    private SerialPort serialPort;
    private Queue<byte> recievedData = new Queue<byte>();

    public MySerialReader()
    {
        serialPort = new SerialPort();
        serialPort.Open();

        serialPort.DataReceived += serialPort_DataReceived;
    }

    void serialPort_DataReceived(object s, SerialDataReceivedEventArgs e)
    {
        byte[] data = new byte[serialPort.BytesToRead];
        serialPort.Read(data, 0, data.Length);

        data.ToList().ForEach(b => recievedData.Enqueue(b));

        processData();
    }

    void processData()
    {
        // Determine if we have a "packet" in the queue
        if (recievedData.Count > 50)
        {
            var packet = Enumerable.Range(0, 50).Select(i => recievedData.Dequeue());
        }
    }

    public void Dispose()
    {
        if (serialPort != null)
            serialPort.Dispose();
    }

不能保证从串口读取的数据实际上是serialPort.BytesToRead。建议检查serialPort.Read的返回值。 - user8128167

10

我们之前经历了同样的过程。

要读取“数据包”的唯一方法是在流中具有某些概念,以确定它们的开始和结束位置。

来自MSDN

由于SerialPort类缓冲数据,而BaseStream属性中包含的流不会缓冲数据,因此两者可能会发生关于可读取的字节数的冲突。 BytesToRead属性可以指示有可读取的字节,但这些字节可能对于包含在BaseStream属性中的流不可访问,因为它们已被缓冲到SerialPort类中。

我们使用了后台线程(您可以使用BackgroundWorker)将数据读入缓冲区。 如果无法可靠地使用SerialPort.Newline属性设置终止字符(例如,它会变化!),那么您将需要实现自己的数据包检测系统,因为您将无法使用阻塞的SerialPort.ReadLine()方法。

您可以使用SerialPort.Read()(或字符串使用SerialPort.ReadExisting()方法)读入字节缓冲区,并在检测到有效数据包时生成事件。 请注意,Read()(我假设ReadExisting())会清空SerialPort的缓冲区,因此您需要将数据存储在其他地方。

如果设置了SerialPort.ReadTimeout,则可以处理TimeoutException并轻松处理设备未传输数据的情况。 如果您正在使用固定数量的字节或其他非终止模式,则这是重置数据包检测的好方法。 (如果不需要部分数据包,请在超时时使用SerialPort.DiscardInBuffer())。

祝你好运


据我所知,目前没有检测方案。我会尝试计时 - 在处理之前等待几毫秒以允许数据包到达。我看不到其他选择。我使用ReadLine()6个月,直到有人发现数据丢失的错误。 - sarsnake
我们使用许多工业秤,通常可以在设备中设置终止字符。您是否能够控制输入设备以查看此内容?ReadLine更容易 :) - Byron Ross
我们要做的是:(ASCII 13)。我将尝试使用SerialPort.ReadByte()逐字节读取数据,并查找13。我还将设置SerialPort.NewLine为\n(现在它设置为\r\n...嗯,也许这就是问题所在。谢谢! - sarsnake
最初我将SerialPort.NewLine设置为\n,但它没有起作用,所以我切换到了\r\n。到目前为止,我一直在使用ReadLine。 - sarsnake

4

由于字节可以随时到达,缓冲传入数据非常关键。因此,您应该:

  1. 缓冲传入的数据
  2. 扫描您的缓冲区以查找完整数据
  3. 从缓冲区中删除已使用的数据

我想知道您是否仍然遇到串口问题。如果是这样,我使用C#开发了一个串口编程语言,我相信它几乎解决了每个人所遇到的所有问题。

您能否请看一下并尝试一下?例如,您可以像以下方式缓冲来自串口的传入数据并轻松进行字符串操作。

state Init
  // define a global variable
  our $BUFFER = "";
  jump(Receive);
end state

state Receive
  recv();
  $len = length($DATA_PACKET);
  if("$len > 0") {
    $BUFFER += $DATA_PACKET;
    call(Parser);
  }
end state

state Parser
  // check if buffer matchs regular expression pattern
  if(match($BUFFER, "(?<WILLDELETE>.*?<STX>(?<DATA>.*?)<ETX>(?<CHECKSUM>[0-9A-F]{2}))")) {
    // Received complete data
    $lenData = length($WILLDELETE);
    $BUFFER = remove($BUFFER, 0, $lenData);

    // Do operations with the other parsed fields. $DATA and $CHECKSUM in this example.
  }
end state

该项目在sourceforge上免费提供,如果您有任何问题,请随时提问。

项目主页

下载链接


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