上位機(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);
}
}
聲明:轉換方法是摘自熱心網友的,追究可刪