【问题标题】:C#: An approach to plot rapid serial port dataC#:一种绘制快速串行端口数据的方法
【发布时间】:2013-11-11 12:43:34
【问题描述】:

我正在尝试绘制实时数据。一个电路快速发送数据,我知道第一个字节是“$”,接下来的 16 个字节是声音,最后一个字节是脉冲传感器数据。我正在使用一种将数据存储到数组的方法,它从最后一个索引。

byte[] Read_Data1 = new byte[100000];
byte[] Read_Data2 = new byte[100000];
byte[] Read_Data3;
private void myPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    if (!myPort.IsOpen)
    return;
    while (myPort.BytesToRead > 0)
    {
        int bytes = myPort.BytesToRead;
        byte[] buffer = new byte[bytes];
        myPort.Read(buffer, 0, bytes);
        bytetobyte(Read_Data1, buffer, buffer.Length, count);
        count += buffer.Length;
    }
}
public void bytetobyte(byte[] Storage, byte[] databyte, int datacount, int count)
{
    //count comes from count += buffer.Lenght
    int abc;
    for (abc = 0; abc < datacount; abc++)
    {
        Storage[abc+count] = databyte[abc];
    }
}

然后我开始使用一种适用于计时器滴答的方法来处理数据。我应该删除第一个字节'$'并且需要将 16 个字节保存到一个列表和 1 个字节到另一个列表。方法如下:

byte[] Read_Data3;
LinkedList<byte> data;
public void DrawingAudioData(byte[] data) //This method works inside timer.
{
    Read_Data2 = Read_Data1;
    int lastCount = count;
    int division = count / 18;
    int remaning = (count - 18 * division);

    Read_Data3 = new byte[count - remaning];

    for (int i = 0; i < count - remaning ; i++)
    {
        Read_Data3[i] = Read_Data2[i];
    }
    count = 0;

    IPointListEdit listAuido = curveAudio.Points as IPointListEdit;
    IPointListEdit listPulse = curvePulse.Points as IPointListEdit;

    XDate time = new XDate(DateTime.Now);

    if (Read_Data3 == null)
    return;
    data= new LinkedList<byte>(Read_Data3);

    if(data.First == null)
    return;
    if (data.Count >= 18 & data.ElementAt(0) == Convert.ToByte('$'))
    {
        data.Remove(veri.ElementAt(0));
        for (int x = 0; x < 16; x++)
        {
            listAuido.Add(time, data.ElementAt(0));
            data.Remove(data.ElementAt(0));
        }
        listPulse.Add(time, data.ElementAt(0));
        data.Remove(data.ElementAt(0));
    }
    lastCount = 0;
}

我认为当计时器滴答时我应该有一个新字节,因为串行端口快速发送数据并且数据在 Read_Data1 内部更改。之后,我将计数归零。因为数据仍在流动,我不想让我的数组超出范围。然后我开始处理新的 Read_Data2。正如我之前所说,我必须处理每 18 个字节,所以我修剪了 Read_Data2 并删除了最后几个数据,我均衡了Read_Data3[i]=Read_Data2[i]; 然后使用包含 Read_Data3 的LinkedList,我尝试编写一个循环来删除之后的第一个数据将其填写到列表中。处理 LinkedList 后,lastCount 等于 0,因为我应该根据新的数据集设置新的大小。看起来逻辑是正确的,但我有一个问题。有时脉搏传感器数据会列在音频数据中。我的意思是数据应该在 listPulse 中,但它会进入 listAudio。

首先我想知道方法是正确的方法。我的意思是使用计时器是快速数据的好主意?请分享您的意见。如果您有更好的方法,请告诉我或提供参考文章或示例链接。谢谢。

【问题讨论】:

  • 你这里的似乎是并发问题
  • 我同意@SalvatoreSorbello。您获取数据的速度有多快,以及处理它的速度有多快?你需要什么样的时间分辨率?
  • 电路为 1 khz,波特率为 38400。我需要每 20 ms 或更短时间绘制一次数据。 @SalvatoreSorbello 你的意思是计时器在方法完成循环之前工作?
  • @Blast 是的。考虑到这是您在编程时最头疼的问题之一,您应该最后使用lock(),但最有效的解决方案取决于您的整个代码,而不仅仅是方法
  • 现在我正在尝试在 SerialPort_DataRecived 事件中编写一个线程。其实我已经写好了。它修剪数据,但我无法将数据填充到列表中。如果您知道更好的方法,而不是在 DataRecieved 事件或 lock 中使用线程,请与我分享。忘记感谢您的回复。

标签: c# arrays loops serial-port signal-processing


【解决方案1】:

尝试用这种方式修改你的代码:

byte[] Read_Data3;
LinkedList<byte> data;
object w=new object(w)
public void DrawingAudioData(byte[] data) //This method works inside timer.
{
  lock(w}
  {
  //Your previous code here
  }
}

这样你就不应该再有listPulse、listAuido、Read_Data3、Read_Data2之间混合数据的问题了。

为了正常工作,方法必须在

【讨论】:

    【解决方案2】:

    我会使用带锁或互斥锁的双缓冲来控制对缓冲区的访问,而不是计时器,这将是用于读写的共享资源。此外,从 SerialPort.DataReceived 事件内部调用的 SerialPort.Read(byte[],int,int) 方法已经在新线程上自动执行。

    当您尝试同时读取和写入同一内​​存位置时,就会出现问题。解决此问题的一种方法是使用 2 个缓冲区(字节类型的数组)并写入其中一个,同时您的图形代码从另一个读取。在这种情况下,缓冲区被称为共享资源,它们的访问应该受到监管。您可以使用锁,但我通常为每个缓冲区使用一个互斥锁。实例化互斥锁后,您调用“WaitOne”方法,该方法将强制任何其他想要使用共享资源的代码等待互斥锁被释放。它可能看起来像这样。

      void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (writeToBuffer1)
            {
                numToRead1 = SerialPort.BytesToRead;
                mutex1.WaitOne();
                numRead1 = SerialPort.Read(buffer1, 0, numToRead1);
                mutex1.ReleaseMutex();
                writeToBuffer1 = false;
                PlotData(buffer1, numRead1);
            }
            else
            {
                numToRead2 = SerialPort.BytesToRead;
                mutex2.WaitOne();
                numRead2 = SerialPort.Read(buffer2, 0, numToRead2);
                mutex2.ReleaseMutex();
                writeToBuffer1 = true;
                PlotData(buffer2, numRead2);
            }
        }
    

    您的 PlotData 方法还需要访问 mutex1 和 mutex2。使用 WaitOne 和 ReleaseMutex 方法将读取字节的代码包围起来。您还需要学习如何编写对绘图数据方法的线程安全调用,以便它可以被创建它的线程以外的线程调用。 Thread-safe method

    【讨论】:

    • 感谢您的建议,但我需要理清思路。你的意思是我应该在 SerialPort_DataReceived 事件中使用带有双缓冲的线程而不是计时器?我问这个是因为我对双缓冲、锁和互斥锁没有足够的知识。
    • 我已经编辑了我的答案并添加了详细信息。见上文。
    猜你喜欢
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多