VB.NET串口通信例子

前端之家收集整理的这篇文章主要介绍了VB.NET串口通信例子前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。


这是我3年前的一个例子,最近翻出来回忆一下。

串口是计算机上一种非常通用设备通信的协议。大多数计算机包含两个基于RS232的串口,现在配电脑好像只有一个。串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS-232口。同时,串口通信协议也可以用于获取远程采集设备的数据。串口通信在工控领域用途很广。

串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。

典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配:
a,波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
b,数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
d,奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。

串口通信还有一个参数是单工、半双工和双工。

如果在通信过程的任意时刻,信息只能由一方A传到另一方B,则称为单工。

如果在任意时刻,信息既可由A传到B,又能由B传A,但只能由一个方向上的传输存在,称为半双工传输。

如果在任意时刻,线路上存在A到B和B到A的双向信号传输,则称为全双工。

电话线就是二线全双工信道。 由于采用了回波抵消技术,双向的传输信号不致混淆不清。双工信道有时也将收、发信道分开,采用分离的线路或频带传输相反方向的信号,如回线传输。

在调试时可以用串口助手和windows的超级终端,不要带电插拔串口,容易烧毁。

在VB.NET中提供了IO.Ports类,是我们的编程变得很简单,下面是我的例子:

Form1.vb

  1. ImportsSystem.IO.Ports
  2. PublicClassForm1
  3. DimWithEventsRS232AsSerialPort
  4. DelegateSubSetTextCallback(ByValInputStringAsString)'声明一个代理
  5. PrivateSubForm1_Load(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesMyBase.Load
  6. ForEachspAsStringInSerialPort.GetPortNames()
  7. cmbCom.Items.Add(sp)
  8. Next
  9. cmbCom.Sorted=True
  10. cmbCom.SelectedIndex=0
  11. EndSub
  12. PrivateSubbtnStart_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesbtnStart.Click
  13. DimmBaudRateAsInteger
  14. DimmParityAsIO.Ports.Parity
  15. DimmDataBitAsInteger
  16. DimmStopBitAsIO.Ports.StopBits
  17. DimmPortNameAsString
  18. mPortName=cmbCom.SelectedItem.ToString
  19. mBaudRate=9600
  20. mParity=Parity.None
  21. mDataBit=8
  22. mStopBit=StopBits.One
  23. RS232=NewIO.Ports.SerialPort(mPortName,mBaudRate,mParity,mDataBit,mStopBit)
  24. IfNotRS232.IsOpenThen
  25. RS232.Open()
  26. btnSend.Enabled=True
  27. RS232.ReceivedBytesThreshold=1'设置引发事件的门限值
  28. Else
  29. MsgBox("通讯端口打开错误!",MsgBoxStyle.Critical)
  30. EndIf
  31. EndSub
  32. PrivateSubRS232_DataReceived(ByValsenderAsObject,ByValeAsSystem.IO.Ports.SerialDataReceivedEventArgs)HandlesRS232.DataReceived
  33. DimInByte()AsByte,ReadCountAsInteger,strReadAsString
  34. IfRS232.BytesToRead<=0ThenExitSub
  35. ReDimInByte(RS232.BytesToRead-1)
  36. ReadCount=RS232.Read(InByte,RS232.BytesToRead)
  37. strRead=""
  38. IfReadCount=0Then
  39. ExitSub
  40. Else
  41. ForEachbDataAsByteInInByte
  42. strRead+=bData.ToString&vbCrLf'若有数据则加到接收文本框
  43. DisplayText(strRead)
  44. Next
  45. EndIf
  46. EndSub
  47. '*************************************************
  48. '代理子程序
  49. '处理上述通信端口的接收事件
  50. '由于欲将数据显示到接收文本框中,因此必须检查
  51. '是否由另外得Thread所调用的,若是,则必须先
  52. '建立代理对象
  53. 'Invoke用于在拥有控件基础窗口控制代码的线程上
  54. '运行代理
  55. '*************************************************
  56. PrivateSubDisplayText(ByValcomDataAsString)
  57. '如果调用txtReceive的另外的线程,返回true
  58. IfMe.txtReceive.InvokerequiredThen
  59. '利用代理类型建立对象,并指定代理的函数
  60. DimdAsNewSetTextCallback(AddressOfDisplayText)
  61. Me.Invoke(d,NewObject(){comData})'以指定的自变量列表调用函数
  62. Else'相同的线程
  63. 'showstring(comData)'将收到的数据填入接收文本框中
  64. Me.txtReceive.Text+=comData
  65. EndIf
  66. EndSub
  67. PrivateSubbtnClose_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesbtnClose.Click
  68. IfRS232IsNothingOrElseNotRS232.IsOpenThen'尚未打开
  69. MsgBox("通讯端口尚未打开",MsgBoxStyle.CriticalOrMsgBoxStyle.OkCancel)
  70. Else
  71. RS232.Close()
  72. btnStart.Enabled=False
  73. btnClose.Enabled=False
  74. RS232=Nothing
  75. EndIf
  76. EndSub
  77. PrivateSubbtnEnd_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesbtnEnd.Click
  78. IfNotRS232IsNothingThen
  79. IfRS232.IsOpenThenRS232.Close()
  80. EndIf
  81. End
  82. EndSub
  83. PrivateSubbtnSend_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesbtnSend.Click
  84. DimbDataOut(0)AsByte
  85. Try
  86. bDataOut(0)=CType(Me.txtSend.Text,Byte)'将类型转换为字节
  87. RS232.Write(bDataOut,1)
  88. CatchexAsException
  89. MessageBox.Show("输入数值错误:"+ex.ToString,"错误通知:",MessageBoxButtons.OK,MessageBoxIcon.Exclamation)
  90. EndTry
  91. EndSub
  92. PrivateSubcmbCom_KeyPress(ByValsenderAsObject,ByValeAsSystem.Windows.Forms.KeyPressEventArgs)HandlescmbCom.KeyPress
  93. e.KeyChar=ChrW(0)'禁止用户在其中输入任何文字
  94. EndSub
  95. PrivateSubButton1_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesButton1.Click
  96. Me.Hide()
  97. Form2.Show()
  98. EndSub
  99. EndClass
Form2.vb
  1. '**************************************************************
  2. '时间:2008-11-08
  3. '作者:lincyang
  4. '实际应用中,串行通信的数据可能一次发送大量的数据,
  5. '发送之前就必须将数据先编码,
  6. '将其编成我们需要的字节数组数据,
  7. '才能将这些数据以字节的方式发送出去
  8. '目前操作系统使用的字符数据是Unicode:所有的字符均使用
  9. '两个字节来表示一个字符
  10. '**************************************************************
  11. ImportsSystem.IO.Ports
  12. ImportsSystem.Text
  13. PublicClassForm2
  14. DimRS232AsSerialPort
  15. PrivateSubForm2_Load(ByValsenderAsSystem.Object,mStopBit)
  16. IfNotRS232.IsOpenThen
  17. RS232.Open()
  18. btnSend.Enabled=True
  19. Timer1.Interval=100
  20. Timer1.Enabled=True
  21. Else
  22. MsgBox("通讯端口打开错误!",MsgBoxStyle.Critical)
  23. EndIf
  24. EndSub
  25. PrivateSubTimer1_Tick(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesTimer1.Tick
  26. DimInByte()AsByte,ReadCountAsInteger
  27. IfRS232.BytesToRead<=0ThenExitSub
  28. ReDimInByte(RS232.BytesToRead-1)
  29. ReadCount=RS232.Read(InByte,RS232.BytesToRead)
  30. IfReadCount=0Then
  31. ExitSub
  32. Else
  33. ForEachbDataAsByteInInByte
  34. Me.txtReceive.Text+=bData.ToString&vbCrLf'若有数据则加到接收文本框
  35. Next
  36. EndIf
  37. EndSub
  38. PrivateSubbtnClose_Click(ByValsenderAsSystem.Object,MsgBoxStyle.CriticalOrMsgBoxStyle.OkCancel)
  39. Else
  40. RS232.Close()
  41. btnStart.Enabled=False
  42. btnClose.Enabled=False
  43. Timer1.Enabled=False
  44. RS232=Nothing
  45. EndIf
  46. EndSub
  47. PrivateSubbtnSend_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesbtnSend.Click
  48. DimbDataOut()AsByte,BufAsString
  49. DimiSentCountAsInteger
  50. DimEncode1AsEncoding=Encoding.ASCII'声明编码对象,使用ASCII
  51. Try
  52. Buf=txtSend.Text.Trim()
  53. bDataOut=Encode1.GetBytes(Buf)'将字符串转换为字节数组
  54. iSentCount=bDataOut.GetLength(0)'发送总字节数
  55. '显示出总字节数
  56. lblSentCount.Text="总传输量:"&iSentCount.ToString&"字节"
  57. RS232.Write(bDataOut,iSentCount)
  58. CatchexAsException
  59. MessageBox.Show("输入数值错误:"+ex.ToString,"错误通知",MessageBoxIcon.Exclamation)
  60. EndTry
  61. EndSub
  62. PrivateSubbtnEnd_Click(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesbtnEnd.Click
  63. IfNotRS232IsNothingThen
  64. IfRS232.IsOpenThenRS232.Close()
  65. EndIf
  66. End
  67. EndSub
  68. PrivateSubcmbCom_KeyPress(ByValsenderAsObject,ByValeAsSystem.Windows.Forms.KeyPressEventArgs)HandlescmbCom.KeyPress
  69. e.KeyChar=ChrW(0)'禁止用户在其中输入任何文字
  70. EndSub
  71. EndClass


一不小心看到一高手写的C#串中操作系列的文章,很不错,可惜我辈不懂C#,于是顺便改成用VB.NET2010

其中几乎都有注解。

顺便说明,再发送16进制不是很完善,有懂的兄弟,说明一下

001. Imports System
@H_403_2377@ 002. Imports System.Collections.Generic
003. Imports System.ComponentModel
@H_403_2377@ 004. Imports System.Data
005. Imports System.Drawing
@H_403_2377@ 006. Imports System.Linq
007. Imports System.Text
@H_403_2377@ 008. Imports System.IO.Ports
009. Imports System.Text.RegularExpressions
@H_403_2377@ 010.
011.
@H_403_2377@ 012. Public Class Form1
013.
@H_403_2377@ 014. WithEvents Comm As SerialPort = New SerialPort
015. Private Builder As StringBuilder = New StringBuilder '避免在事件处理方法中反复的创建,所以定义到外面
@H_403_2377@ 016. Private ReceiveCount As Long = 0 '接收计数
017. Private SendCount As Long = 0 '发送计数
@H_403_2377@ 018.
019. Private Listening As Boolean = False '是否没有执行完invoke相关操作
@H_403_2377@ 020. Private Closingg As Boolean = False '是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke
021.
@H_403_2377@ 022. Public Delegate Sub UpdateData(ByVal mByte() As Byte)
023.
@H_403_2377@ 024. Public Sub ShowData(ByVal mByte() As Byte)
025. Console.WriteLine(mByte)
@H_403_2377@ 026. ReceiveCount += mByte.Length
027. Builder.Clear()
@H_403_2377@ 028.
029. If CheckBoxHex.Checked Then
@H_403_2377@ 030. For Each b As Byte In mByte
031. Builder.Append(b.ToString("X2") + " ")
@H_403_2377@ 032. Next
033.
@H_403_2377@ 034. Else
035.
@H_403_2377@ 036. Builder.Append(Encoding.ASCII.GetString(mByte))
037.
@H_403_2377@ 038. End If
039. TxtGet.AppendText(Builder.ToString)
@H_403_2377@ 040. labelGetCount.Text = "Get:" + ReceiveCount.ToString
041. End Sub
@H_403_2377@ 042.
043. Private Sub Form1_Load(sender As System.Object,e As System.EventArgs) Handles MyBase.Load
@H_403_2377@ 044.
045. '初始化下拉串口名称列表框
@H_403_2377@ 046. Dim Ports() As String = SerialPort.GetPortNames
047. Array.Sort(Ports)
@H_403_2377@ 048. ComboPortName.Items.AddRange(Ports)
049. ComboPortName.SelectedIndex = IIf(ComboPortName.Items.Count > 0,-1)
@H_403_2377@ 050. ComboBaudrate.SelectedIndex = ComboBaudrate.Items.IndexOf("9600")
051. '初始化Serialport对象
@H_403_2377@ 052. Comm.NewLine = vbCrLf
053. Comm.RtsEnable = True
@H_403_2377@ 054.
055. 'AddHandler Obj.Ev_Event,AddressOf EventHandler
@H_403_2377@ 056. 'RemoveHandler Obj.Ev_Event,AddressOf EventHandler
057. 'AddHandler Comm.DataReceived,AddressOf Comm_DataReceived
@H_403_2377@ 058.
059. End Sub
@H_403_2377@ 060.
061. Private Sub Comm_DataReceived(sender As Object,e As System.IO.Ports.SerialDataReceivedEventArgs) Handles Comm.DataReceived
@H_403_2377@ 062. If Closingg Then Return '如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环
063.
@H_403_2377@ 064. Try
065. Listening = True '设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
@H_403_2377@ 066. Dim n As Long = Comm.BytesToRead '先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
067. Dim Buf(n - 1) As Byte '声明一个临时数组存储当前来的串口数据
@H_403_2377@ 068.
069. Comm.Read(Buf,n) '读取缓冲数据
@H_403_2377@ 070. Builder.Clear() '清除字符串构造器的内容
071.
@H_403_2377@ 072. Dim b As UpdateData = New UpdateData(AddressOf ShowData)
073. Me.BeginInvoke(b,Buf)
@H_403_2377@ 074.
075. Catch ex As Exception
@H_403_2377@ 076. Err.Clear()
077. Finally
@H_403_2377@ 078. Listening = False '我用完了,ui可以关闭串口了。
079. End Try
@H_403_2377@ 080. End Sub
081.
@H_403_2377@ 082. Private Sub ShowMsg(ByVal buffer() As Byte)
083. If CheckBoxHex.Checked Then
@H_403_2377@ 084. For Each b As Byte In Buffer
085. Builder.Append(b.ToString("X2") + " ")
@H_403_2377@ 086. Next
087. Else
@H_403_2377@ 088. Builder.Append(Encoding.ASCII.GetString(buffer))
089. End If
@H_403_2377@ 090. Me.TxtGet.AppendText(Builder.ToString())
091. labelGetCount.Text = "Get:" + ReceiveCount.ToString
@H_403_2377@ 092. End Sub
093.
@H_403_2377@ 094. Private Sub BtnXOpen_Click(sender As System.Object,e As System.EventArgs) Handles BtnXOpen.Click
095. '根据当前串口对象,来判断操作
@H_403_2377@ 096. If Comm.IsOpen Then
097. Closingg = True '
@H_403_2377@ 098. While Listening
099. Application.DoEvents()
@H_403_2377@ 100. End While
101. '打开时点击,则关闭串口
@H_403_2377@ 102. Comm.Close()
103. Closingg = False
@H_403_2377@ 104. Else
105. Comm.PortName = ComboPortName.Text
@H_403_2377@ 106. Comm.BaudRate = Integer.Parse(ComboBaudrate.Text)
107. Try
@H_403_2377@ 108. Comm.Open()
109. Catch ex As Exception
@H_403_2377@ 110. '捕获到异常信息,创建一个新的comm对象,之前的不能用了。
111. Comm = New SerialPort
@H_403_2377@ 112. '现实异常信息给客户。
113. MessageBox.Show(ex.Message)
@H_403_2377@ 114. End Try
115. End If
@H_403_2377@ 116.
117. '设置按钮的状态
@H_403_2377@ 118. BtnXOpen.Text = IIf(Comm.IsOpen,"Close","Open")
119. BtnXOpen.Enabled = Comm.IsOpen
@H_403_2377@ 120.
121. End Sub
@H_403_2377@ 122.
123. '动态的修改获取文本框是否支持自动换行。
@H_403_2377@ 124. Private Sub CheckBoxNewLineGet_CheckedChanged(sender As System.Object,e As System.EventArgs) Handles CheckBoxNewLineGet.CheckedChanged
125. TxtGet.WordWrap = CheckBoxNewLineGet.Checked
@H_403_2377@ 126. End Sub
127.
@H_403_2377@ 128. Private Sub BtnXSend_Click(sender As System.Object,e As System.EventArgs) Handles BtnXSend.Click
129. Dim n As Integer = 0 '定义一个变量,记录发送了几个字节
@H_403_2377@ 130. If checkBoxHexSend.Checked Then '16进制发送
131. '我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数
@H_403_2377@ 132. Dim Mc As MatchCollection = Regex.Matches(TxtSend.Text.Trim,"(?i)[/da-f]{2}") '"(?i)[/da-f]{2}"
133. Dim buf As List(Of Byte) = New List(Of Byte)
@H_403_2377@ 134.
135. '依次添加到列表中
@H_403_2377@ 136. For Each m As Match In Mc
137. ' buf.Add(Byte.Parse(m.Value))
@H_403_2377@ 138. buf.Add(Byte.Parse(m.Value,System.Globalization.NumberStyles.HexNumber))
139. Next
@H_403_2377@ 140.
141. '转换列表为数组后发送
@H_403_2377@ 142. Comm.Write(buf.ToArray,buf.Count)
143. n = buf.Count
@H_403_2377@ 144. Else 'ascii编码直接发送
145. '包含换行符
@H_403_2377@ 146. If checkBoxNewlineSend.Checked Then
147. Comm.WriteLine(TxtSend.Text)
@H_403_2377@ 148. n = TxtSend.Text.Length + 2
149. Else
@H_403_2377@ 150. Comm.Write(TxtSend.Text)
151. n = TxtSend.Text.Length
@H_403_2377@ 152. End If
153. End If
@H_403_2377@ 154.
155. SendCount += n '累加发送字节数
@H_403_2377@ 156. labelSendCount.Text = "Send:" + SendCount.ToString
157. End Sub
@H_403_2377@ 158.
159. Private Sub BtnXReset_Click(sender As System.Object,e As System.EventArgs) Handles BtnXReset.Click
@H_403_2377@ 160.
161. '复位接受和发送的字节数计数器并更新界面。
@H_403_2377@ 162. SendCount = 0
163. ReceiveCount = 0
@H_403_2377@ 164. labelGetCount.Text = "Get:0"
165. labelSendCount.Text = "Send:0"
@H_403_2377@ 166. Builder.Clear()
167.
@H_403_2377@ 168. End Sub
169.
@H_403_2377@ 170. Private Sub BtxClear_Click(sender As System.Object,e As System.EventArgs) Handles BtxClear.Click
171. TxtGet.Text = ""
@H_403_2377@ 172. Builder.Clear()
173. End Sub
@H_403_2377@ 174. End Class


猜你在找的VB相关文章