C#編寫上位機遇到的關於serialPort的若干問題


  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";

這是錯誤的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM