1 引言
计算机与通信的紧密结合极大地推动了工业自动化的进程。人们坐在计算机前就可以实现对远端设备的集中监控,从而提高了整个系统的稳定性和可靠性。本文作者曾经参与了电源远程监控系统软件的开发工作。在运用VB开发监控软件的过程中,为实现远程通讯功能,我们利用了VB提供的通讯控件MSComm。该控件屏蔽了通信过程中的底层操作,程序员只需设置和监视MSComm控件的属性和事件,就可以轻而易举地实现串行异步通讯。 远程监控中心可以对分布的各个独立的电源系统进行遥测、遥控。电源控制器可以现场监测和控制电源系统的各项工作参数;可以接受远端监控中心的查询、控制;并可以向中心上报工作状态。为实现上述功能 ,首要任务是建立两者之间的通讯。因此可分别将监控中心计算机和电源控制器通过RS-232C接口与Modem相连,再通过Modem接入公用电话网(PSTN),由PSTN实现本地Modem与电源端Modem的联接,就可以实现监控中心计算机与电源控制器间的通讯。如图1所示。
图1
2 通讯控件简介
MSComm 控件提供了一系列标准通讯属性和方法。使用它可以建立起应用程序与串行端口的联接。为实现远程通讯,还需要把调制解调器与串行端口通过RS-232C接口连接,这样,在应用程序中发出AT命令,就可以达到控制调制解调器的目的,进行远程通讯。有关调制解调器的AT命令,本文不做讨论,读者可以参考相关的调制解调器手册。 2.1 在项目中加入MSComm控件 启动Visual Basic,建立一个新项目。 选Project菜单,从中选择Components子菜单。 在弹出的Components对话框里的Controls标签下选择Microsoft Comm Control 5.0项。 2.2 与程序设计有关的 MSComm控件属性 CommPort 设置或返回通讯端口号。格式为:MSComm.CommPort[ = PortNumber ]。 在设计时,PortNumber 可以设置成从 1 到 16 的任何数(缺省值为 1)。但是如果用 PortOpen 属性打开一个并不存在的端口时,就会产生错误 。还需注意的是必须在打开端口之前设置 CommPort 属性。例如:MSComm.CommPort=2,即设置当前通信串口为COM2口。 Settings 以字符串形式设置或返回波特率、奇偶校验、数据位和停止位。格式为: MSComm.Settings[=ParaString]。ParaString是一个包含四部分的字符串。第一部分为波特率,其可选值为110,300,600,1200,2400,4800,9600,14400,19200,28800。第二部分为奇偶校验,N 表示不校验,E 表示偶校验,O表示奇校验,S表示空格检验,M表示符号校验。第三部分为数据位位数,其可选值为4,5,6,7,8。第四部分为停止位位数,其可选值为1,1.5,2 。Settings属性的缺省值为“9600,N,8,1” 。 PortOpen 设置并返回通讯端口的状态(打开或关闭)。在设计时无效。格式为: MSComm.PortOpen[=TRUE/FALSE] 。 Input 返回并删除接收缓冲区中的数据流。该属性在设计时无效,在运行时为只读。 格式为:MSComm.Input。 InputLen 设置并返回 Input 属性从接收缓冲区中每次读取的字符数。格式为:MSComm.InputLen[=CharNumber]。InputLen 属性的缺省值是 0。设置 InputLen 为 0 时,使用 Input 将使 MSComm 控件读取接收缓冲区中全部的内容。若接收缓冲区中的字符数小于InputLen 属性设置的字符数,Input 属性返回一个零长度字符串 ("")。所以在使用Input 前,用户可以通过检查 InBufferCount 属性来确定缓冲区中是否已有需要数目的字符。 InBufferCount 返回接收缓冲区中已接收的字符数。格式为: MSComm.InBufferCount[=0]。该属性在设计时无效,但设置InBufferCount 属性为0 可以清除接收缓冲区。 InputMode 设置或返回 Input 属性取回的数据的类型。格式为MSComm.InputMode[=ModeValue]。若数据只用 ANSI 字符集,设置InputMode属性值为0(缺省),数据通过 Input 属性以文本形式取回。如数据中有嵌入控制字符、Nulls 等等,可设置InputMode属性值为1,数据通过 Input 属性以二进制形式取回。 Output 向发送缓冲区写数据。该属性在设计时无效,在运行时为只读。格式为:MSComm.Output=OutData。OutData为要发送的数据,可以是文本数据或二进制数据。 Rthreshold 设置或返回输入缓冲区中存放接收字符的最小数。当其属性值为1时,则缓冲区中每接收到一个字符就引发一次OnComm事件,以便及时从缓冲区中取走数据;当设为0时,则不引发OnComm事件;当设为其它值时,如Rthreshold属性值为5时,则缓冲区中每接收到5个字符引发一次OnComm事件。 CommEvent 返回最近的通讯事件或错误的数字代码。格式为:Mscomm.CommEvent。 当CommEvent属性值为常数ComEvReceive=2时,收到 Rthreshold 个字符,就会触发OnComm事件,直到用 Input 属性从接收缓冲区中取出数据。 DTREnable 确定在通讯时是否使 Data Terminal Ready (DTR) 线有效。Data Terminal Ready 是计算机发送到调制解调器的信号,指示计算机准备就绪,可以开始传输数据。格式为:MSComm.DTREnable[=TRUE/FALSE]。 DTREnable 设置为 True,当端口被打开时 Data Terminal Ready 线设置为高电平(开),当端口被关闭时 Data Terminal Ready 线设置为低电平(关)。 DTREnable 设置为 False,Data Terminal Ready 线始终保持为低电平。在很多情况下,当Data Terminal Ready 线发生从开到关的转换时,调制解调器进入命令状态,执行“ATH0”命令来挂断电话。 2.3 MSComm控件的OnComm 事件 通讯控件产生的唯一事件是OnComm事件。每当有通讯错误或某事件发生时,通讯控件就会产生此事件。事件或错误的数字代码放在CommEvent属性中。
3 程序设计
MSComm 控件提供下列两种处理通讯的方式:查询方式和事件驱动方式。 3.1 查询通讯 通过检查InBufferCount 属性值来判定输入缓冲区中是否接收到相应数目的字符或字节。若已接收到相应数目的字符或字节,就可以用Input属性来接收这些字符或字节;否则继续查询InBufferCount属性值,直到满足条件。 下面是用查询方式实现的通讯程序。在下面给出的例子中,我们接收的数据都是48字节的定长二进制数据。为实现该程序,需在窗体上加入一个通讯控件MSComm1,四个命令按钮cmdDial,cmdHangUp,cmdOrder,cmdExit。 窗体文件清单(只给出主要的程序代码) Dim ret 定义窗体级变量 Dim databuffer() As Byte databuffer是存放接收数据的数组 Private Sub Form—Load() 设置当前通信串口为COM2口 MSComm1.CommPort = 2 设置串口传输速率为2400bps,数据位8位,无校验,一位停止位 MSComm1.Settings = "2400,n,8,1" MSComm1.InputMode = 1 以二进制形式从输入缓冲区中读数据 MSComm1.RThreshold = 0 接收到数据不引发OnComm事件 MSComm1.InputLen = 0 一次读出输入缓冲区中的所有数据 MSComm1.PortOpen = True 打开串口 End Sub Private Sub cmdDial—Click() 拨号联机 Static Num As String Num = InputBox$("Enter Phone Number:", "Dial Number", Num) 输入电话号码 If Num = "" Then Exit Sub If Not MSComm1.PortOpen Then MSComm1.PortOpen = True If Err Then Exit Sub End If MSComm1.Output = "ATDT" & Num & vbCrLf ATDT通知调制解调器以音频方式拨号 cmdHangUp.Enabled = True cmdDial.Enabled = False MSComm1.InBufferCount = 0 查询InBufferCount 属性值来确定输入缓冲区中是否接收到调制解调器回送的字符串“10 ? ”,以证实拨号成功,正确建立联接 Do DoEvents Loop Until MSComm1.InBufferCount =3 End Sub Sub ReceiveData() 接收数据子程序 Dim temp MSComm1.InBufferCount = 0 清空输入缓冲区 查询InBufferCount 属性值来确定输入缓冲区中是否有接收到48字节的数据 Do DoEvents Loop Until MSComm1.BufferCount = 48 temp = MSComm1.Input databuffer = temp 将接收到的数据的类型转换为字节数组类型 End Sub Private Sub cmdHangUp—Click() 数据通讯结束后,执行挂机操作 Dim ret ret = MSComm1.DTREnable 当DTR信号发生从高电平到低电平的转换时,调制解调器从传输状态进入命令状态 MSComm1.DTREnable = True MSComm1.DTREnable = False MSComm1.DTREnable = ret MSComm1.Output = "ATH0" ATH0是挂机命令 cmdHangUp.Enabled = False cmdDial.Enabled = True If Err Then MsgBox Error$, 48 End Sub Private Sub cmdExit—Click() 单击退出按钮触发的事件过程 Mscomm1.Portopen=False 关闭串口 End End Sub Private Sub cmdOrder—Click() 单击查询按钮触发的事件过程 Dim temp Dim order() As Byte order = StrConv(Chr(0) + "aa" + Chr(13), vbFromUnicode) 查询命令串 strconv()函数的功能是将Unicode字符串转换为ASCII码字符串 temp = order MSComm1.Output = temp 通过与串口联接的调制解调器向远端电源控制器发送查询命令 ReceiveData 查询接收48字节的数据 End Sub
3.2 事件驱动通讯 事件驱动通讯是处理串行端口交互作用的一种非常有效的方法。在许多情况下,当输入缓冲区中收到字符或是输出缓冲区空时,需要通知程序以便处理,在这些情况下,可以利用 MSComm 控件的 OnComm 事件捕获并处理这些通讯事件。 下文给出的用事件驱动方法设计的通讯程序与上面的查询通讯程序具有相同的窗口界面和功能。在实现过程中,要把查询通讯程序的查询接收子程序去掉,这是因为在事件驱动的通讯中,接收数据都是在OnComm事件过程里完成的。并且,在Form—load事件过程中将Rthreshold属性值设为1。在程序设计时,还要引入一个窗体级变量comstate,指示串口当前的状态。下面只给出OnComm 事件过程。 Private Sub MSComm1—OnComm() Dim temp As Variant Dim tempbyte() As Byte Select Case MSComm1.CommEvent 根据不同的CommEvent属性的值来确定引起OnComm事件的具体原因,然后进行不同的处理 Case 2 MSCOMM—EV—RECEIVE=2 temp = MSComm1.Input 从缓冲区中读出接收到的数据 tempbyte= temp 将接收到的数据的类型转换为字节数组类型 Dim k As Integer Static i As Integer For k = LBound(tempbyte) To UBound(tempbyte) If comstate=0 Then comstate=0, 串口处在拨号状态 If tempbyte(k)=13 Then 判断是否收到调制解调器回送的结果码结束符回车符 comstate=1 拨号成功 End If Else comstate=1,串口处在接收数据状态 databuffer(i)=tempbyte(k) i=i+1 If i=47 Then i=0 End If Next k End Select End Sub 以上,我们讨论了用VB实现远程数据通讯的两种方法。需要注意的是,用查询方式进行通讯程序设计时,要对输入缓冲区中的数据及时处理,保证数据的正确接收。例如,在输出调制解调器拨号命令后,程序就立即查询调制解调器的回送结果码;而向电源控制器发出查询命令后,程序就立即查询接收上报的数据。如果在数据量大,功能比较复杂的通讯程序中,就应该采用事件驱动的通讯方法,保证通讯的可靠性。
|