命名空間:using System.IO.Ports;
該類提供了同步 I/O 和事件驅動的 I/O、對管腳和中斷狀態的訪問以及對串行驅動程序屬性的訪問。
操作類聲明: SerialPort sp = null;

/// <summary> /// 打開串口 /// </summary> /// <param name="protName">串口號</param> /// <param name="baudRate">波特率</param> /// <param name="dataBit">數據位</param> /// <param name="stopBits">停止位</param> /// /// <param name="parity">校驗位</param> /// <returns></returns> public bool OpenCom(string protName, int baudRate, int dataBit, float stopBits, int parity) { bool flag = true; if (sp == null) { sp = new SerialPort(); } sp.PortName = protName;//串口號 sp.BaudRate = baudRate;//波特率 float f = stopBits;//停止位 if (f == 0) { sp.StopBits = StopBits.None; } else if (f == 1.5) { sp.StopBits = StopBits.OnePointFive; } else if (f == 1) { sp.StopBits = StopBits.One; } else { sp.StopBits = StopBits.Two; } sp.DataBits = dataBit;//數據位 if (parity == 0) { sp.Parity = Parity.None; } else if (parity == 1) { sp.Parity = Parity.Odd; } else if (parity == 2) { sp.Parity = Parity.Even; } else { sp.Parity = Parity.None; } // sp.ReadTimeout = 1000;//設置超時讀取時間 // sp.WriteTimeout = 1000;//超時寫入時間 try { if (!sp.IsOpen) { sp.Open(); } } catch (Exception) { flag = false; } return flag; } /// <summary> /// 關閉端口 /// </summary> /// <returns></returns> public bool CloseCom() { try { if (sp.IsOpen) { sp.Close(); } return true; } catch { return false; } }
在串口的打開方法中 SerialPort類對分別用[BaudRate]、[Parity]、[DataBits]、[StopBits]屬性設置通訊格式中的波特率、校驗位、數據位、停止位,其中[Parity]和[StopBits]分別是枚舉類型Parity、StopBits,Parity類型中枚舉了Odd(奇)、Even(偶)、Mark、None、Space,Parity枚舉了None、One、OnePointFive、Two。
項目中遇到一個問題是,一個界面開關幾次后,再次關閉,要等到很長時間才能退出,等再次打開窗口,串口類接收數據的效率明顯降低。
原因是 當串口正在進行大量的數據處理和頁面更新的時候,如何強制關閉串口,會造成串口死掉。
解決方法是在串口方法接收數據的時候加入一個變量,先控制停止串口數據的接收,然后關閉串口,這樣就不會串口死掉很長時間后才關閉的現象。

