最近在调试带rs485串口通讯的设备,项目上主要是用PLC和串口通讯,因为PLC有集成好的串口块,使用起来比较容易,为了方便测试,就想着用上位机写一个简易的串口通讯程序,用于调试。
在网上查找了一些资料后,基本上选择了两个方式,VB.net和python,python来写串口通讯程序,我在另一篇博客里面已经发了:
https://blog.csdn.net/normer123456/article/details/124402399
本文中介绍一下使用vb.net来写串口程序的示例。
图片是UI界面,vb.net相对来说,界面比python要友好一点,控件可以直接拖放到窗体,而python制作界面,需要利用pyqt这类UI模块,有点麻烦。
软件:Microsoft Visual Studio Community 2019 版本 16.11.3
硬件:windows10电脑
rs485串口设备(流量计)
串口通讯在逻辑步骤上,基本上差不多,无非是初始化参数,打开串口,读写数据,关闭串口,就不多赘述了。
直接看程序吧。
01 初始化:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim baud_installs() As String = New String() _
{"9600", "19200", "38400", "115200"}
baudcombox.Items.AddRange(baud_installs)
baudcombox.Text = baudcombox.Items(0)
databitcombox.Items.AddRange({"8", "7"})
databitcombox.Text = databitcombox.Items(0)
stopbitcombox.Items.AddRange({"0", "1"})
stopbitcombox.Text = stopbitcombox.Items(0)
paritycombox.Items.AddRange({"无", "偶校验", "奇校验"})
paritycombox.Text = paritycombox.Items(0)
ToolStripStatusLabel1.Text = "当前无可用串口。。。"
huayuan(PictureBox1, Color.Gray)
End Sub
这里参数利用combox控件来进行选择,根据所选择不同,传入不同的值来进行初始化,所以,程序启动后,必须先设置参数值,然后才能点击“打开按钮,否则会提示错误。
02 打开串口:
Private Sub portopen()
Try
SerialPort1.Open()
Call portstart()
If SerialPort1.IsOpen Then
ToolStripStatusLabel1.Text = SerialPort1.PortName + "已打开"
ToolStripStatusLabel1.ForeColor = Color.Green
huayuan(PictureBox1, Color.Green)
Console.WriteLine(SerialPort1.PortName)
Console.WriteLine("ok!")
End If
Catch ex As UnauthorizedAccessException
MsgBox("串口被占用或串口错误!", MsgBoxStyle.Information, "tips!")
End Try
End Sub
打开串口就是serial.open即可。
03 写入数据:
Private Sub comsend()
Dim senddata As String
Dim senddata_byte(8) As Byte
'datestr = Format(Now(), "yyyy/MM/dd H:mm:ss ffff")
datestr = Format(Now(), "yyyy/MM/dd H:mm:ss")
'TextBox1.Clear()
'senddata_byte(0) = &H1 '一种表示16进制数的方式
'senddata_byte(1) = &H3
'senddata_byte(2) = &H0
'senddata_byte(3) = &H10
'senddata_byte(4) = &H0
'senddata_byte(5) = &H2
'senddata_byte(6) = &HC5
'senddata_byte(7) = &HCE
senddata_byte = strtohex(TextBox1.Text)
senddata = ""
Try
SerialPort1.Write(senddata_byte, 0, 8)
TextBox1.AppendText(senddata)
TextBox2.AppendText(vbCrLf + "[" + datestr + "]" + " " + senddata + vbCrLf)
comrec()
Console.WriteLine("send ok!")
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.YesNoCancel, "tips!")
End Try
End Sub
写入数据或者说发送数据,就是serial.write,这里需要注意的是,发送的数据是字节数组形式,因此,需要进行字符串到字节的转换。
关于字符串和字节数组之间的互相转换,我在测试的时候,在网上找了不少资料,许多博客虽然写了,但大多是抄来的,或者就是不够实用,很多人是利用字符转成ASCII码,在对应转16进制,程序比较麻烦。
然后我找了半天,发现一个利用正则表达式来进行转换的, 比较简单有效,感谢网络。
string to byte 转换程序:
Private Function strtohex(s As String) '将字符串转成16进制数值
Dim mc As MatchCollection = Regex.Matches(s, "(?i)[\da-f]{2}") '使用正则表达式从输入文本框的字符串中提取16进制数
Dim buf As New List(Of Byte)()
Dim bt As Byte()
For Each m As Match In mc
buf.Add(Byte.Parse(m.Value, System.Globalization.NumberStyles.HexNumber))
Next
bt = buf.ToArray()
Return bt
'Console.WriteLine(bt)
End Function
至于16进制转字符串比较简单, vb.net有个Hex函数直接就可以转换,不多说了。
04 读取数据:
Private Sub comrec()
Dim rxstr As Integer
Dim recstr As String
Dim recbyte(20) As Byte
datestr = Format(Now(), "yyyy/MM/dd H:mm:ss")
Try
rxstr = SerialPort1.Read(recbyte, 0, 10)
For i = 0 To rxstr - 1
recstr = recstr + hextostr(recbyte(i)) + " "
Next
TextBox2.AppendText(vbCrLf + "[" + datestr + "]" + " " + recstr + vbCrLf)
Console.WriteLine(recstr)
Application.DoEvents()
'TextBox1.AppendText(recstr)
Catch ex As Exception
MsgBox(ex.Message + "or 无可用串口", MsgBoxStyle.YesNo, "tips!")
'rxstr = SerialPort1.ReadExisting
'TextBox1.AppendText(vbCrLf + "[" + datestr + "]" + " " + rxstr + vbCrLf)
'Application.DoEvents()
End Try
End Sub
读取就是使用serial.read,或者readall、readline都行。read的话,需要设置缓冲区、偏移量、接收长度,比较简单。
读写完成后,如果不想一直读写。可以关闭串口:
Private Sub portclose()
Try
If SerialPort1.IsOpen Then
SerialPort1.Close()
If SerialPort1.IsOpen = 0 Then
ToolStripStatusLabel1.Text = SerialPort1.PortName + "已关闭"
ToolStripStatusLabel1.ForeColor = Color.Red
huayuan(PictureBox1, Color.Gray)
End If
Else
MsgBox("无可用串口", MsgBoxStyle.Information, "tips!")
End If
Catch ex As Exception
MsgBox("串口未打开或串口异常!", MsgBoxStyle.Information, "tips!")
End Try
End Sub
serial.close即可。
关于程序的一些细节,我这边就不多说了,基本上都是在网上找了很多资料,然后自己一点点试出来的,网上虽然有很多资料,但很少有直接拿过来能用的,想要满足自己使用,还是得自己摸索半天。我把完整程序也发出来,需要说明的是,程序是经过我测试,完全可用的,但我不确定如果有朋友想直接使用,是否是和我一样的。
完整程序:
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Net
Public Class Form1
Dim ser_br As Integer
Dim ser_db As Integer
Dim ser_stopbit As Integer
Dim ser_parity As Integer
Dim datestr As String = ""
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim baud_installs() As String = New String() _
{"9600", "19200", "38400", "115200"}
baudcombox.Items.AddRange(baud_installs)
baudcombox.Text = baudcombox.Items(0)
databitcombox.Items.AddRange({"8", "7"})
databitcombox.Text = databitcombox.Items(0)
stopbitcombox.Items.AddRange({"0", "1"})
stopbitcombox.Text = stopbitcombox.Items(0)
paritycombox.Items.AddRange({"无", "偶校验", "奇校验"})
paritycombox.Text = paritycombox.Items(0)
ToolStripStatusLabel1.Text = "当前无可用串口。。。"
huayuan(PictureBox1, Color.Gray)
End Sub
Private Sub portstart()
SerialPort1.BaudRate = ser_br
SerialPort1.DataBits = ser_db
SerialPort1.StopBits = ser_stopbit
SerialPort1.Parity = ser_parity
SerialPort1.ReadTimeout = 1000
SerialPort1.NewLine = vbCrLf
End Sub
Private Sub getserialportname()
For Each sp As String In My.Computer.Ports.SerialPortNames
ComboBox1.Items.Add(sp)
Next
ComboBox1.Text = ComboBox1.Items(0)
'Console.WriteLine(ComboBox1.Text)
End Sub
Private Sub portopen()
Try
SerialPort1.Open()
Call portstart()
If SerialPort1.IsOpen Then
ToolStripStatusLabel1.Text = SerialPort1.PortName + "已打开"
ToolStripStatusLabel1.ForeColor = Color.Green
huayuan(PictureBox1, Color.Green)
Console.WriteLine(SerialPort1.PortName)
Console.WriteLine("ok!")
End If
Catch ex As UnauthorizedAccessException
MsgBox("串口被占用或串口错误!", MsgBoxStyle.Information, "tips!")
End Try
End Sub
Private Sub portclose()
Try
If SerialPort1.IsOpen Then
SerialPort1.Close()
If SerialPort1.IsOpen = 0 Then
ToolStripStatusLabel1.Text = SerialPort1.PortName + "已关闭"
ToolStripStatusLabel1.ForeColor = Color.Red
huayuan(PictureBox1, Color.Gray)
End If
Else
MsgBox("无可用串口", MsgBoxStyle.Information, "tips!")
End If
Catch ex As Exception
MsgBox("串口未打开或串口异常!", MsgBoxStyle.Information, "tips!")
End Try
End Sub
Private Sub comrec()
Dim rxstr As Integer
Dim recstr As String
Dim recbyte(20) As Byte
datestr = Format(Now(), "yyyy/MM/dd H:mm:ss")
Try
rxstr = SerialPort1.Read(recbyte, 0, 10)
For i = 0 To rxstr - 1
recstr = recstr + hextostr(recbyte(i)) + " "
Next
TextBox2.AppendText(vbCrLf + "[" + datestr + "]" + " " + recstr + vbCrLf)
Console.WriteLine(recstr)
Application.DoEvents()
'TextBox1.AppendText(recstr)
Catch ex As Exception
MsgBox(ex.Message + "or 无可用串口", MsgBoxStyle.YesNo, "tips!")
'rxstr = SerialPort1.ReadExisting
'TextBox1.AppendText(vbCrLf + "[" + datestr + "]" + " " + rxstr + vbCrLf)
'Application.DoEvents()
End Try
End Sub
Private Sub comsend()
Dim senddata As String
Dim senddata_byte(8) As Byte
'datestr = Format(Now(), "yyyy/MM/dd H:mm:ss ffff")
datestr = Format(Now(), "yyyy/MM/dd H:mm:ss")
'TextBox1.Clear()
'senddata_byte(0) = &H1 '一种表示16进制数的方式
'senddata_byte(1) = &H3
'senddata_byte(2) = &H0
'senddata_byte(3) = &H10
'senddata_byte(4) = &H0
'senddata_byte(5) = &H2
'senddata_byte(6) = &HC5
'senddata_byte(7) = &HCE
senddata_byte = strtohex(TextBox1.Text)
senddata = ""
Try
SerialPort1.Write(senddata_byte, 0, 8)
TextBox1.AppendText(senddata)
TextBox2.AppendText(vbCrLf + "[" + datestr + "]" + " " + senddata + vbCrLf)
comrec()
Console.WriteLine("send ok!")
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.YesNoCancel, "tips!")
End Try
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
getserialportname()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Try
SerialPort1.PortName = ComboBox1.SelectedItem()
Console.WriteLine("you choose:" & SerialPort1.PortName)
portopen()
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.YesNoCancel, "tips!")
End Try
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
portclose()
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
ToolStripStatusLabel1.Text = "当前选择串口号:" + ComboBox1.SelectedItem()
'Console.WriteLine(ComboBox1.Text)
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
comsend()
End Sub
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
comrec()
End Sub
Private Sub paritycombox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles paritycombox.SelectedIndexChanged
Select Case paritycombox.Text
Case "无"
ser_parity = 0
Case "偶校验"
ser_parity = 1
Case "奇校验"
ser_parity = 2
End Select
'Console.WriteLine(ser_parity)
End Sub
Private Sub baudcombox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles baudcombox.SelectedIndexChanged
Select Case baudcombox.Text
Case "9600"
ser_br = 9600
Case "19200"
ser_br = 19200
Case "38400"
ser_br = 38400
Case "115200"
ser_br = 115200
End Select
'Console.WriteLine(ser_br)
End Sub
Private Sub databitcombox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles databitcombox.SelectedIndexChanged
Select Case databitcombox.Text
Case "8"
ser_db = 8
Case "7"
ser_db = 7
End Select
End Sub
Private Sub stopbitcombox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles stopbitcombox.SelectedIndexChanged
Select Case stopbitcombox.Text
Case "0"
ser_stopbit = 0
Case "1"
ser_stopbit = 1
End Select
End Sub
'''
''' 16进制转字符函数
''' 本函数为自定义函数
'''
''' 字节
Private Function hextostr(h As Byte) '将16进制数值转成字符串,且二者等效转换
Dim x As String
If h < 10 Then
x = "0" + Hex(h)
Else
x = Hex(h)
End If
Return x
End Function
'''
''' 字符转16进制函数
''' 本函数为自定义函数
'''
''' 字符串
Private Function strtohex(s As String) '将字符串转成16进制数值
Dim mc As MatchCollection = Regex.Matches(s, "(?i)[\da-f]{2}") '使用正则表达式从输入文本框的字符串中提取16进制数
Dim buf As New List(Of Byte)()
Dim bt As Byte()
For Each m As Match In mc
buf.Add(Byte.Parse(m.Value, System.Globalization.NumberStyles.HexNumber))
Next
bt = buf.ToArray()
Return bt
'Console.WriteLine(bt)
End Function
'''
''' 画圆函数
''' 本函数为自定义函数
'''
''' 图形容器
''' 颜色
Private Sub huayuan(pb As PictureBox, c As Color) '在picturebox中绘制圆形的函数
Dim b As Bitmap = New Bitmap(pb.Width, pb.Height)
Dim g As Graphics = Graphics.FromImage(b)
Dim mybrush As New SolidBrush(c)
g.FillEllipse(mybrush, 0, 0, pb.Width, pb.Height)
g.Dispose()
pb.Image = b
End Sub
Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click
Dim buf As Byte()
Dim n As Integer
Console.WriteLine(strtohex(TextBox1.Text))
End Sub
End Class
如果有朋友有什么问题,可以评论区问,我知道的,会回复,不懂的,就没办法了。