【问题标题】:Waiting for serial transmission to complete in Win32在 Win32 中等待串行传输完成
【发布时间】:2018-06-01 17:36:12
【问题描述】:

我在等待串行数据传输完成时似乎有点麻烦。

我对相关MSDN article 的解释是EV_TXEMPTY 事件是正确的信号,它表明:

EV_TXEMPTY - 输出缓冲区中的最后一个字符已发送。

但是,在我的测试中,事件总是在数据提交到缓冲区后立即触发,并且远在最终结果实际到达线路之前很久。请参阅下面的重现代码,其中期间始终为零。

我是否在实现中犯了错误,是我误解了标志的用途,还是现代驱动程序根本不支持此功能?在后一种情况下,是否有可行的解决方法,比如某种形式的同步线路状态请求?

为了记录,测试是使用 Windows 10 系统中的 FTDI USB-RS485 和 TTL-232R 设备、Windows 7 系统上的 USB-SERIAL CH340 接口以及 2005 的板载串行接口进行的-老式的 Windows XP 机器。在 FTDI 案例中,嗅探 USB 总线仅显示批量输出事务,没有明显的完成中断通知。

#include <stdio.h>
#include <windows.h>

static int fatal(void) {
    fprintf(stderr, "Error: I/O error\n");
    return 1;
}

int main(int argc, const char *argv[]) {
    static const char payload[] = "Hello, World!";
    // Use a suitably low bitrate to maximize the delay
    enum { BAUDRATE = 300 };
    // Ask for the port name on the command line
    if(argc != 2) {
        fprintf(stderr, "Syntax: %s {COMx}\n", argv[0]);
        return 1;
    }
    char path[MAX_PATH];
    snprintf(path, sizeof path, "\\\\.\\%s", argv[1]);
    // Open and configure the serial device
    HANDLE handle = CreateFileA(path, GENERIC_WRITE, 0, NULL,
        OPEN_EXISTING, 0, NULL);
    if(handle == INVALID_HANDLE_VALUE)
        return fatal();
    DCB dcb = {
        .DCBlength = sizeof dcb,
        .BaudRate = BAUDRATE,
        .fBinary = TRUE,
        .ByteSize = DATABITS_8,
        .Parity = NOPARITY,
        .StopBits = ONESTOPBIT
    };
    if(!SetCommState(handle, &dcb))
        return fatal();
    if(!SetCommMask(handle, EV_TXEMPTY))
        return fatal();
    // Fire off a write request
    DWORD written;
    unsigned int elapsed = GetTickCount();
    if(!WriteFile(handle, payload, sizeof payload, &written, NULL) ||
        written != sizeof payload)
        return fatal();
    // Wait for transmit completion and measure time elapsed
    DWORD event;
    if(!WaitCommEvent(handle, &event, NULL))
        return fatal();
    if(!(event & EV_TXEMPTY))
        return fatal();
    elapsed = GetTickCount() - elapsed;
    // Display the final result
    const unsigned int expected_time =
        (sizeof payload * 1000 /* ms */ * 10 /* bits/char */) / BAUDRATE;
    printf("Completed in %ums, expected %ums\n", elapsed, expected_time);
    return 0;
}

背景是,这是 Modbus RTU 协议测试套件的一部分,我试图在线路上的字符之间注入 >3.5 个字符的空闲延迟以验证设备响应。 诚然,嵌入式实时系统更适合这项任务,但出于各种原因,我更愿意坚持使用 Windows 环境,同时尽可能控制时间。

【问题讨论】:

  • 您可以添加对SetupComm() 的调用来设置缓冲区大小,不确定默认情况下它是什么。不过,我不是 Windows 程序员。
  • 另外,或许SetCommBreak()可以用来实现传输延迟?
  • @unwind:谢谢。我尝试了SetupComm(handle, 2, 2)(单字符缓冲区导致ERROR_INVALID_DATA 错误)但不幸的是它没有任何区别。至少对于 Windows 10 系统上的 FTDI 接口不是这样,并且该请求在我可以看到的 USB 总线上没有产生任何流量。
  • 底层设备驱动程序一般只会告诉你传输缓冲区是否为空。它不会告诉您传输中的数据,例如 UART 芯片内置的 FIFO 缓冲区中存储的内容。或者电线另一端的 USB 控制器在做什么。如果这很重要,请避免让它变得重要,那么在检测到 EV_TXEMPTY 后,你只能睡一会儿。
  • 因为您使用同步句柄并因此同步 WriteFile - 额外调用 WaitCommEvent 以等待 SERIAL_EV_TXEMPTY 完全没有意义 - 因为同步写入直到缓冲区中的所有字节都不会返回控制将被发送到较低级别的驱动程序或硬件。 事件总是在数据提交后立即触发这个并且必须是

标签: c winapi serial-port ftdi


【解决方案1】:

根据@Hans Passant 和@RbMm 的cmets,EV_TXEMPTY documentation 中引用的输出缓冲区是一个中间缓冲区,该事件表明数据已转发给驱动程序。没有定义包含完整链到最终设备缓冲区的等效通知事件。

目前,除了基于比特率的手动延迟以及为要遍历的任何剩余缓冲层、字符间间隙、时钟偏差等增加显着的最坏情况余量之外,我目前还没有明确的通用解决方法。

因此,我非常感谢提供更好的替代解决方案的答案。


不过,对于我的特定应用程序,我已经实施了一个可行的解决方法。

目标硬件是带有 FTDI RS485 接口的半双工总线。此特定设备提供可选的本地回显模式,在该模式下主动传输到总线上的数据不会主动从接收中过滤。

因此,在每次传输之后,我都可以等待预期的回声以往返确认的形式出现。此外,这还可用于检测某些故障,例如总线短路。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多