【发布时间】: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