上位机(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);
}
}
声明:转换方法是摘自热心网友的,追究可删