三、 RS-232-C串行端口监控软件的程序实现

  (一) 界面风格

  由于是实时监控软件,那就既要监测从外设传来的实时数据,又要通过串口向外设发送一些具体的指令以控制外设完成预先设定的动作。为了方便向串口发送命令可以在工具条上再加一个类似于"Internet Explorer 浏览器"风格的对话条,可以在初建工程时指定"Internet Explorer ReBars"风格,也可以通过添加Microsoft Visual C++ 6.0自带的"Dialog Bar"组件来实现。而要及时将从外部读取的数据显示给管理人员,并且留有相当记录以备查阅,可以选择列表视图来实现。

  (二) 串口的参数设置及打开 

  对RS-232-C串行端口进行参数配置是使用串口进行通讯的必要条件。而且由于场合不同、用途、功能的不同对串口也采取不同的配置方式,为了使本程序更灵活,适应面更广,采取将所有的可能参数都预先设置在几个组合框中,可以在程序运行后随时更改设置。自定义一个设置串口参数的数据结构:

typedef struct tagCOM_CONFIG 

{ 

int nPort; file://端口号,从COM1到COM4 

int nBaud; file://波特率,从1200bps到57600bps(对应的宏为CBR_1200到CBR_57600) 

int nData; file://数据位个数,7位或是8位 

int nStop; file://停止位个数,可以是1位、1.5位、2位。 

int nParity;//采取的校验方式,有无校验(NOPARITY)、 

file://奇校验(ODDPARITY)和偶校验(EVENPARITY)等。 

}COM_CONFIG;



  当选择好适当的参数后就可以根据设置好的端口配置情况打开通讯端口了。与以往DOS下串行通信程序不同的是,Windows操作平台下不提倡应用程序直接控制硬件(包括端口),也不让使用中断(除非打入到Ring0系统级),而是通过Windows操作系统提供的设备驱动程序来进行数据传递。在Windows操作系统下串行口和其他通讯端口一样是作为文件来进行处理的,而不是直接对端口进行操作,对于串行通信,Win 32 提供了相应的文件I/O函数与通信函数,通过了解这些函数的使用,可以编制出符合不同需要的通信程序。与通信设备相关的结构有COMMCONFIG ,COMMPROP,COMMTIMEOUTS,COMSTAT,DCB,MODEMDEVCAPS,MODEMSETTINGS共7个,与通信有关的Windows API函数共有26个,具体说明可参考MSDN帮助文件。下面是打开串口的部分关键代码:

//以创建文件的形式打开文件,并将返回的端口句柄保存于句柄idComDev之中。 

idComDev =CreateFile( g_szCom_Port[g_com_config.nPort],  

GENERIC_READ | GENERIC_WRITE, 

0,  

NULL,  

OPEN_EXISTING, 

FILE_ATTRIBUTE_NORMAL, 

NULL );  

……  

file://cfg为COMMCONFIG结构的实例对象,获取当前通讯口的状态。 

cfg.dcb.DCBlength = sizeof( DCB ) ; 

GetCommState( idComDev, &(cfg.dcb) ) ; 

file://设置发送、接收缓存大小 

SetupComm( idComDev, 4096, 4096 ) ; 

// PurgeComm()是一个清除函数,它可以用来中止任何未决的后台读或写,并且可以冲掉I/O 

file://缓冲区.其中:PURGE_TXABORT 用于中止后台写操作;PRUGE_RXABORT用于中止后台 

file://读操作 ;PRUGE_TXCLEAR用于清除发送缓冲区;PRUGE_RXCLEAR用于清除接收缓冲区 

PurgeComm(idComDev,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); 

file://根据设置的参数填充DCB结构对象dcb的各个数据成员变量 

dcb.DCBlength = sizeof( DCB ) ; 

GetCommState( idComDev, &dcb ) ; 

file://设置端口通讯参数 

dcb.BaudRate =g_Com_Baud[g_com_config.nBaud]; 

dcb.ByteSize =g_Com_ByteSize[g_com_config.nData]; 

dcb.Parity =g_Com_Parity[g_com_config.nParity] ; 

dcb.StopBits =g_Com_StopBits[g_com_config.nStop]; 

file://硬件流控制 

dcb.fDtrControl = DTR_CONTROL_DISABLE ; 

dcb.fOutxCtsFlow = FALSE ; 

dcb.fRtsControl = RTS_CONTROL_DISABLE ; 

file://软件流控制 

dcb.fInX = dcb.fOutX = FALSE ; 

dcb.XonChar = (char)0xFF ; 

dcb.XoffChar = (char)0XFF ; 

dcb.XonLim = 100 ; 

dcb.XoffLim = 100 ; 

dcb.EvtChar=0x0d; 

dcb.fBinary = TRUE ; 

dcb.fParity = TRUE ;



file://超时控制的设置。超时有两种:区间超时:(仅对从端口中读取数据有用)它指定在读取两个字符之间要经历的时间;总超时: 当读或写特定的字节数需要的总时间超过某一阈值时,超时触发。计算超时可以根据公式:

file://ReadTotalTimeout = (ReadTotalTimeoutMultiplier * bytes_to_read)+  

// ReadToTaltimeoutConstant 

file://WriteTotalTimeout = (WriteTotalTimeoutMuliplier * bytes_to_write)+  

// WritetoTotalTimeoutConstant 

file://如果在设置超时时参数为0则为无限等待,即无超时。 

CommTimeOuts.ReadIntervalTimeout =MAXDWORD; 

CommTimeOuts.ReadTotalTimeoutMultiplier =0; 

CommTimeOuts.ReadTotalTimeoutConstant = 0 ; 

CommTimeOuts.WriteTotalTimeoutMultiplier =2*9600/dcb.BaudRate ; 

CommTimeOuts.WriteTotalTimeoutConstant = 25 ; 

SetCommTimeouts(idComDev , &CommTimeOuts ) ; 

file://根据设置好的dcb结构设置好通讯口的状态,并开启用于侦听端口,监视从外设传来的数 

file://据的线程COMReadThreadProc。 

if (SetCommState( idComDev, &dcb )) 

{ 

m_bComPortOpen=TRUE; 

g_hCom=idComDev; 

AfxBeginThread(COMReadThreadProc,NULL,THREAD_PRIORITY_NORMAL); 

return; 

}