SetCommMask
用途:设置串口通信事件
原型:BOOL SetCommMask(HANDLE hFile, //标识通信端口的句柄
DWORD dwEvtMask //能够使能的通信事件
);
参数说明:-hFile:串口句柄
-dwEvtMask:准备监视的串口事件掩码
串口上可能发生的事件如下表所示:
值 | 事件描述 |
EV_BREAK | A break was detected on input. |
EV_CTS | The CTS (clear-to-send) signal changed state. |
EV_DSR | The DSR(data-set-ready) signal changed state. |
EV_ERR | A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. |
EV_RING | A ring indicator was detected. |
EV_RLSD | The RLSD (receive-line-signal-detect) signal changed state. |
EV_RXCHAR | A character was received and placed in the input buffer. |
EV_RXFLAG | The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function. |
EV_TXEMPTY | The last character in the output buffer was sent. |
参数含义:
EV_BREAK:收到BREAK信号。
EV_CTS:CTS(clear to send)线路发生变化。
EV_DSR:DST(Data Set Ready)线路发生变化。
EV_ERR:线路状态错误,包括了CE_FRAME / CE_OVERRUN / CE_RXPARITY 3种错误。
EV_RING:检测到振铃信号。
EV_RLSD:CD(Carrier Detect)线路信号发生变化。
EV_RXCHAR:输入缓冲区中已收到数据,即接收到一个字节并放入输入缓冲区。
EV_RXFLAG:使用SetCommState()函数设置的DCB结构中的等待字符已被传入输入缓冲区中。
EV_TXEMPTY:输出缓冲区中的数据已被完全送出。
操作举例:SetCommMask(hComm,EV_RXCHAR|EV_TXEMPTY);
上面函数执行完毕后将监视串口中有无数据和发送缓冲区中的数据是否全部发送完毕。
WaitCommEvent()
用途:用来判断用SetCommMask()函数设置的串口通信事件是否已发生。
原型:BOOL WaitCommEvent(HANDLE hFile,
LPDWORD lpEvtMask,
LPOVERLAPPED lpOverlapped
);
参数说明:
-hFile:串口句柄
-lpEvtMask:函数执行完后如果检测到串口通信事件的话就将其写入该参数中。
-lpOverlapped:异步结构,用来保存异步操作结果。
操作举例:OVERLAPPED os;
DWORD dwMask,dwTrans,dwError=0,err;
memset(&os,0,sizeof(OVERLAPPED));
os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!WaitCommEvent(hComm,&dwMask,&os)){
//如果异步操作不能立即完成的话,函数返回FALSE,并且调用GetLastError()函
//数分析错误原因后返回ERROR_IO_PENDING,指示异步操作正在后台进行.这种情
//况下,在函数返回之前系统设置OVERLAPPED结构中的事件为无信号状态,该函数
//等待用SetCommMask()函数设置的串口事件发生,共有9种事件可被监视:
//EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR,
//EV_RXFLAG,EV_TXEMPTY;当其中一个事件发生或错误发生时,函数将
//OVERLAPPED结构中的事件置为有信号状态,并将事件掩码填充到dwMask参数中
if(GetLastError()==ERROR_IO_PENDING){
/**************************************************************/
/*在此等待异步操作结果,直到异步操作结束时才返回.实际上此时 */
/*WaitCommEvent()函数一直在等待串口监控的事件之一发生,当事件发*/
/*生时该函数将OVERLAPPED结构中的事件句柄置为有信号状态,此时 */
/*GetOverlappedResult()函数发现此事件有信号后马上返回,然后下面*/
/*的程序马上分析WaitCommEvent()函数等到的事件是被监视的串口事 */
/*件中的哪一个,然后执行相应的动作并发出相应消息. */
/**************************************************************/
GetOverlappedResult(hComm,&os,&dwTrans,true);
switch(dwMask){
case EV_RXCHAR:
PostMessage(Parent,WM_COMM_RXCHAR,0,0);
break;
case EV_TXEMPTY:
PostMessage(Parent,WM_COMM_TXEMPTY,0,0);
break;
case EV_ERR:
switch(dwError){
case CE_FRAME:
err=0;
break;
case CE_OVERRUN:
err=1;
break;
case CE_RXPARITY:
err=2;
break;
default:break;
}
PostMessage(Parent,WM_COMM_ERR,(WPARAM)0,(LPARAM)err);
break;
case EV_BREAK:
PostMessage(Parent,WM_COMM_BREAK,0,0);
break;
case ...://其他用SetCommMask()函数设置的被监视的串口通信事件。
... ...
break;
default:break;
}
}
-以上简要介绍了大部分的串口通信api函数,笔者所写的串口通信软件用的是事件通知方式,该方式是
windows2000下效率较高的一种方式。而且只熟悉这些api函数也还是不够的,该机制下还要牵涉到多
线程和消息机制,其中读写串口的动作是由主线程来完成的,比如说操作者按下发送数据的按钮之后
,相应函数马上将某特定区域里面的数据发送出去,所以说用api函数写串口发送数据的功能是相对较
简单的。收数据的时候就要麻烦一点,在打开串口后首先主线程要设置要监视的串口通信事件,然后
将监视线程打开,用来监视主线程设置的这些串口通信事件是否已发生,当其中的某个事件发生后,
监视线程马上将该消息发送给主线程,其中监视线程在发送消息之前要确保主线程在收到消息后肯定
的知道串口究竟发生了什么样的事件,然后根据不同的事件类型进行处理。下面给出大致的主线程和
监视线程的大致工作流程:
主线程打开(其实就是主窗体打开之后)
|
|
V
打开串口(设置波特率、校验方式、数据位数、停止位数)
|
|
V
设置监视线程需要监视的串口通信事件
|
|
V
打开监视线程
|
|
V
等待各种事件的发生(比如发送数据单击事件,更改通信参数事件,监视线程发来的消息等)
--------------------------------------------------------------------------------
监视线程被打开
|
|
V
串口事件发生否(WaitCommEvent())(无论发生否均进入下面的代码)
|
|
V
异步操作是否正在后台进行?(if(GetLastError()==ERROR_IO_PENDING))
|
|
V
在此等待异步操作结果(GetOverlappedResult(hComm,&os,&dwTrans,true))
|
|
V
处理通信事件,根据事件类型的不同给主窗体发送不同的消息
总结起来说SetCommMask()和WaitCommEvent()是要成对使用的。在串口打开的时候SetCommMask,然后在读数据的线程中WaitCommEvent。不过要注意的是,一般不会在WaitCommEvent之后做关于该串口数据的处理(尤其是在数据比较多,处理缓慢的时候——容易丢失数据吧~~),一般会把数据简单的保存下来,再发送消息给一个专门的处理函数进行处理。