【问题标题】:Reading from the serial port in C#从 C# 中的串行端口读取
【发布时间】:2009-03-13 23:19:22
【问题描述】:

我尝试使用 Readline() 并且数据被丢弃,我尝试使用 Read() 但我不知道如何有一个错误证明方法来做到这一点,因为我可能会一个接一个地收到几个数据包而我没有知道会有另一个数据包进来的方法。在数据包之间BytesToRead是0,所以我不能使用它。当向缓冲区读取数据时,您有一个计时器或让线程休眠以允许所有数据包到达?

我迷路了。不知道下一步该尝试什么。

我应该提一下,我不能保证从串行端口出来的字符串将以 \n 或 \r 或 \r\n 结尾。我只需要一种万无一失的方法来读取用户按下 PRINT 时来自秤的所有数据包。

有人在这里回答了我喜欢的想法 - 为所有数据包等待一定的时间,但他们删除了他们的答案。你有机会重新发布吗?

【问题讨论】:

    标签: c# compact-framework serial-port


    【解决方案1】:

    您是否尝试过监听SerialPort 类的DataRecieved 事件?

    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 的返回值。
    【解决方案2】:

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

    读取“数据包”的唯一方法是了解它们在流中的开始和结束位置。

    来自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 个月,直到有人注意到数据丢失的错误。
    • 我们使用了很多工业秤,您可以在设备中正常设置终止符。您是否可以控制输入设备以便查看此内容? ReadLine 更容易 :)
    • 我们这样做:\n (ascii 13)。我将尝试使用 SerialPort.ReadByte() 逐字节读取数据并查找 13。我还将将 SerialPort.NewLine 设置为 \n (我现在将其设置为 \r\n。 ...嗯,也许这就是问题所在。谢谢!
    • 对不起,我的胡言乱语:) 最初我将 SerialPort.NewLine 设置为 \n 但这不起作用,这就是我切换到 \r\n 的原因。到目前为止,我一直在使用 ReadLine。
    【解决方案3】:

    由于字节可能随时进入,缓冲传入数据至关重要。所以你应该

    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 上免费提供,如果您有任何问题,请随时提问。

    Project Homepage

    Download Link

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-12
      • 2012-03-10
      • 1970-01-01
      • 1970-01-01
      • 2017-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多