public bool SwitchDeviceState(int StateNum) { if (sp == null) { return false; } byte[] data = new byte[8]; data[0] = 0x55;//幀頭 data[1] = 0xAA;//幀頭 data[2] = 0x06;//幀長度 data[3] = 0x02;//設備地址 data[4] = 0x00;//通道號(改變設備狀態時,通道號為0,表示整個設備) data[5] = 0x02;//命令碼 switch (StateNum) { //命令數據 case 0: data[6] = 0x00; break;//空閑 case 1: data[6] = 0x01; break;//循環刺激 case 2: data[6] = 0x02; break;//肌電反饋 case 4: data[6] = 0x04; break;//抓握采集 case 5: data[6] = 0x05; break;//電流標定 } return DataWrite(2, data); } /// <summary> /// 向模擬板發送數據命令 /// </summary> /// <param name="verifyIndex">開始計算校驗位的位置</param> /// <param name="b"></param> /// <returns></returns> public bool DataWrite(int verifyIndex, byte[] b) { bool flag = true; try { int numAdjust = 0; for (; verifyIndex < b.Length - 1; verifyIndex++) { numAdjust += Convert.ToInt32(b[verifyIndex].ToString()); } if (numAdjust > 0xff) { string strAdjust = Convert.ToString(numAdjust, 16); numAdjust = Convert.ToInt32(strAdjust.Substring(strAdjust.Length - 2), 16); } b[b.Length - 1] = (byte)numAdjust; sp.Write(b, 0, b.Length); } catch { flag = false; } return flag; }
在此方面中C++語言有比較好的封裝方法,C#語言我自身沒有找到之前比較好的示例,於是自己寫了一些簡單的方法,已經可以成功向下位機模擬板進行數據發送
DataWrite方法中首先由verifyIndex位開始計算校驗數據,c#中先把十六進制的byte轉換為Int32類型相加減,然后再轉換為byte類型得到校驗位。最后使用 sp.Write(b, 0, b.Length)方法把整個byte數組發送出去,這里要注意的是發送的數據一定要使用byte[] 數組的形式,且發送的都必須時十六進制的數據,以后在接收模擬板的數據時也是一樣。其中原因有待查看。
Serial發送數據的方法有Write和WriteLine,其中WriteLine可發送字符串並在字符串末尾加入換行符。此處采用的是Write方式
3.注冊對象的數據接收事件的方法(可以在串口操作類的構造函數中注冊)
SerialPort中串口數據的讀取與寫入有較大的不同。由於串口不知道數據何時到達,因此有兩種方法可以實現串口數據的讀取。一、線程實時讀串口,即每個一段時間抓取串口緩沖區的數據;二、事件觸發方式實現。由於線程實時讀串口的效率不是十分高效,因此比較好的方法是事件觸發的方式。在SerialPort類中有DataReceived事件,當串口的讀緩存有數據到達時則觸發DataReceived事件,其中SerialPort.ReceivedBytesThreshold屬性決定了當串口讀緩存中數據多少個時才觸發DataReceived事件,默認為1。
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
sp.ReceivedBytesThreshold = 1;//事件發生前內部輸入緩沖區的字節數,每當緩沖區的字節達到此設定的值,就會觸發對象的數據接收事件
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] Resoursedata = new byte[sp.BytesToRead];
sp.Read(Resoursedata, 0, Resoursedata.Length);//在此就可以讀取到當前緩沖區內的數據
//執行數據操作
sp.DiscardInBuffer();//丟棄傳輸緩沖區數據
sp.DiscardOutBuffer();//每次丟棄接收緩沖區的數據
}
首先SerialPort類讀取數據的方法有多種,但是有的讀是同步,有的是異步,同步就是和主程序保持一致,只有運行完了ReadByte之后才能運行程序之后的代碼,異步就是重新開啟一個線程來處理這些問題,主程序不受到干擾,繼續運行。
serialPort中有6個讀的方法:
Read();ReadLine(); ReadByte();ReadChar();ReadExisting();ReadTo();
ReadTo和ReadExisting是異步讀取,剩下的都是同步讀取。
我在程序測試中使用ReadTo和ReadExisting獲取緩沖區的數據,由於這兩個方法的接收類型都是String類型,並且顯示出來都是一些編碼混亂的字符,需要進一步編碼格式轉換。因此此處選擇的是Read同步讀取數據,接收數據的類型是一個byte數據,接下來更容易對數據進行下一步的處理和操作。如何考慮系統運行效率的問題(在沒秒鍾內模擬板可能向上位機發送非常龐大的數據量),可以考慮在開一個線程來控制模擬板數據的讀取,然后仍然使用Read方法進行數據的讀取。
4.部分c#數據類型轉換
//十進制轉二進制
Console.WriteLine(Convert.ToString(69, 2));
//十進制轉八進制
Console.WriteLine(Convert.ToString(69, 8));
//十進制轉十六進制
Console.WriteLine(Convert.ToString(69, 16));
//二進制轉十進制
Console.WriteLine(Convert.ToInt32(”100111101″, 2));
//八進制轉十進制
Console.WriteLine(Convert.ToInt32(”76″, 8));
//C# 16進制轉換10進制
Console.WriteLine(Convert.ToInt32(”FF”, 16));