C# 結構使用

上位機(C#)需要和單片機通過串口傳輸數據,本人也是踩了幾個坑之後纔將該功能實現,時間比較匆忙,寫的潦草,有不清楚的可以追問。

單片機上已經定義好了接口和數據格式(結構)

上位機上處理方法:

1、串口接收到byte數組,從數組中按下標獲取

如果數據結構單一這個方法未嘗不可,如果數據結構較多,結構體較大 那麼需要認真的計算下標,否則很容易出現問題,且不易維護。

例如:


private void sp_DataReceived(object sender, EventArgs e)
            {
            SerialPort sp1 = (SerialPort)sender;
            if (sp1.IsOpen)     //判斷是否打開串口
            {
                try
                {
                    if (this.InvokeRequired) //加線程防止假死
                    {
                        this.Invoke(new MethodInvoker(delegate
                        {
                            int int_len = sp1.BytesToRead;
                            byte[] b = new byte[sp1.BytesToRead];

                            sp1.Read(b, 0, b.Length); //字節

                            myBuffer.AddRange(b);


                            while (myBuffer.Count >= 15)//至少等於報文頭長度
                            {

                                if (myBuffer[0] == 0xf9 && Array.IndexOf(CMDS, myBuffer[1]) >= 0)
                                {
                                    int strLen = (myBuffer[2] << 8) + myBuffer[3]+6;

                                    if (strLen > myBuffer.Count)
                                    {
                                        break;
                                    }
                                    int cmpType = (myBuffer[5] << 8) + myBuffer[6];
                                    switch (cmpType)
                                    {
 
                                        case 0x0B11:
                                            sp.DiscardInBuffer();
                                            sp.DiscardOutBuffer();
                                            myBuffer.RemoveRange(0, myBuffer.Count);

                                            break;
                         
                                        default:
                                            break;
                                    }

                                    myBuffer.RemoveRange(0, myBuffer.Count);

                                }
                                else
                                {

                                    myBuffer.RemoveRange(0, 1);
                                }
                            }
                        }));
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

方法2 將單片機上的數據結構定義到 上位機上

 

1、對應單品機定義一個一樣的結構 用於接收數據 (單片機上已定義  #parama  pack(1))  總之 單片機和 上位機上的 pack(x)要一致

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct TEST
        {

            byte a;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
            byte[] b;/
            byte c;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
            char[] d;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
            string e;// 單片機上定義的長度是多少  
          
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
            string login_data;// 
        }

注意事項:  1、[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]  是結構的對齊方式 ,pack=1 是指結構的對齊方式爲1字節。否則默認是4個字節,不指定pack方式,會發現同樣的結構體,sizeof(結構)的大小不一樣,以至於出現byte數組轉結構的時候,數據對應不上,

如果不使用或者        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)] 中UnmanagedType.ByValTStr與結構中元素不對應,使用在使用sizeof的時候會報 

Marshal.SizeOf報“不能作爲非託管結構進行封送處理;無法計算有意義的大小或偏移量“錯誤。

2、單片機上字符串使用char[200],定長     

c#中結構可以使用string,但是也要定長(定長使用UnmanagedType.ByValTStr),定長的方法

       //
        // 摘要:
        //     用於在結構中出現的內聯定長字符數組。char 類型用於 System.Runtime.InteropServices.UnmanagedType.ByValTStr
        //     取決於 System.Runtime.InteropServices.StructLayoutAttribute 屬性的 System.Runtime.InteropServices.CharSet
        //     參數應用於包含的結構。應始終使用 System.Runtime.InteropServices.MarshalAsAttribute.SizeConst
        //     字段來指示數組的大小。

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
            string e;// 單片機上定義的長度是多少  

單片機上字符串使用unsigned char[28],定長且UnmanagedType.ByValArray, 用於存儲 非字符串的數組

        //
        // 摘要:
        //     當 System.Runtime.InteropServices.MarshalAsAttribute.Value 屬性設置爲 ByValArray時,必須設置
        //     System.Runtime.InteropServices.MarshalAsAttribute.SizeConst 字段指示元素數。數組的。當需要區分字符串類型時,System.Runtime.InteropServices.MarshalAsAttribute.ArraySubType
        //     字段可以選擇包含數組元素的 System.Runtime.InteropServices.UnmanagedType。可以使用此僅 System.Runtime.InteropServices.UnmanagedType
        //     數組中元素的形式出現在結構中的字段的屬性。

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
            char[] d;

 

結構體和數組互轉://僞代碼

   public TESTtest= new TEST();

數組轉結構:

List<byte> myBuffer = new List<byte>();
        private void sp_DataReceived(object sender, EventArgs e)
            {
            SerialPort sp1 = (SerialPort)sender;
            if (sp1.IsOpen)     //判斷是否打開串口
            {
                try
                {
                    if (this.InvokeRequired) //加線程防止假死
                    {
                        this.Invoke(new MethodInvoker(delegate
                        {
                            //  Thread.Sleep(20);
                            int int_len = sp1.BytesToRead;
                            byte[] b = new byte[sp1.BytesToRead];

                            sp1.Read(b, 0, b.Length); //字節

                            myBuffer.AddRange(b);
                            while (myBuffer.Count >= sizeof(TEST))//至少等於報文長度
                            {

                               try

                               {

                                   test= (TEST)BytesToStruct(myBuffer.ToArray(), typeof(TEST));

                              }catch()

                              {

                                   myBuffer.RemoveRange(0, 1);

                                  }
                                          
                                          
                                    }

                                    myBuffer.RemoveRange(0, myBuffer.Count);

                                }
                            }
                        }));
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

結構轉數組:

                byte[] s = StructToBytes(test);

 

轉換方法:
       

/// <summary>
        /// 結構體轉化成byte[]
        /// </summary>
        /// <param name="structure"></param>
        /// <returns></returns>
        public static Byte[] StructToBytes(Object structure)
        {
            Int32 size = Marshal.SizeOf(structure);
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.StructureToPtr(structure, buffer, false);
                Byte[] bytes = new Byte[size];
                Marshal.Copy(buffer, bytes, 0, size);

                return bytes;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }
        /// <summary>
        /// byte[]轉化成結構體
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="strcutType"></param>
        /// <returns></returns>
        public static Object BytesToStruct(Byte[] bytes, Type strcutType)
        {
            Int32 size = Marshal.SizeOf(strcutType);
            IntPtr buffer = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.Copy(bytes, 0, buffer, size);

                return Marshal.PtrToStructure(buffer, strcutType);
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }

 

聲明:轉換方法是摘自熱心網友的,追究可刪

 

發佈了12 篇原創文章 · 獲贊 20 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章