C#用於開發上位機非常的方便,因為這屬於可視化編程,很多時候我們需要做的只是把我們需要用到的控件拉到窗口界面,雙擊控件就會自動生成代碼框架,我們再把其中的一些邏輯調通了就可以。因為一般情況下C#不會用到指針,所以編寫程序就會變得更加簡單,閱讀起來也很通俗易懂。
最近做的項目有涉及到串口通訊的,為了方便測試,我用C#寫了一個上位機作為測試工具。其中有一個控件必不可少,那就是serialPort控件。我們只需要在工具箱找到這個控件,然后把它拖到我們的窗口界面,但是它並不會像其他控件一樣出現在界面上,而是出現在界面下方。
1、設置屬性
接着我們需要設置一些相關的參數,比如波特率,數據位,停止位,校驗位等等,設置的方法有兩種,第一是選中這個控件,然后 在屬性里面直接修改:
第二種是在Form_Load()函數里面修改,相關的一些屬性介紹如下:
.PortName 串口名稱,COM1, COM2等。
.BaudRate 波特率,也就是串口通訊的速度,進行串口通訊的雙方其波特率需要相同,如果用PC連接其他非PC系統,一般地,波特率由非PC系統決定。
.Parity 奇偶校驗。可以選取枚舉Parity中的值
.DataBits 數據位
.StopBits 停止位,可以選取枚舉StopBits中的值. .Handshake 握手方式,也就是數據流控制方式,可以選取枚舉Handshake中的值
屬性說明
名 稱 |
說 明 |
BaseStream |
獲取 SerialPort 對象的基礎 Stream 對象 |
BaudRate |
獲取或設置串行波特率 |
BreakState |
獲取或設置中斷信號狀態 |
BytesToRead |
獲取接收緩沖區中數據的字節數 |
BytesToWrite |
獲取發送緩沖區中數據的字節數 |
CDHolding |
獲取端口的載波檢測行的狀態 |
CtsHolding |
獲取“可以發送”行的狀態 |
DataBits |
獲取或設置每個字節的標准數據位長度 |
DiscardNull |
獲取或設置一個值,該值指示 Null 字節在端口和接收緩沖區之間傳輸時是否被忽略 |
DsrHolding |
獲取數據設置就緒 (DSR) 信號的狀態 |
DtrEnable |
獲取或設置一個值,該值在串行通信過程中啟用數據終端就緒 (DTR) 信號 |
Encoding |
獲取或設置傳輸前后文本轉換的字節編碼 |
Handshake |
獲取或設置串行端口數據傳輸的握手協議 |
IsOpen |
獲取一個值,該值指示 SerialPort 對象的打開或關閉狀態 |
NewLine |
獲取或設置用於解釋 ReadLine( )和WriteLine( )方法調用結束的值 |
Parity |
獲取或設置奇偶校驗檢查協議 |
ParityReplace |
獲取或設置一個字節,該字節在發生奇偶校驗錯誤時替換數據流中的無效字節 |
PortName |
獲取或設置通信端口,包括但不限於所有可用的 COM 端口 |
ReadBufferSize |
獲取或設置 SerialPort 輸入緩沖區的大小 |
ReadTimeout |
獲取或設置讀取操作未完成時發生超時之前的毫秒數 |
ReceivedBytesThreshold |
獲取或設置 DataReceived 事件發生前內部輸入緩沖區中的字節數 |
RtsEnable |
獲取或設置一個值,該值指示在串行通信中是否啟用請求發送 (RTS) 信號 |
StopBits |
獲取或設置每個字節的標准停止位數 |
WriteBufferSize |
獲取或設置串行端口輸出緩沖區的大小 |
WriteTimeout |
獲取或設置寫入操作未完成時發生超時之前的毫秒數 |
二、打開串口
設置好屬性之后應該打開串口,調用serialPort.Open();即可。
三、發送和接收數據
發送數據時可以采用serialPort.Write()方法,或者serialPort.WriteLine()方法,接受數據采用serialPort.Read()和serialPort.ReadLine()方法。
serialPort.WriteLine()和serialPort.Write()兩者的區別是前者是阻塞式的,如果接收方沒有及時讀取數據,就會引起TimeoutException異常,這跟serialPort.ReadLine()是一樣的,serialPort.ReadLine()和serialPort.WriteLine()都是以換行符作為結束的,如果serialPort.ReadLine()在讀取數據的時候一直沒有讀取到換行符,那么在等待ReadTimeout時間后,拋出一個TimeoutException。因為這兩個方法是阻塞性的,所以在使用的時候一般交由其他線程進行讀寫,避免因為阻塞而導致程序不響應。
對於字節或字符數據,用Read()方法來讀數據,該方法需要一個字節或字符數組作為參數來保存讀取的數據,結果返回實際讀取的字節或字符數。寫數據使用Write()方法,該方法可以將字節數組、字符數據或字符串發送給另一方。如果通訊雙方交換的數據位字節流數據,要構建一個使用的串口通訊程序,那么雙方應該定義數據幀格式。通常數據楨由幀頭和幀尾來界定。發送數據比較簡單,只需要將構造好的數據用Write()方法發送出去即可。接收數據則比較復雜,通訊是以字節流的形式到達的,通過調用一次Read()方法並不能確保所讀取的數據就是完整一幀。因此需要將每次讀取的數據整合在一起,對整合后的數據進行分析,按照定義的幀格式,通過幀頭和幀尾,將幀信息從字節流中抽取出來,這樣才能獲取有意義的信息。除了利用Read()方法來讀數據,還可以使用ReadExisting()方法來讀取數據。該方法讀取當前所能讀到的數據,以字符串的形式返回。
四、事件
SerialPort 提供了DataReceived事件。當有數據進入時,該事件被觸發。該事件的觸發由操作系統決定,當有數據到達時,該事件在輔助線程中被觸發。輔助線程的優先級比較低,因此並不能確保每個字節的數據到達時,該事件都被觸發。在使用該事件接收數據時,最好對定義通訊協議格式,添加楨頭和楨尾。在DataReceived事件中接收數據時,把數據放在數組中或字符串中緩沖起來,當接收的包含楨頭和楨尾的完整數據時,在進行處理,另外,為了有效地接收數據,可以在每次讀取數據后,加入System.Threading.Thread.Sleep方法進行演示。
示例代碼:
首先要在Form_Load()函數里手動添加事件處理程序:
1 serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialPortDataReceivedEventHandler);
處理程序的具體內容:
1 private void SerialPortDataReceivedEventHandler(object sender, SerialDataReceivedEventArgs e) 2 { 3 byte[] readBuffer = null; 4 int n = serialPort1.BytesToRead; 5 byte[] buf = new byte[n]; 6 serialPort1.Read(buf, 0, n); 7 buffer.AddRange(buf); 8 9 while (buffer.Count >= 5) 10 { 11 if ((buffer[0] == 0x0A)&&(buffer[4] == 0xff)) //幀頭和幀尾接收正確 12 { 13 if (buffer[1] == 0x01) //命令字 14 { 15 ... //處理數據內容 16 } 17 buffer.RemoveRange(0, 5); //從緩沖區中清除 18 } 19 } 20 21 }
五、方法說明
方 法 名 稱 |
說 明 |
Close |
關閉端口連接,將 IsOpen 屬性設置為False,並釋放內部 Stream 對象 |
Open |
打開一個新的串行端口連接 |
Read |
從 SerialPort 輸入緩沖區中讀取數據字節數 |
ReadByte |
從 SerialPort 輸入緩沖區中同步讀取一個字節 |
ReadChar |
從 SerialPort 輸入緩沖區中同步讀取一個字符 |
ReadLine |
一直讀取到輸入緩沖區中的 NewLine 值 |
ReadTo |
一直讀取到輸入緩沖區中指定 value 的字符串 |
Write |
已重載。將數據寫入串行端口輸出緩沖區 |
WriteLine |
將指定的字符串和 NewLine 值寫入輸出緩沖區 |
DiscardInBuffer DiscardOutBuffer |
清空接收緩沖區數據 清空輸出緩沖去數據 |
參考:https://www.cnblogs.com/fx2008/p/4317302.html
六、this.invoke()的作用和用法
Invoke()的作用是:在應用程序的主線程上執行指定的委托。一般應用:在輔助線程中修改UI線程( 主線程 )中對象的屬性時,調用this.Invoke();
正確的做法是將工作線程中涉及更新界面的代碼封裝為一個方法,通過 Invoke 或者 BeginInvoke 去調用,兩者的區別就是一個導致工作線程等待,而另外一個則不會。
如我在輔助線程里修改TextBox.Text的內容,應該這樣寫:
1 this.Invoke(new Action(() => 2 { 3 TextBox1.Text = "L"; 4 }));
而不能直接寫成:
1 TextBox1.Text = "L";
這是錯誤的。