串口通讯随笔
之前了做了个串口通讯的项目,主要用到 SerialPort类
这里主要说说几个问题
1. 在关闭串口的时候软件容易卡死的问题.
我们在使用SerialPort类打开串口时候,会开一个接收数据的线程来接收从串口发送过来的数据,也可以开个调用SerialPort类的事件DataReceived (我这里使用后者,自己开启的线程要一直循环接收数据,消耗资源)
_serialPort = New SerialPort() AddHandler _serialPort.DataReceived, AddressOf serialPort_DataReceived '添加串口接收数据事件 Private Sub serialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs)
' '这里写接收数据的处理
'
End Sub
在写好接收数据处理的方法后, 我们来看看具体的处理方法
private Delegate sub DelShowMessage(Byval msg As String) '定义一个带字符串参数的委托
Private Sub serialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs)
Dim bytestoread As Integer = _serialPort.BytesToRead If bytestoread = 0 Then Exit Sub End If
'定义字节数组接收数据 Dim buffer(bytestoread - 1) As Byte
'读取缓冲区数据到字节数组中 _serialPort.Read(buffer, 0, bytestoread)
'把字节转成相应的字符串 (这里只是个Demo 真正的项目接收的字节都是有自己的协议进行转换的) Dim messagestr As String = ConvertBuffer(buffer) '接收数据这个方法是个线程,用委托来处理显示
Me.Invoke(New DelShowMessage(AddressOf ShowMessage),messagestr)
End Sub
‘在文本框中显示字符串 Private Sub ShowMessage(Byval message As String) TextBox1.Text=message
End Sub
'字节转换
Private Function ConvertBuffer(ByVal buffer() As Byte) As String
Return BitConverter.ToString(buffer)
End Function
上段代码将接收到的数据转换成字符串显示到文本框中
下面是关闭端口的方法
'关闭端口方法
Private Sub btnPortClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPortClose.Click if _serialPort.IsOpen = True Then
_serialPort.Close()
End If End Sub
就是这样直接关闭端口, 有时候软件会卡死
找到原因: 在点击按钮关闭端口的时候, 关闭这个方法在UI线程中运行, 如果串口接收的数据正好也需要用委托UI线程, 这样操作的的时候, 会结束这个接收的方法,如果这个方法同样在操作UI线程,就会造成死锁.
改进的方法:1. 在接收的数据的时候加一个标志说明已经有数据在处理,数据处理结束将此标志恢复, 在点击按钮关闭串口的时候, 等待这个标志恢复,再去关闭这个串口.
在点击按钮关闭串口的时候同样加一个标志说明需要关闭串口, 接收数据的时候判断是否有此标志, 有此标志则不进行处理
2. 是比较简单的方法, 在处理完数据界面显示的用异步委托
第一种方法:
'关闭端口方法
Private _bClosing As Boolean = False
private _bListening As Boolean = False Private Sub btnPortClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPortClose.Click '添加关闭标志,说明在关闭串口
_bCloseing = True
'监听界面是否更新数据
While (_bListening)
Application.DoEvents()
End While
if _serialPort.IsOpen = True Then
_serialPort.Close() EndIf
_bCloseing = False
End Sub
Private Sub serialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs)
'监听端口是否关闭
If _bCloing = True Then
Exit Sub
End If
Try
_bListening = True
If _serialPort.IsOpen = True Then
Dim bytestoRead As Integer = _serialPort.BytesToRead()
If bytestoread=0 Then
Exit Sub
End
'定义字节数组接收数据 Dim buffer(bytestoread - 1) As Byte
'读取缓冲区数据到字节数组中 _serialPort.Read(buffer, 0, bytestoread)
'把字节转成相应的字符串 (这里只是个Demo 真正的项目接收的字节都是有自己的协议进行转换的) Dim messagestr As String = ConvertBuffer(buffer) '接收数据这个方法是个线程,用委托来处理显示
Me.Invoke(New DelShowMessage(AddressOf ShowMessage),messagestr)
End If
Catch ex As Exception
Finally
_bListening = False
End Try
End Sub
第二种方法:
将委托的那部分用异步委托代替
Me.BeginInvoke(New DelShowMessage(AddressOf ShowMessage),messagestr)