小記:VB.NET的串口通信用了很長時間了,也只用Write和Read這樣的方法,以前都是用這種方式做上位機軟件,如此足矣。而前幾天研究GSM模塊時對串口返回的數(shù)據(jù)總是把握不好,參考開發(fā)板附送的例程,發(fā)現(xiàn)采用SerialPort的DataReceived事件,可以實現(xiàn)中斷觸發(fā)式的數(shù)據(jù)接收。于是想到要自己做一個串口調試助手,在實現(xiàn)基本功能的前提下增加一些方便自己調試的功能。經過斷斷續(xù)續(xù)的編寫,就做成了下面這個小軟件:
這個軟件能夠實現(xiàn)串口調試助手的全部功能,經過通信測試,數(shù)據(jù)接收性能不亞于呼嘯工作室的SComAssistant2.2,通過加大輸入緩沖區(qū),可以滿足大量數(shù)據(jù)接收。
VB.NET的串口通信主要使用VS自帶的SerialPort控件,而不是早先的MSComm,更具有兼容性,這也是很久以前就放棄VB改用.NET的直接原因。該控件的主要方法、屬性如下(該數(shù)據(jù)來自VS的MSDN幫助庫):
方法 | 說明 |
Open | 打開一個新的串行端口連接。 |
Close | 關閉端口連接,將 IsOpen 屬性設置為 false,并釋放內部 Stream 對象。 |
Read | 已重載。 從 SerialPort 輸入緩沖區(qū)中讀取。 |
ReadByte | 從 SerialPort 輸入緩沖區(qū)中同步讀取一個字節(jié)。 |
ReadChar | 從 SerialPort 輸入緩沖區(qū)中同步讀取一個字符。 |
ReadExisting | 在編碼的基礎上,讀取 SerialPort 對象的流和輸入緩沖區(qū)中所有立即可用的字節(jié)。 |
ReadLine | 一直讀取到輸入緩沖區(qū)中的 NewLine 值。 |
ReadTo | 一直讀取到輸入緩沖區(qū)中的指定 value 的字符串。 |
Write | 已重載。 將數(shù)據(jù)寫入串行端口輸出緩沖區(qū)。 |
WriteLine | 將指定的字符串和 NewLine 值寫入輸出緩沖區(qū)。 |
DiscardInBuffer | 丟棄來自串行驅動程序的接收緩沖區(qū)的數(shù)據(jù)。 |
DiscardOutBuffer | 丟棄來自串行驅動程序的傳輸緩沖區(qū)的數(shù)據(jù)。 |
GetPortNames | 獲取當前計算機的串行端口名稱數(shù)組。 |
屬性 | 說明 |
PortName | 獲取或設置通信端口,包括但不限于所有可用的 COM 端口。 |
BaudRate | 獲取或設置串行波特率。 |
DataBits | 獲取或設置每個字節(jié)的標準數(shù)據(jù)位長度。 |
Parity | 獲取或設置奇偶校驗檢查協(xié)議。 |
StopBits | 獲取或設置每個字節(jié)的標準停止位數(shù)。 |
IsOpen | 獲取一個值,該值指示 SerialPort 對象的打開或關閉狀態(tài)。 |
BytesToRead | 獲取接收緩沖區(qū)中數(shù)據(jù)的字節(jié)數(shù)。 |
BytesToWrite | 獲取發(fā)送緩沖區(qū)中數(shù)據(jù)的字節(jié)數(shù)。 |
Encoding | 獲取或設置傳輸前后文本轉換的字節(jié)編碼。 |
ReadBufferSize | 獲取或設置 SerialPort 輸入緩沖區(qū)的大小。 |
ReceivedBytesThreshold | 獲取或設置 DataReceived 事件發(fā)生前內部輸入緩沖區(qū)中的字節(jié)數(shù)。 |
WriteBufferSize | 獲取或設置串行端口輸出緩沖區(qū)的大小。 |
想要通過串口收發(fā)數(shù)據(jù),就需要對串口進行配置,包括設置端口、波特率、數(shù)據(jù)格式(如COM1端口、9600bps、8位數(shù)據(jù)位、無校驗位、1位停止位)等屬性,之后通過Open方法打開串口。打開串口可通過手動指定,也可以使用GetPortNames方法獲取計算機中存在的串口。如果打開出錯,則可能是串口不存在或者已被占用。下面是相應代碼:
Private Sub SerialPortOpen()
On Error GoTo Err
If SerialPort.IsOpen = True Then SerialPort.Close() '避免重復打開端口
SerialPort.Open()
LabelCOMStatus.Text = "串口已打開"
Exit Sub
Err: MsgBox("串口不存在或已被占用!" + vbNewLine + ErrorToString()) '出現(xiàn)錯誤,顯示錯誤信息
End Sub
如果想要在串口中支持中文字符收發(fā),則可在初始化時設置串口控件的編碼:
SerialPort.Encoding = System.Text.Encoding.Default
發(fā)送數(shù)據(jù)通過Write方法來完成,由于串口調試助手需要支持文本和16進制,需要加入轉換代碼:
Private Sub ButtonSendData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSendData.Click
On Error GoTo Err
Dim outDataBuf As String = TextBoxSend.Text
If outDataBuf = "" Then Exit Sub '如果輸入文本框中沒有數(shù)據(jù)則不發(fā)送
If SerialPort.IsOpen = True Then '判斷串口是否打開
If HexSendFlag = True Then
'-----------十六進制發(fā)送------------
outDataBuf = outDataBuf.Replace(" ", "") '清除空格與回車
outDataBuf = outDataBuf.Replace(vbNewLine, "")
'十六進制數(shù)據(jù)位數(shù)為偶數(shù),例如:FF 00 15 AC 0D
If outDataBuf.Length Mod 2 <> 0 Then
MsgBox("請輸入正確的十六進制數(shù),用空格和回車隔開。")
Exit Sub
End If
Dim outBytes(outDataBuf.Length / 2 - 1) As Byte
For I As Integer = 1 To outDataBuf.Length - 1 Step 2
outBytes((I - 1) / 2) = Val("&H" + Mid(outDataBuf, I, 2)) 'VB的十六進制表示方法,例如0x1D表示為&H1D
Next
SerialPort.Write(outBytes, 0, outDataBuf.Length / 2)
BarCountTx.Text = Val(BarCountTx.Text) + outDataBuf.Length / 2
Else
'-------------文本發(fā)送--------------
SerialPort.Write(outDataBuf)
BarCountTx.Text = Val(BarCountTx.Text) + outDataBuf.Length '發(fā)送字節(jié)計數(shù)
End If
Else
MsgBox("串口未打開,請先打開串口。")
End If
Exit Sub
Err: MsgBox("數(shù)據(jù)輸入或發(fā)送錯誤!" + vbNewLine + ErrorToString())
End Sub
接收數(shù)據(jù)采用DataReceived事件,該事件在串口輸入緩沖區(qū)中的字節(jié)數(shù)滿足設置條件時觸發(fā),并執(zhí)行事件中的代碼。事件觸發(fā)的字節(jié)數(shù)在ReceivedBytesThreshold屬性中設置,默認為1字節(jié)。由于DataReceived事件采用了獨立的線程,無法對軟件界面中的控件進行直接操作,因而在現(xiàn)實時需要采用委托實例的方法。首先建立委托:
Delegate Sub RecieveRefreshMethodDelegate(ByVal [text] As String) '聲明委托
Dim RecieveRefresh As New RecieveRefreshMethodDelegate(AddressOf RecieveRefreshMethod) '定義數(shù)據(jù)顯示委托實例
Sub RecieveRefreshMethod(ByVal str As String) '定義一個數(shù)據(jù)顯示委托實例的方法
ShowRecieveData(str)
End Sub
其中ShowRecieveData函數(shù)將str字符串顯示到TextBox控件中。
下面是DataReceived事件中對十六進制數(shù)據(jù)的處理。同發(fā)送數(shù)據(jù)一樣,讀取數(shù)據(jù)時也要根據(jù)不同的顯示方式使用不同的方法。VB.NET通過Read方法,根據(jù)緩沖區(qū)中存在的字節(jié)數(shù)讀取十六進制數(shù)據(jù),而文本顯示則簡單的多,只需ReadExisting即可。最后通過Invoke方法調用委托,顯示數(shù)據(jù)。
Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
If HexRecieveFlag Then
'-----------十六進制顯示------------
Dim inDataLen As Integer = SerialPort.BytesToRead() '獲取可讀取的字節(jié)數(shù)
If inDataLen > 0 Then
Dim inBytes(inDataLen - 1) As Byte, bytes As Byte
Dim strHex As String = ""
SerialPort.Read(inBytes, 0, inDataLen) '讀取數(shù)據(jù)
For Each bytes In inBytes
strHex = strHex + [String].Format("{0:X2} ", bytes) '格式化成十六進制(不含&H)
Next
TextBoxRecieve.Invoke(RecieveRefresh, strHex) '調用委托,顯示接收的數(shù)據(jù)
BarCountRx.Text = (Val(BarCountRx.Text) + inDataLen).ToString '接收字節(jié)計數(shù)
End If
Else
'-------------文本顯示--------------
Dim str As String
str = SerialPort.ReadExisting '讀取全部可用字符串
TextBoxRecieve.Invoke(RecieveRefresh, str)
BarCountRx.Text = (Val(BarCountRx.Text) + str.Length).ToString '接收字節(jié)計數(shù)
End If
End Sub
至此就實現(xiàn)了串口收發(fā)的基本功能,另外的定時收發(fā)(使用Timer控件)、文件發(fā)送(使用FileSystem)參見附帶的源文件代碼。
(附件功能完全不給力呀,終于傳上來了……)
在完成串口調試助手的功能后,還可以根據(jù)個人的使用習慣或需求,添加相應的功能。