1.2 串行通信控件MSComm
MSComm控件全称为Microsoft Communications Control,是Microsoft公司提供的简化Windows下串行通信编程的ActiveX控件,它既可以用来提供简单的串行端口通信功能,也可以用来创建功能完备的、事件驱动的高级通信工具。
MSComm控件在串口编程时非常方便,程序员不必花时间去了解较为复杂的API函数,而且在VC++、VB、Delphi等语言中均可使用。
MSComm控件提供了一系列标准通信命令的使用接口,使用它可以建立与串行端口的连接,通过串行端口连接到其他通信设备(例如调制解调器),发出命令、交换数据,以及监视和响应串行连接中发生的事件和错误。利用它可以实现诸如拨打电话、监视串行端口的输入数据,乃至创建功能完备的终端程序等。
1.2.1 MSComm控件处理通信的方式
MSComm控件通过串行端口传输和接收数据,为应用程序提供串行通信功能。它提供下列两种处理通信的方式。
1.事件驱动方式
该方式相当于一般程序设计中的中断方式。当串口发生事件或错误时,MSComm控件会产生OnComm事件,用户程序可以捕获该事件进行相应处理。它是处理串行端口交互作用的一种非常有效的方法。在许多情况下,事件发生时程序会希望得到通知,例如,在串口接收缓冲区中有一个字符到达或一个变化发生时,程序都可以利用MSComm控件的OnComm事件捕获并处理这些通信事件;OnComm事件还可以检查和处理通信错误。在程序的每个关键功能之后,可以通过检查CommEvent属性的值来查询事件和错误。
在程序设计中,可以在OnComm事件处理函数中加入自己的处理代码,一旦事件发生即可自动执行该段程序。这种方法的优点是程序响应及时,可靠性高。
2.查询方式
在程序的每个关键功能之后,在用户程序中设计定时或不定时的查询,通过检查CommEvent属性的值来查询事件和错误,从而做出相应的处理。在进行简单应用程序设计时可采用这种方法。例如,如果写一个简单的电话拨号程序,则没有必要对每接收一个字符都产生事件,因为唯一等待接收的字符是调制解调器的“OK”响应。
不过,MSComm控件通信功能的实现,还是间接调用Windows API编程的结果,只是先通过Comm.drv解释,然后再传递给设备驱动程序进行的。
由于MSComm控件本身没有提供方法,所以CMSComm类除了Creat()成员函数外,其他的函数都是Get/Set函数对,用来获取或设置控件的属性。与CserialPort类一样,这些函数对就是用API函数编写的。
MSComm控件也只有1个OnComm事件,用来向调用者通知有通信事件发生。
1.2.2 MSComm控件的使用
1.MSComm控件的添加
在应用程序中插入MSComm控件后就可以较为方便地实现计算机串口收发数据。要使用ActiveX控件MSComm,程序员必须将其添加入工程,方法如下:
(1)单击主菜单Project的子菜单Add To project下的Components and Controls选项。
(2)在弹出的“Components and Controls Gallery”对话框中选择Registered ActiveX Controls文件夹中的“Microsoft Communications Control,version 6.0”选项,如图1-3所示。
图1-3 选择Microsoft Communications Control
(3)单击其中的“Insert”按钮,MSComm控件就被添加到工程中了。与此同时,类CMSComm的相关文件mscomm.h和mscomm.cpp也一并被加入Project的Header Files和Source Files中,如图1-4所示。当然,程序员可以自己修改文件名。
图1-4 添加MSComm控件
(4)单击“OK”按钮,在控件面板上就出现了一个形似电话的控件,如图1-5所示,使用时,将其拖到界面上即可。
图1-5 控件面板
2.MSComm控件的通信步骤
通常以下面的步骤来使用VC++的MSComm控件进行串口编程:
(1)在建立的程序工程中插入Microsoft Communication Control控件。
(2)添加MSComm控件ID的控制变量(或者对象)。
(3)对串口进行初始化,设置MSComm控件的属性。
(4)打开串行端口,打开通信资源。
(5)编写串口发送等其他代码;将要发送的信息写入串口。
(6)添加串口事件的消息处理函数OnComm()函数,在函数中根据应用需要,编写数据处理代码。
(7)读取串行端口信息,当串口发生EV_RXCHAR(接收到字符并放入了输入缓冲区)消息后读取串口,数据传输错误处理,字符串处理如回车符、空格并相应转化成数据等。
(8)使用完MSComm通信对象后,将通信端口关闭,即关闭事件,清除通信事件,丢弃通信资源并关闭。
遵循以上步骤,可以建构自己的串行通信传输系统。
1.2.3 MSComm控件的常用属性
MSComm控件的属性很多,这里介绍串口编程中经常用到的几个重要属性。
1.CommPort属性
功能:设置或返回通信端口号。
语法:void CMSComm::SetCommPort(short nNew Value) //设置串口号
short CMSComm::GetCommPort() //查询当前串口号
说明:nNew Value可以设置为1 到16 之间的任何整数值(默认值为1),表示串口COM1、COM2...。如果用PortOpen属性打开一个并不存在的端口时,MSComm控件会产生错误68(设备无效)。
注意:必须在打开端口之前设置CommPort属性。
2.Settings属性
功能:设置并返回波特率、奇偶校验、数据位、停止位参数。
语法:void CMSComm::SetSettings(LPCTSTR lpszNew Value)
Cstring CMSComm::GetSettings()
Value由4个设置值组成,有如下的格式:“BBBB,p,D,S”,BBBB为波特率,p为奇偶校验,D为数据位数,S为停止位数。Value的默认值是:“9600,N,8,1”。
说明:奇偶校验设置为了进行数据校验,通常是不用的,并设置为“N”。数据位数指定了代表一个数据块的比特数。停止位指出了何时接收到一个完整数据块。
当端口打开时,如果设置值lpszNew Value非法,则MSComm控件产生错误380(非法属性值)。
3.OutPut属性
功能:向发送缓冲区写数据,或返回发送缓冲区当前的数据。
语法:void CMSComm::SetOutput(const VARIANT & newValue)
VARIANT CMSComm::GetOutput()
说明:变量类型为VARIANT,该属性在串口没有打开时不可用。Output可以发送文本或二进制数据,当发送文本类型数据时,将字符型数据放入VARIANT型变量中;发送二进制数据(即按字节发送)时,将字节型数据放入VARIANT型变量中。若数据中包含了内嵌控制字符、空字符等,必须将其作为二进制数据发送。
4.PortOpen属性
功能:用于打开或关闭串口,或者返回串口的开、关状态。
语法:void CMSComm::SetPortOpen(BOOL bNew Value)
BOOL CMSComm::GetPortOpen()
说明:bNew Value值设置为TRUE,则打开串口;bNew Value值设置为FALSE,则关闭串口。编程时可以在程序中打开或关闭串口,当程序终止,MSComm控件自动关闭串口。
在打开端口之前,确定CommPort属性设置为一个合法的端口。如果CommPort属性设置为一个非法的端口,则打开该端口时,MSComm控件产生错误68(设备无效)。
串行端口设备必须支持Settings属性当前的设置值。如果Settings属性包含硬件不支持的通信设置值,硬件可能不会正常工作。
5.InputMode属性
功能:设置或返回传输数据的类型。
语法:void CMSComm::SetInputMode(long nNew Value)
long CMSComm::GetlnputMode()
InputMode属性的Value值可以设置为如下常数:
① 0——通过Input属性以文本方式取回传入的数据。
② 1——通过Input属性以二进制方式取回传入的数据。
6.Input属性
功能:从接收缓冲区内读出数据。
语法:VARIANT CMSComm::GetInput()
说明:返回数据类型为VARIANT型变量,该属性在串口没有打开时不能用,在运行时是只读的。
当InputMode属性值为0时(检取数据为文本方式),变量中含String型数据。
当InputMode属性值为1时(检取数据为二进制方式),变量中含Byte数组型数据。
7.InputLen属性
功能:设置并返回Input属性从接收缓冲区读取的字符数。
语法:void CMSComm::SetInputLen(short nNew Value)
short CMSComm::GetlnputLen()
说明:InputLen属性的默认值是0。设置InputLen为0时,使用Input将使MSComm控件读取接收缓冲区中全部的内容。
若接收缓冲区中InputLen字符无效,Input属性返回一个零长度字符串(“”)。在使用Input前,用户可以选择检查InBufferCount属性来确定缓冲区中是否已有需要数目的字符。
该属性在从输出格式为定长数据的机器读取数据时非常有用。
8.InBufferSize属性
功能:设置或返回输入缓冲区的大小。
语法:void CMSComm::SetInBufferSize(short nNew Value)
short CMSComm::GetInBufferSize()
说明:设置值的默认大小为1024字节(B)。
9.InBufferCount属性
功能:设置或返回输入缓冲区内等待读取的字节个数。
语法:void CMSComm::SetInBufferCount(short nNew Value)
short CMSComm::GetInBufferCount()
说明:当设置InBufferCount属性的值为0时,可以清除空接收缓冲区。
10.OutBufferSize属性
功能:设置或者返回发送缓冲区的大小。
语法:void CMSComm::SetOutBufferSize(short nNew Value)
short CMSComm::GetOutBufferSize()
说明:设置值为字节数,默认值为512字节。此值不能太小,否则缓冲区易溢出,但太大则会不必要地占用内存。
11.OutBufferCount属性
功能:返回发送缓冲区的字节数,或者清空发送缓冲区。
语法:void CMSComm::SetOutBufferCount(short nNew Value)
short CMSComm::GetOutBufferCount()
说明:设置值为0时清空发送缓冲区。
12.RThreshold属性
功能:在MSComm控件设置CommEvent属性为comEvReceive并产生OnComm之前设置并返回的要接收的字符数。
语法:void CMSComm::SetRThreshold(short nNew Value)
short CMSComm::GetRThreshold()
说明:接收缓冲区收到nNew Value个字符产生OnComm事件。当接收字符后,若nNew Value设置为0(默认值),则不产生OnComm事件。例如,设置nNewValue为1,则接收缓冲区收到每一个字符都会使MSComm控件产生OnComm事件。
13.SThreshold属性
功能:MSComm控件设置CommEvent属性为comEvSend并产生OnComm事件之前设置并返回传输缓冲区中允许的最小字符数。
语法:void CMSComm::SetSThreshold(short nNew Value)
short CMSComm::GetSThreshold()
nNew Value整形表达式,代表在OnComm事件产生之前在传输缓冲区中的最小字符数。
说明:Sthreshold属性为0(默认值),数据传输事件不会产生OnComm事件。若设置SThreshold属性为1,当传输缓冲区完全空时,MSComm控件产生OnComm事件。如果在传输缓冲区中的字符数小于value,则CommEvent属性设置为comEvSend,并产生OnComm事件。ComEvSend事件仅当字符数与SThreshold交叉时被激活一次。例如,如果SThreshold等于5,仅当在输出队列中字符数从5降到4时,comEvSend才发生。如果在输出队列中从没有比SThreshold多的字符,comEvSend事件将绝不会发生。
14.Handshaking属性
功能:设置或返回硬件握手状态,即设定串口通信设备之间的流控制。
语法:void CMSComm::SetHandshaking(long nNew Value)
long CMSComm::GetHandshaking()
说明:属性nNew Value值可设定为下面的值:0(无握手,为默认值);1(Xon/Xoff握手);2(Request-to-send/clear-to-send握手);3(Request-to-send和clear-to-send握手皆可)。
Handshaking是指内部通信协议,通过该协议,数据从硬件端口传输到接收缓冲区。当一个数据字符到达串行端口,通信设备就把它移到接收缓冲区,以使程序可以读它。如果没有接受缓冲区,则程序需要直接从硬件读取每一个字符,这很可能会造成数据丢失,因为字符到达的速度可以非常快。
握手协议保证在缓冲区过载时数据不会丢失,所谓缓冲区过载,是指数据到达端口太快而使通信设备来不及将它移到接收缓冲区。
15.CTSHolding属性
功能:确定是否可通过查询Clear To Send(CTS)线的状态发送数据。Clear To Send是调制解调器发送到相连计算机的信号,指示传输可以进行。该属性在设计时无效,在运行时为只读。
语法:void CMSComm::SetCTSHolding(BOOLbNewValue)
BOOLCMSComm::GetCTSHolding()
Mscomm控件的CTSHolding属性设置值:
① TRUE:Clear To Send线为高电平。
② FALSE:Clear To Send线为低电平。
说明:如果Clear To Send线为低电平(CTSHolding=False)并且超时时,MSComm控件设置CommEvent属性为comEventCTSTO(Clear To Send Timeout),并产生OnComm事件。
Clear To Send线用于RTS/CTS(Request To Send/Clear To Send)硬件握手。如果需要确定Clear To Send线的状态,则CTSHolding属性给出一种手工查询的方法。
16.CDHolding属性
功能:通过查询CarrierDetect(CD)线的状态确定当前是否有传输。
语法:void CMSComm::SetCDHolding(BOOL bNew Value)
BOOL CMSComm::GetCDHolding()
bNew Value为TRUE时表示True Carrier Detect线为高电平,为FALSE时表示True Carrier Detect线为低电平。
说明:CarderDetect是从调制解调器发送到相连计算机的一个信号,指示调制解调器正在联机。该属性在设计时无效,在运行时为只读。
17.DSRHolding属性
功能:确定Data Set Ready(DSR)线的状态。
语法:void CMSComm::SetDTREnable(BOOL bNew Value)
BOOL CMSComm::GetDTREnable()
DSRHolding属性返回以下值:
① 值为TRUE表示Data Set Ready线为高电平。
② 值为FALSE表示Data Set Ready线为低电平。
说明:当Data Set Ready线为高电平(DSRHolding=True)且超时时,MSComm控件设置CommEvent属性为comEventDSRTO(数据准备超时),并产生OnComm事件。当为Data Terminal Equipment(DTE)机器写Data Set Ready/DataTerminal Ready握手例程时,该属性是十分有用的。
Data Set Ready信号由调制解调器发送到相连计算机,指示做好操作准备。该属性在设计时无效,在运行时为只读。
18.EOFEnable属性
功能:确定在输入过程中MSComm控件是否寻找文件结尾(EOF)字符。如果找到EOF字符,将停止输入并激活OnComm事件,此时CommEvent属性设置为ComEvEOF。
语法:void CMSComm::SetEOFEnable(BOOL bNew Value)
BOOL CMSComm::GetEOFEnable()
说明:EOFEnable属性语法包括下列部分:bNew Value布尔表达式,确定当找到EOF字符时,OnComm事件是否被激活,如“设置值”中所描述。
bNewValue的设置值如下:
① TRUE:EOF字符找到时,OnComm事件被激活。
② FALSE(默认):当EOF字符找到时,OnComm事件不被激活。OnComm控件将不在输入流中寻找EOF字符。
19.DTREnable属性
功能:设置或返回Data Terminal Ready(DTR)线状态。确定在通信时是否使Data Terminal Ready线有效。Data Terminal Ready是计算机发送到调制解调器的信号,指示计算机在等待接受传输。
语法:void CMSComm::SetDTREnable(BOOL bNew Value)
BOOL CMSComm::GetDTREnable()
bNewValue的设置值如下:
① True:使Data Terminal Ready线有效。
② False默认:使DataTerminalReady线无效(默认)。
说明:DTREnable设置为True,当端口被打开时,Data Terminal Ready线设置为高电平(开),当端口被关闭时,Data Terminal Ready线设置为低电平(关)。当DTREnable设置为False,Data Terminal Ready线始终保持为低电平。
20.RTSEnalbe属性
功能:确定是否使Request To Send(RTS)线有效。一般情况下,由计算机发送Request To Send信号到连接的调制解调器,以请示允许发送数据。
语法:void CMSComm::SetRTSEnable(BOOL bNew Value)
BOOL CMSComm::GetRTSEnable()
bNewValue设置值为:
① True:使Request To Send线有效。
② False:使Request To Send线无效。
说明:当RTSEnable设置为True,端口打开时,Request To Send线设置为高电平,端口关闭时,设置为低电平。
Request To Send线用在RTS/CTS硬件握手。RTSEnable属性允许手动检测Request To Send线以确定其状态。
有关握手协议详细信息,请参阅Handshaking属性。
21.Break属性
功能:设置或清除中断信号的状态。该属性在设计时无效。
语法:void CMSComm::SetBreak(BOOL bNew Value)
BOOL CMSComm::GetBreak()
BNew Value的设置值为:
① True:设置中断信号状态。
② False:清除中断信号状态。
说明:当设置为True,Break属性发送一个中断信号。该中断信号挂起字符传输,并置传输线为中断状态直到把Break属性设置为False。一般地,仅当使用的通信设备要求设置一个中断信号时,才设置一个短时的中断状态。
22.CommEvent属性
功能:设置或返回无论何时当CommEvent属性的值变化时,就产生OnComm事件,标志发生了一个通信事件或一个错误。
语法:void CMSComm::SetCommEvent(short nNew Value)
short CMSComm::GetCommEvent()
说明:通常由函数GetCommEvent()得到当前事件值,再进行相应的处理。
CommEven属性值反映错误或者事件类型,通常在程序中的事件消息处理函数中对CommEven事件进行处理。该属性在设计时无效,在运行时为只读。
1.2.4 MSComm控件的OnComm事件
根据应用程序的用途和功能,在连接到其他设备过程中,以及接收或发送数据过程中,可能需要监视并响应一些事件和错误。
可以使用OnComm事件和CommEvent属性捕捉并检查通信事件和错误的值。
在发生通信事件或错误时,将触发OnComm事件,CommEvent属性的值将被改变。因此,在发生OnComm事件时,如果有必要,可以检查CommEvent属性的值。由于通信(特别是通过电话线的通信)是不可预料的,捕捉这些事件和错误将有助于使应用程序对这些情况做出相应的反应。
MSComm控件把17个事件归并为一个事件OnComm,用属性CommEvent的17个值来区分不同的触发时机。
表1-1 列出了几个可能触发OnComm事件的通信事件,对应的值将在发生事件时被写入CommEvent属性。
表1-1 通信事件常数定义值
另外10种情况是可能发生的各种通信错误时触发,可参看有关资料。
表1-2所列错误同样会触发OnComm事件,并在CommEvent属性中写入相应的值。
表1-2 通信错误常数定义值
MSComm控件可捕获的错误消息如表1-3所示。
表1-3 MSComm控件可捕获的错误消息
通过事件的引发,借由CommEvent属性值的数值便可明确了解所发生的错误或事件,而程序中通常就以常数定义作为判断,一旦OnComm事件发生,连带地会引入CommEvent参数,用户可以在每一个相关的Case语句之后编写程序代码来处理特定的错误或事件。
1.2.5 MSComm控件通信步骤
使用MSComm控件开发串口通信程序一般可分为如下几个步骤:
(1)添加MSComm控件。在打开的工程中,选择菜单“Proiect/Add To Proiect/Components and Controls”,在弹出的“Components and Controls Gallery”对话框中选中“Microsoft Communications Controls version 6.0”,单击“Insert”按钮即可完成添加MSComm控件操作。如果工程是个对话框的话,就会在对话框上多出个电话符号。不过这个电话符号是不会在实际应用程序中出现的。添加成功后,相应地会出现一个CMSComm类。
(2)初始化串口。在对话框模板上右键单击MSComm控件,选择“Property”菜单项,即可设置MSComm控件各项属性。另外,亦可通过修改对话框类的OnlnitDialog()函数来设置控件的属性。
(3)串口接收数据初始化。如果MSComm控件采用事件驱动的方式从端口获取数据,必须对端口数据监视和处理。有事件发生(串口接收到数据)时通知程序,并及时地捕获和处理这个通信事件。添加串口事件消息处理函数OnCommMscomm(),用来监视和处理端口数据。在相应的头文件和定义文件中添加事件驱动说明和事件的驱动。当控件引起comEvReceiv事件,就表示有数据到达,则启动相应的函数功能接收数据。接收的数据是一个变量型VARIANT变量,必须先要转变为ColeSafeArray,然后再转变为对应的字符。该函数的事件是由参数RThreshold引起的,由串口控件的函数SetRThreshold0设置该参数。
(4)串口发送数据。添加发送函数OnSend()。MSComm类的写函数比较简单,为SetOutput()。函数原形为VARIANT void SetOutput(const VARIANT newValue),使用VARIANT类型。但PC发送和接收数据时习惯用字符串形式。可以用BSTR表示字符串,但所有的BSTR都包含宽字符,而只有Windows NT支持宽字符,Windows 9×并不支持。所以要完成一个适应操作系统的串口应用程序必须解决这个问题,使用CbyteArray即可解决不同操作系统的字符转换问题。