串口数据精确时间戳

时间:2013-01-13 23:02:03

标签: c# serial-port

我正在研究一个项目,该项目需要从连接到编码器的串行端口(带有QSB的美国数字S5光轴编码器)读取的每个数据条目的精确时间(ms)。

我将编码器安装在一个小推车上,我用它来计算推车的速度。

这是我到目前为止所做的:

  1. 连接到串口并向QSB写入命令以告知编码器流数据。这里有命令:

    www.usdigital.com/assets/general/QSB%20Commands%20List_1.pdf www.usdigital.com/assets/general/QSB%20Applications%20Examples.pdf

  2. 使用readline()读取收到的数据。

  3. 将所有数据行放入一个StringBuilder并将其输出到文件中。
  4. 当我将输出值阈值和间隔速率设置为尽可能快时,我能够在1ms之间获得数据条目。 这是我得到的:

    ----time stamp(h/m/s/ms)-------value
    

    具有正确时间戳的数据:https://www.dropbox.com/s/pvo1dz56my4o99y/Capture1.JPG

    但是,当数据连续时(我正在以恒定的速度滚动推车),会出现突然的“跳跃”,大约200ms

    时间戳错误的数据:https://www.dropbox.com/s/sz3sxwv4qwsb2cn/Capture2.JPG

    这是我的代码:

    private void buttonOpenEncoderPort_Click(object sender, EventArgs e)
        {
            serialPortEncoder.Write("S0E\r\n");//start streaming data
            System.Threading.Thread.Sleep(500);
            serialPortEncoder.Write("W0B0\r\n");//set threshold to 0 so the encoder will stream data a the interval I set.
            System.Threading.Thread.Sleep(500);
            serialPortEncoder.Write("W0C0000\r\n");//set output interval to 0 so it will stream as fast as possible
            System.Threading.Thread.Sleep(1500);
            backgroundWorkerEncoder.RunWorkerAsync();}
            //I am using a background worker to pull data out.
    
    
     private void backgroundWorkerEncoder_DoWork(object sender, DoWorkEventArgs e)
        {
            while (serialPortEncoder.IsOpen)
            {
                if (serialPortEncoder.BytesToRead != 0)
                {
                    try
                    {
                        String s = serialPortEncoder.ReadLine();//read from encoder
                        LazerBucket.Add(getCurrentTimeWithMS(timeEncoder) + "-----" + s + "\r\n");//put one line of data with time stamp in a List<String>
                        richTextBoxEncoderData.BeginInvoke(new MethodInvoker(delegate()
                        {
                            richTextBoxEncoderData.Text = s; })); //update UI
    
                    }
                    catch (Exception ex) { MessageBox.Show(ex.ToString()); }                   
                }
    
            }
        }
    
    private String getCurrentTimeWithMS(DateTime d)//to get time
        {
            StringBuilder s = new StringBuilder();
            d = DateTime.Now;
            int hour = d.Hour;
            int minute = d.Minute;
            int second = d.Second;
            int ms = d.Millisecond;
            s.Append("  ----" + hour.ToString() + ":" + minute.ToString() + ":" + second.ToString() + ":" + ms.ToString());
            return s.ToString();
        }
    
    如果有人能找到时间跳跃的原因,我会认定它。 200ms太多了,不容忽视。

    EDIT: 
    

    根据建议,我尝试了Stopwatch,但仍有200毫秒的延迟。但是当我打印出时间戳和BytesToRead时,我发现缓冲区中的数据正在减少,因为正在执行readLine()。最终BytesToRead将降至单个数字,这就是延迟发生的地方。我正在寻找有关如何实现线程的更好的解决方案。还有延迟的解释。也许我读得很快,所以缓冲区无法跟上我?

    EDIT:
    

    问题解决了。请参阅下面的答案。谢谢你的回复。秒表确实有帮助。现在我想弄清楚事件驱动或轮询是否更好。

4 个答案:

答案 0 :(得分:3)

在对网络进行了一些无休止的研究之后,我找到了延迟的原因。 设备管理器---&gt;端口----&gt;提前----&gt;将延迟改为1ms将解决问题。 我现在使用单独的线程轮询数据。它工作得很好。

答案 1 :(得分:1)

您使用的是C#4.5吗?如果是这样,我强烈建议您使用async / await而不是BackgroundWorker

此外,DateTime对于实时应用程序并不准确。我建议DateTime严格地作为开始时间然后使用Stopwatch中的System.Diagnostics来获取已用时间,因为开始时间

private void backgroundWorkerEncoder_DoWork(object sender, DoWorkEventArgs e)
{
  var startTime = DateTime.Now;
  var stopwatch = Stopwatch.StartNew();

  while (serialPort.IsOpen && !backgroundWorker.CancellationPending)
  {
    if (serialPort.BytesToRead > 0)
    {
      try
      {
        var line = serialPort.ReadLine();
        var timestamp = (startTime + stopwatch.Elapsed);

        var lineString = string.Format("{0}  ----{1}", 
                                       line,
                                       timestamp.ToString("HH:mm:ss:fff"));

        // Handle formatted line string here.
      }
      catch (Exception ex)
      {
        // Handle exception here.
      }
    }
  }

至于 200 ms 的差异,可能是各种各样的事情。也许BackgroundWorker的优先级较低,并且没有达到您希望的CPU时间。也可能是SerialPort I / O 侧或实际的串行设备本身。

答案 2 :(得分:0)

当您需要精确测量时,不应使用DateTime.Now,请尝试使用秒表。 如详细herehere,DateTime准确但不精确到毫秒。如果您需要精确的精度,请在开始测量时保存DateTime.Now并从秒表获取偏移量。

虽然200ms似乎是一个很长的延迟 - 即使对于DateTime--秒表可能确实解决了你的问题。

答案 3 :(得分:0)

对我而言,操作系统似乎是[以你的方式]。

我建议如下。

  1. 在单独的进程(或服务)或优先级高于正常的单独线程中从端口读取数据

  2. 将原始(!)数据存储在具有准确时间戳的队列中,以便以后处理。这个“任务”应该尽可能轻,以避免GC或调度程序启动并在最短的时间内停止它。例如,没有字符串concats或格式。那些操作花费时间并且会对记忆施加压力。

  3. 在单独的线程或进程中处理该数据。如果时间戳保持一段时间没有真正受到伤害,因为时间戳是准确的。

  4. 总之;将阅读与处理分离。

    Windows的Imo库存版本过多地受IO限制(它喜欢并拥抱交换的概念)适用于RT流程。在差异框上使用差异操作系统,读取并将其发送到Winbox进行进一步处理也许也是一个考虑的选择(最后的手段?)