【问题标题】:Serial connection in C program with Win32 Api在 C 程序中使用 Win32 Api 进行串行连接
【发布时间】:2016-02-29 08:11:39
【问题描述】:

我编写了一个小型 C 程序,用于监控串行端口流量(信号和输入)。我使用的应用程序是一个同步的、事件驱动的应用程序。我同步调用WaitCommEvent 函数(所以不使用 使用OVERLAPPED 结构)。

在我的应用程序中监视以下 COM 事件:

  • EV_CTS:CTS(清除发送)信号状态改变
  • EV_RLSD: RLSD (receive-line-signal-detect) 信号状态改变
  • EV_RXCHAR: 接收到一个字符并将其放入输入缓冲区中

我的问题是,如果上面提到的信号之一改变了它的状态,那么WaitCommEvent(第二个参数)的输出掩码具有它的值(EV_CTS0x0008)或EV_RLSD0x0020),如果信号被设置)或EV_RXCHAR 的值(0x0001,如果信号被清除)。换句话说:如果其中一个信号被清除,那么对于“清除事件”,我会收到一个EV_RXCHAR,所以我的软件无法区分“收到字符”和“信号清除”事件。

请帮我找到一个想法,让我的软件能够区分“收到字符”和“清除信号”事件。 WinApi 为这两种情况返回事件掩码值0x0001

更新:

为了更好地理解我的问题,我发布了我的程序的代码和控制台输出。

串行总线上发生以下情况(我的应用程序也应该检测到):

  1. RLSD 信号已设置。
  2. 数据已发送(因此应由我的应用程序读取/接收)。
  3. RLSD 信号被清除。

事件处理的代码如下:

if(TRUE == WaitCommEvent(hComPort, &dwEvtMask, NULL))
{
    PrintCurrentDateTime();
    printf("the dwEvtMask = 0x%04X\r\n", dwEvtMask);

    GetCommModemStatus(hComPort, &dwModemState);

    PrintCurrentDateTime();
    printf("the dwModemState = 0x%04X\r\n", dwModemState);

    if(dwEvtMask & EV_CTS) // Clear-to-send signal changed
    {
        PrintCurrentDateTime();
        printf("EV_CTS triggered.\r\n");
    }

    if(dwEvtMask & EV_RLSD) // Data-carrier-detect signal changed
    {
        PrintCurrentDateTime();
        printf("EV_RLSD triggered.\r\n");
    }

    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 = 0x0000

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 = 0x0000

2015.11.26 11:51:03:671 在端口 COM1 上收到 3 个字符:07 01 06

2015.11.26 11:51:03:671 dwEvtMask = 0x0001

2015.11.26 11:51:03:671 dwModemState = 0x0000

2015.11.26 11:51:03:671 在端口 COM1 上收到 0 个字符:

也许 WinApi 不像 MSDN 里写的那样,谁知道呢……

更新 2:

正如 Hans Passant 在下面所写,问题是,我总是在 EV_RLSD 之后收到事件 EV_RXCHAR,这导致了误解。我必须使用的协议定义了,数据只应在设置 RLSD 信号期间接收。确实如此,所以总线动作正确,但是由于接收需要一些时间(因为序列化等原因,详情见上面Hans Passant的帖子)。

如果 RLSD 信号“下降”(1 -> 0),我可以通过检查 EV_RLSD 上是否接收到数据(通过调用 ReadFile)来解决问题。

【问题讨论】:

  • 听起来你做对了,但是你能显示一些代码吗?
  • 根据WaitCommEvent 的文档,EV_CTS/EV_RLSD 位将在信号更改时设置,这意味着更改 0->1 或更改 1->0。您可能需要拨打GetCommModemStatus 来获取CTS/RLSD 的值。接收 EV_RXCHAR 并不意味着清除 CLS/RLSD。您可能需要检查您的代码,否则该函数的行为与文档不符。
  • 仔细阅读,尤其是接收多个字符时有关 EV_RXCHAR 的部分。 msdn.microsoft.com/en-us/library/ff802693.aspx
  • 亲爱的 Roddy,我使用同步 WaitCommEvent(这意味着不存在 OVERLAPPED 结构)。因此,样本对我没有帮助。我使用的应用程序是单线程应用程序,因为实时性是它的关键。
  • 尊敬的 user1969104,我阅读的 MSDN 源代码可以在这里找到:msdn.microsoft.com/en-us/library/windows/desktop/… 但我发布了我的软件的代码和输出。

标签: c windows winapi serial-port


【解决方案1】:

区分“收到字符”和“信号清除”事件

EV_CTS 和 EV_RLSD 不是这个意思,它们只是表示信号改变了状态,并没有说它是“清除”的。您可以使用该事件调用 GetCommModemStatus() 并获取实际的信号状态。所以得到 0x0001 只表示接收到一个字节,信号没有变化。

事件掩码可帮助您最大限度地减少需要进行的函数调用次数。因此,无需为每个事件调用 GetCommModemStatus()。请注意,您可能会同时收到多个事件信号。

在您订购预期获得的事件的方式中,还有另一个问题的迹象。您希望 EV_RXCHAR 之后获得 EV_RLSD。不是它的工作方式,握手信号的变化是立即生效的。但是发送和接收一个字节需要时间。该字节必须由设备上的 UART 串​​行化,通常需要 10 倍的波特率时钟。 PC 上的 UART 将字节放入 FIFO 缓冲区,该缓冲区最终会产生一个中断,告诉驱动程序将字节复制到其接收缓冲区中。然后你会得到 EV_RXCHAR 事件。

所以使用 DCD 作为某种“门”信号是没有用的。设备不可能知道 PC 何时收到字节,它总是会过早关闭 DCD。您可以使用握手信号玩很多游戏,但是它们通常不会那么好。 DCD 是一种调制解调器信号,仅用于协商连接,将其用于其他用途会产生一种巴洛克式的协议,很少有程序员知道如何正确实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 2013-01-30
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    相关资源
    最近更新 更多