3
串口状态
有两种获取通信端口状态的方法。第一种方法是设置事件掩码,当指定事件发生时应用程序会收到通知。SetCommMask函数用于设置事件掩码,WaitCommEvent用于等待指定的事件发生。它们与16位Windows中的SetCommEventMask和EnableCommNotification类似,只是它们不发送WM_COMMNOTIFY消息。第二种方法是不时地调用另一些状态函数来获取通信端口的状态。当然,轮询是低效的,不建议使用。
3.1
通信事件
通信事件在使用通信端口时可能随时发生。接收通信事件需要两个步骤:
下面是使用SetCommMask的示例:
DWORD dwStoredFlags;
dwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |
EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;
if (!SetCommMask(hComm, dwStoredFlags))
// error setting communications mask |
下表描述了每种事件类型。
事件标志
|
描述
|
EV_BREAK
|
检测到输入中的break
|
EV_CTS
|
CTS(Clear To Send)信号状态改变。要取得CTS线路状态,应使用GetCommModemStatus函数。
|
EV_DSR
|
DSR(Data Set Ready)信号状态改变。要取得DSR线路状态,应使用GetCommModemStatus函数。
|
EV_ERR
|
某线路状态错误发生。线路状态错误包括CE_FRAME、CE_OVERRUN和CE_RXPARITY。要取得具体错误种类,需调用ClearCommError函数。
|
EV_RING
|
检测到振铃指示
|
EV_RLSD
|
RLSD(Receive Line Signal Detect)信号状态改变。要取得RLSD线路状态,需调用GetCommModemStatus函数。注意,RLSD通常被称作CD(carrier detect)。
|
EV_RXCHAR
|
接收到一个字符并且已放入输入缓冲区。请参考下面的“警告”节对此标志的详细讨论。
|
EV_RXFLAG
|
接收到一个事件字符并且已放入输入缓冲区。事件字符由下文讨论的DCB结构EvtChar字段指定。下面的“警告”节也讨论了这个标志。
|
EV_TXEMPTY
|
输出缓冲区中最后一个字符被发送到串口设备了。如果使用硬件缓冲区,此标志仅表示所有数据已经发送到硬件了。如果不与设备驱动交互,是无法确定硬件缓冲区空的。
|
指定事件掩码后,使用WaitCommEvent函数检测事件发生。如果以非重叠方式打开端口,则WaitCommEvent不需要OVERLAPPED结构体,函数阻塞调用线程直到某事件发生。如果没有事件发生,调用线程将无限阻塞。
下面的代码片段展示了如何在以非重叠方式打开的端口上等待EV_RING事件。
DWORD dwCommEvent;
if (!SetCommMask(hComm, EV_RING))
// Error setting communications mask
return FALSE;
if (!WaitCommEvent(hComm, &dwCommEvent, NULL))
// An error occurred waiting for the event.
return FALSE;
else
// Event has occurred.
return TRUE; |
如果没有事件发生,上面的代码将无限阻塞调用线程。解决方法是以重叠方式打开端口,用下面的方式等待状态事件:
#define STATUS_CHECK_TIMEOUT 500 // Milliseconds
DWORD dwRes;
DWORD dwCommEvent;
DWORD dwStoredFlags;
BOOL fWaitingOnStat = FALSE;
OVERLAPPED osStatus = {0};
dwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |
EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;
if (!SetCommMask(comHandle, dwStoredFlags))
// error setting communications mask; abort
return 0;
osStatus.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osStatus.hEvent == NULL)
// error creating event; abort
return 0;
for ( ; ; ) {
// Issue a status event check if one hasn't been issued already.
if (!fWaitingOnStat) {
if (!WaitCommEvent(hComm, &dwCommEvent, &osStatus)) {
if (GetLastError() == ERROR_IO_PENDING)
bWaitingOnStatusHandle = TRUE;
else
// error in WaitCommEvent; abort
break;
}
else
// WaitCommEvent returned immediately.
// Deal with status event as appropriate.
ReportStatusEvent(dwCommEvent);
}
// Check on overlapped operation.
if (fWaitingOnStat) {
// Wait a little while for an event to occur.
dwRes = WaitForSingleObject(osStatus.hEvent, STATUS_CHECK_TIMEOUT);
switch(dwRes)
{
// Event occurred.
case WAIT_OBJECT_0:
if (!GetOverlappedResult(hComm, &osStatus, &dwOvRes, FALSE))
// An error occurred in the overlapped operation;
// call GetLastError to find out what it was
// and abort if it is fatal.
else
// Status event is stored in the event flag
// specified in the original WaitCommEvent call.
// Deal with the status event as appropriate.
ReportStatusEvent(dwCommEvent);
// Set fWaitingOnStat flag to indicate that a new
// WaitCommEvent is to be issued.
fWaitingOnStat = FALSE;
break;
case WAIT_TIMEOUT:
// Operation isn't complete yet. fWaitingOnStatusHandle flag
// isn't changed since I'll loop back around and I don't want
// to issue another WaitCommEvent until the first one finishes.
//
// This is a good time to do some background work.
DoBackgroundWork();
break;
default:
// Error in the WaitForSingleObject; abort
// This indicates a problem with the OVERLAPPED structure's
// event handle.
CloseHandle(osStatus.hEvent);
return 0;
}
}
}
CloseHandle(osStatus.hEvent);
|
上面的代码片段与重叠读取操作的代码非常相似。实际上,MTTTY使用WaitForMultipleObjects在同一个线程中等待读取完成或者状态改变事件发生。SetCommMask
和WaitCommEvent有两种很有意思的边际效应。第一,如果以非重叠方式打开通信端口,WaitCommEvent将阻塞直到某事件发生。如果其
他线程调用SetCommMask设置新的事件掩码,则线程将阻塞在SetCommMask调用上,原因是第一个线程的WaitCommEvent调用仍
在执行中。SetCommMask将一直阻塞调用线程,直到第一个线程的WaitCommEvent调用返回。这种边际效应对于以非重叠方式打开的端口是
通用的。如果某线程阻塞在任何通信函数上,则第二个线程对任何通信函数的调用都将阻塞,直到第一个线程的函数调用返回。第二种边际效应是关于以重叠方式打
开的端口的。如果使用SetCommMask设置新的事件掩码,则未决的WaitCommEvent调用将成功完成,导致调用完成的事件掩码将是
NULL。