在Win32中等待串行传输完成

时间:2017-12-19 09:59:11

标签: c winapi serial-port ftdi

我似乎在等待完成串行数据传输时遇到了一些麻烦。

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

  

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

然而,在我的测试中,一旦数据提交到缓冲区并且在最终实际到达线路之前很久就会立即触发事件。请参阅下面的重复代码,其中周期始终为零。

我是否在实施中犯了错误,我误解了旗帜的用途,还是现代司机根本不支持这个功能?在后一种情况下是否存在可行的解决方法,比如某种形式的同步线路状态请求?

为了记录,测试是在Windows 10系统中使用FTDI USB-RS485和TTL-232R设备,在Windows 7系统上使用USB-SERIAL CH340接口以及2005年的板载串行接口进行的。 -vintage 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环境,同时尽可能地控制时序。

1 个答案:

答案 0 :(得分:0)

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

目前没有一般的解决方法可以解决基于比特率的手动延迟,并为要遍历的任何剩余缓冲层添加显着的最坏情况余量,字符间间隙,时钟偏差等。

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

然而,对于我的特定应用程序,我实施了一个可行的解决方法。

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

在每次传输后,我因此能够等待预期的回声显示为往返确认。此外,这用于检测某些故障,例如短路总线。