天天看點

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);
            }
        }
           

 

声明:转换方法是摘自热心网友的,追究可删

Â