5

我正在尝试通过微控制器从传感器读取多个串行端口。每个串口将接收超过 2000 个测量值(每个测量值是 7 个字节,全部为十六进制)。他们同时开火。现在我正在从 4 个串行端口轮询。此外,我将每个测量值转换为字符串并将其附加到 Stringbuilder。当我完成接收数据时,它们将被输出到一个文件中。问题是CPU消耗非常高,从80%到100%不等。

我浏览了一些文章并将 Thread.Sleep(100) 放在最后。当没有数据到来时,它会减少 CPU 时间。当 BytesToRead 小于 100 时,我还将 Thread.Sleep 放在每次轮询的末尾。它仅在一定程度上有所帮助。

有人可以建议一种解决方案来从串行端口轮询并处理我得到的数据吗?也许每次我得到一些东西时都附加会导致问题?

//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox,     StringBuilder data)
    {
        textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));

        int bytesRead;
        int t;
        Byte[] dataIn;

        while (mySerialPort.IsOpen)
        {
            try
            {
                if (mySerialPort.BytesToRead != 0)
                {
                  //trying to read a fix number of bytes
                    bytesRead = 0;
                    t = 0;
                    dataIn = new Byte[bytesPerMeasurement];
                    t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
                    bytesRead += t;
                    while (bytesRead != bytesPerMeasurement)
                    {
                        t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
                        bytesRead += t;
                    }
                    //convert them into hex string
                    StringBuilder s = new StringBuilder();
                    foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
                    var line = s.ToString();

                                            var lineString = string.Format("{0}  ----          {2}",
                                                      line,
                                                    mySerialPort.BytesToRead);
                    data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.

                    ////use delegate to change UI thread...
                    textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));

                    if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
                }
            else{Thread.Sleep(100);}

            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.ToString());
            }
        }


    }
4

3 回答 3

6

这不是一个好方法,处理 DataReceived 事件要好得多。

基本上,串行端口有一个运行良好的 3 阶段过程。

  • 从串口接收数据
  • 等到你有一个相关的数据块
  • 解释数据

所以像

class DataCollector
{
    private readonly Action<List<byte>> _processMeasurement;
    private readonly string _port;
    private SerialPort _serialPort;
    private const int SizeOfMeasurement = 4;
    List<byte> Data = new List<byte>();

    public DataCollector(string port, Action<List<byte>> processMeasurement)
    {
        _processMeasurement = processMeasurement;
        _serialPort = new SerialPort(port);
        _serialPort.DataReceived +=SerialPortDataReceived;
    }

    private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        while(_serialPort.BytesToRead > 0)
        {
           var count = _serialPort.BytesToRead;
           var bytes = new byte[count];
           _serialPort.Read(bytes, 0, count);
           AddBytes(bytes);
        }
    }

    private void AddBytes(byte[] bytes)
    {
        Data.AddRange(bytes);
        while(Data.Count > SizeOfMeasurement)            
        {
            var measurementData = Data.GetRange(0, SizeOfMeasurement);
            Data.RemoveRange(0, SizeOfMeasurement);
            if (_processMeasurement != null) _processMeasurement(measurementData);
        }

    }
}

注意:添加字节会一直收集数据,直到您有足够的数据作为测量值,或者如果您获得大量数据,则将其拆分为单独的测量值......因此您可以一次获得 1 个字节,下一次获得 2 个字节,并且再下一个,然后将其转换为测量值。大多数情况下,如果您的 micro 以突发方式发送它,它将作为一个输入,但有时它会分成 2 个。

然后你可以做的地方

var collector = new DataCollector("COM1", ProcessMeasurement);

  private void ProcessMeasurement(List<byte> bytes)
            {
                // this will get called for every measurement, so then
                // put stuff into a text box.... or do whatever
            }
于 2013-02-27T23:06:51.957 回答
2

首先考虑阅读Using Stopwatches and Timers in .NET。您可以用它来分解任何性能问题,并准确判断您的代码的哪一部分导致了问题。

使用SerialPort.DataReceived 事件触发数据接收过程。

单独的接收过程和数据操作过程。先存储您的数据,然后再处理。

不要从阅读循环中编辑 UI。

于 2013-02-27T23:34:00.960 回答
1

我猜你应该做的是添加一个事件处理程序来处理传入的数据:

mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);

这消除了为您侦听的每个串行端口运行单独线程的需要。此外,每个 DataReceived 处理程序将在有可用数据时准确调用,并且将仅消耗处理数据所需的 CPU 时间,然后屈服于应用程序/操作系统。

如果这不能解决 CPU 使用问题,则意味着您进行了过多的处理。但是除非你有一些非常快的串行端口,否则我无法想象你在那里的代码会造成问题。

于 2013-02-27T23:14:14.767 回答