【问题标题】:Processing Serial Port String data in C# very fast在 C# 中非常快速地处理串行端口字符串数据
【发布时间】:2021-07-06 17:57:49
【问题描述】:

我的串口数据来自 STM32,格式如下:

Array_A 0.0382, 0.0382, 0.0382, 0.0382, 0.0382, 0.0382, 0.0389, 0.0394, 0.0382
Array_B 0.0077, 0.0077, 0.0077, 0.0077, 0.0077, 0.0077, 0.0077, 0.0077, 0.0077

我对数据的处理代码如下

if (RadioButtonA.Checked || RadioButtonB.Checked)
                {   
                    string StrSerialIn = SerialPort1.ReadExisting();
                    string StrSerialInRam;
                    System.Windows.Forms.RichTextBox TB = new System.Windows.Forms.RichTextBox();
                    TB.Multiline = true;
                    TB.Text = StrSerialIn;
                    StrSerialInRam = TB.Lines[0];
                    if (StrSerialInRam.Contains("Poti"))
                    {
                        textBox2.Text = StrSerialInRam;
                    }
                    StrSerialInRam = TB.Lines[0].Substring(0, 8);
                    if (StrSerialInRam == "Array_A ")
                    {
                        Sen_A = TB.Lines[0];
                        string[] valA = Sen_A.Split(',');
                        S1A_Val = valA[0].Substring(8);
                        S2A_Val = valA[1];
                        S3A_Val = valA[2];
                        S4A_Val = valA[3];
                        S5A_Val = valA[4];
                        S6A_Val = valA[5];
                        S7A_Val = valA[6];
                        S8A_Val = valA[7];
                        S9A_Val = valA[8];
                    }
                    Sen_A = "";
                    StrSerialInRam = TB.Lines[1];
                    if (StrSerialInRam.Contains("Poti"))
                    {
                        textBox2.Text = StrSerialInRam;
                    }
                    StrSerialInRam = TB.Lines[1].Substring(0, 8);
                    if (StrSerialInRam == "Array_B ")
                    {
                        Sen_B = TB.Lines[1];
                        string[] valB = Sen_B.Split(',');
                        S1B_Val = valB[0].Substring(8);
                        S2B_Val = valB[1];
                        S3B_Val = valB[2];
                        S4B_Val = valB[3];
                        S5B_Val = valB[4];
                        S6B_Val = valB[5];
                        S7B_Val = valB[6];
                        S8B_Val = valB[7];
                        S9B_Val = valB[8];                        
                    }
                    Sen_B = "";
                    StrSerialInRam = TB.Lines[2];
                    if (StrSerialInRam.Contains("Poti"))
                    {
                        textBox2.Text = StrSerialInRam;
                    }
                }

我的问题是当前的数据处理在 30 Hz 左右完成,但我希望在 350Hz - 400Hz 左右尽快处理我的数据。目前,由于处理速度慢,我正在丢失一些数据。有没有最快的方法来做到这一点?提前致谢。

【问题讨论】:

  • 为什么要使用 TextBox 和 RichTextBox 控件来处理数据?此外,永远不要(永远)使用Lines 属性,除非时间根本不算数。使用 StringBuilder 对象来存储您的数据。不要以任何方式使用 UI 元素。
  • 所有代码逻辑都包含在一个方法中。您应该以不同的方法(或异步任务/线程)拆分为 UI 逻辑和网络逻辑。将两者混合在一起会导致 UI(冻结或响应缓慢)和网络数据处理的性能问题。
  • @Jimi - 我没有使用过 StringBuilder 对象。你能举一些例子或参考吗?我必须提取来自 STM 的 Array 数据的每个值并将其存储在 datagridview 中。
  • 我建议你重建你的代码,StringBuilder 类的功能很容易理解。你还需要更好地解释你的数据是由什么组成的,例如,为什么你有所有这些if (StrSerialInRam.Contains("Poti")) { textBox2.Text = StrSerialInRam; }。您必须指定接收数据的方式。串行端口通常意味着在 UI 线程以外的线程中引发的事件(例如,DataReceived)。 高速(我们称之为)需要智能缓冲,可能需要处理队列。
  • DataGridView 更新完全是另一回事。您使用的是什么数据源?期待什么样的互动?用户能否以您想要达到的速度读取/评估值? -- .Net 版本也很重要,等等。

标签: c# .net winforms


【解决方案1】:

我建议在单独的线程中处理数据并使用来自您的 MCU 的二进制协议。

要传输数据结构,请在 MCU 中使用 struct:

typedef struct{
    int Data1;
    int Data2;
}myStruct;

使用例如 RFC1662 标准来制作框架。

在 C# 网站上:

  1. 在 DataReceived 中接收所有传入的数据并放入 RFC1662 类中。
  2. 在 PacketReceived 中,您将获得一个完整数据包的 byte[] 缓冲区。
  3. 将 byte[] 转换为结构。
  4. 推送结构 tu SyncBuffer 以进行同步。
  5. 例如使用 Timer Tick 和 PopItems 呈现数据。

