我写了一个小C程序,用于监控串行端口流量(信号和输入)。我使用的应用程序是一个同步的、事件驱动的应用程序。我同步调用WaitCommEvent
函数(因此不使用使用OVERLAPPED
结构)。
在我的应用程序中监视以下COM事件:
EV_CTS
:CTS(清除发送)信号改变状态EV_RLSD
:RLSD(接收线信号检测)信号改变状态EV_RXCHAR
:接收到一个字符并将其放入输入缓冲区
我的问题是,如果上面提到的信号之一改变了它的状态,那么WaitCommEvent
(第二参数)的输出掩码要么具有它的值(如果信号被设置,则为EV_CTS
(0x0008
)或EV_RLSD
(0x0020
)),要么具有EV_RXCHAR
的值(当信号被清除时为0x0001
)。换句话说:如果这些信号中的一个被清除;清除事件";我收到一个EV_RXCHAR
,所以我的软件无法区分";字符接收";以及";信号清除";事件
请帮我找到一个想法,让我的软件能够区分";字符接收";以及";信号清除";事件。对于这两种情况,WinApi都返回事件掩码值0x0001
。
更新:
为了更好地理解我的问题,我发布了程序的代码和控制台输出。
以下情况发生在串行总线上(我的应用程序也应该检测到):
- RLSD信号被设置
- 数据已发送(因此我的应用程序应读取/接收数据)
- RLSD信号被清除
事件处理的代码如下:
if(TRUE == WaitCommEvent(hComPort, &dwEvtMask, NULL))
{
PrintCurrentDateTime();
printf("the dwEvtMask = 0x%04Xrn", dwEvtMask);
GetCommModemStatus(hComPort, &dwModemState);
PrintCurrentDateTime();
printf("the dwModemState = 0x%04Xrn", dwModemState);
if(dwEvtMask & EV_CTS) // Clear-to-send signal changed
{
PrintCurrentDateTime();
printf("EV_CTS triggered.rn");
}
if(dwEvtMask & EV_RLSD) // Data-carrier-detect signal changed
{
PrintCurrentDateTime();
printf("EV_RLSD triggered.rn");
}
if(dwEvtMask & EV_RXCHAR) // Data received
{
ReadSerial(hComPort, portNum, readBuff, READ_BUFF_MAX_LENGTH);
}
}
我在控制台上得到的输出如下:
2015.11.26 11:51:03:578 dwEvtMask=0x0020
2015.11.26 11:51:03:593 dwModemState=0x0080
2015.11.26 11:51:03:593 EV_RLSD触发。
2015.11.26 11:51:03:656 dwEvtMask=0x0020
2015.11.26 11:51:03:656 dwModemState=0x000
2015.11.26 11:51:03:656 EV_RLSD触发。
2015.11.26 11:51:03:671 dwEvtMask=0x0001
2015.11.26 11:51:03:671 dwModemState=0x000
2015.11.26 11:51:03:671在端口COM1:07 01 06 上接收到3个字符
2015.11.26 11:51:03:671 dwEvtMask=0x0001
2015.11.26 11:51:03:671 dwModemState=0x000
2015.11.26 11:51:03:671在端口COM1:上收到0个字符
也许WinApi的行为不像MSDN上写的那样,谁知道呢。。。
更新2:
正如Hans Passant在下面所写的,问题是,我总是在EV_RLSD之后接收事件EV_RXCHAR,这导致了误解。我必须使用的协议定义,数据应该只在RLSD信号设置期间接收。事实确实如此,所以总线的操作是正确的,但由于接收需要一些时间(由于序列化等原因,有关详细信息,请参阅Hans Passant在上面的文章)。
我可以通过检查EV_RLSD上接收到的数据(通过调用ReadFile)来解决这个问题;下降";(1->0)。
区分"字符接收"one_answers"信号清除"事件
这不是EV_CTS和EV_RLSD的意思,它们只是表示信号改变了状态,而不是说它被"清除"。您可以使用该事件来调用GetCommModemStatus()并获得实际的信号状态。所以得到0x0001只意味着收到了一个字节,信号没有改变。
事件掩码可以帮助您最大限度地减少需要进行的函数调用的数量。因此,不需要为每个事件调用GetCommModemStatus()。请注意,您可能会同时收到多个事件的信号。
在您对预期获得的事件进行排序的方式中,还有另一个问题的迹象。您希望在EV_RXCHAR之后获得EV_RLSD。与它的工作方式不同,握手信号的变化会立即生效。但是发送和接收一个字节需要时间。字节必须由设备上的UART串行化,通常需要10倍于波特时钟的时间。PC上的UART将字节放入FIFO缓冲区,FIFO缓冲区最终产生一个中断,告诉驱动程序将字节复制到其接收缓冲区。然后得到EV_RXCHAR事件。
因此,使用DCD作为某种"门"信号是没有用的。设备不可能知道PC何时接收到字节,它总是过早地关闭DCD。你可以用握手信号玩很多游戏,但它们通常不会那么好。DCD是一种调制解调器信号,只用于协商连接,将其用于其他用途会产生一种巴洛克式的协议,很少有程序员知道如何正确实现。