祝你好运!

    public Form1()
    {
        InitializeComponent();
        syncBuffer = new SyncBuffer<MyData>();

        sp = new SerialPort();
        sp.PortName = "COM1";
        sp.BaudRate = 9600; // Use higher baudrate
        sp.DataReceived += Sp_DataReceived;

        rfc = new RFC1662();
        rfc.PacketError += Rfc_PacketError;
        rfc.PacketReceived += Rfc_PacketReceived;
    }



    SerialPort sp;
    RFC1662 rfc;
    SyncBuffer<MyData> syncBuffer;

    private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        // Receive data stream
        while(sp.BytesToRead > 0)
        {
            int length = sp.BytesToRead;
            byte[] buff = new byte[length];
            sp.Read(buff, 0, length);
            rfc.PutData(buff, length);
        }
    }

    private void Rfc_PacketReceived(byte[] buffer)
    {
        // One frame (structure) received
        MyData data = LowLevelUtils.FromBytes<MyData>(buffer);
        syncBuffer.PushItem(data); // Put to buffer
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        // Periodically pop items from buffer
        MyData[] data = syncBuffer.PopItems();
        foreach (MyData item in data)
        {
            // Present one data message
        }
    }

    private void Rfc_PacketError(Exception ex, byte[] buffer)
    {
        // Handle packet error
    }

    public struct MyData
    {
        public int Data1;
        public int Data2;
    }

RFC1662:

public class RFC1662
{
    public const byte STX = 0x7E;
    public const byte ESC = 0x7D;
    public readonly byte[] ESC_STX = { 0x7D, 0x5E };
    public readonly byte[] ESC_ESC = { 0x7D, 0x5D };

    public RFC1662()
    {
        m = new MemoryStream();
        writer = new BinaryWriter(m);
    }
    
    public delegate void DlgPacketReceived(byte[] buffer);
    public event DlgPacketReceived PacketReceived;

    public delegate void DlgPacketError(Exception ex, byte[] buffer);
    public event DlgPacketError PacketError;

    MemoryStream m;
    BinaryWriter writer;

    public void PutData(byte[] data, int length)
    {            
        for (int i = 0; i < length; i++)
        {
            byte b = data[i];
            if (b == STX)
            {
                byte[] buffer = m.ToArray();
                if (buffer.Length > 0)
                {
                    bool bufferOK = false;
                    try
                    {
                        buffer = RemoveEscSequences(buffer);
                        bufferOK = true;
                    }
                    catch (Exception ex)
                    {
                        if (this.PacketError != null)
                            this.PacketError(ex, buffer);
                    }

                    if (this.PacketReceived != null && bufferOK)
                        this.PacketReceived(buffer);
                }
                m.Dispose();
                writer.Dispose();
                m = new MemoryStream();
                writer = new BinaryWriter(m);
            }
            else
            {
                writer.Write(b);
            }
        }                        
    }


    /// <summary>
    /// Decode message from RFC1662 standard
    /// </summary>
    /// <param name="buffer"></param>
    /// <returns></returns>
    public byte[] RemoveEscSequences(byte[] buffer)
    {
        using (MemoryStream m = new MemoryStream())
        {
            using (BinaryWriter writer = new BinaryWriter(m))
            {
                int length = buffer.Length;
                for (int i = 0; i < length; i++)
                {
                    byte b = buffer[i];
                    switch (b)
                    {
                        case ESC:
                            i++;
                            if (buffer[i] == ESC_STX[1])
                            {
                                writer.Write(STX);
                            }
                            else if (buffer[i] == ESC_ESC[1])
                            {
                                writer.Write(ESC);
                            }
                            else
                            {
                                throw new FormatException("RFC1662 Corupted");
                            }

                            break;
                        default:
                            writer.Write(b);
                            break;
                    }
                }
            }
            return m.ToArray();
        }
    }

    /// <summary>
    /// Code message to RFC1622 standard
    /// </summary>
    /// <param name="buffer"></param>
    /// <returns></returns>
    public byte[] RemoveSpecialCharacters(byte[] buffer)
    {
        using (MemoryStream m = new MemoryStream())
        {
            using (BinaryWriter writer = new BinaryWriter(m))
            {
                int length = buffer.Length;
                for (int i = 0; i < length; i++)
                {
                    byte b = buffer[i];
                    switch (b)
                    {
                        case STX:
                            writer.Write(ESC_STX);
                            break;
                        case ESC:
                            writer.Write(ESC_ESC);
                            break;
                        default:
                            writer.Write(b);
                            break;
                    }
                }
            }
            return m.ToArray();
        }
    }
}

同步缓冲区:

public class SyncBuffer<T>
{
    private List<T> data = new List<T>();
    /// <summary>
    /// Add item to collection to the end
    /// </summary>
    /// <param name="item"></param>
    public void PushItem(T item)
    {
        lock (this)
        {
            data.Add(item);
        }
    }

    /// <summary>
    /// Clear colection
    /// </summary>
    public void Clear()
    {
        lock (this)
        {
            data.Clear();
        }
    }

    /// <summary>
    /// Get items from collection and clear collection
    /// </summary>
    /// <returns></returns>
    public T[] PopItems()
    {
        T[] ret;
        lock (this)
        {
            ret = data.ToArray();
            data.Clear();
        }
        return ret;
    }

    public int Count
    {
        get
        {
            int count;
            lock (this)
            {
                count = data.Count;
            }
            return count;
        }
    }
}

LowLevelUtils:

public static class LowLevelUtils
{
    public static byte[] GetBytes<T>(T str) where T : struct
    {
        int size = Marshal.SizeOf(str);
        byte[] arr = new byte[size];
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.StructureToPtr(str, ptr, true);
        Marshal.Copy(ptr, arr, 0, size);
        Marshal.FreeHGlobal(ptr);

        return arr;
    }

    public static T FromBytes<T>(byte[] arr) where T : struct
    {
        T str = new T();
        int size = Marshal.SizeOf(str);
        IntPtr ptr = Marshal.AllocHGlobal(size);

        Marshal.Copy(arr, 0, ptr, size);

        str = (T)Marshal.PtrToStructure(ptr, str.GetType());
        Marshal.FreeHGlobal(ptr);

        return str;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    相关资源
    最近更新 更多