错误修复更新:截至 2013 年 6 月,FTDI 确实向我承认该错误是真实的。此后,他们发布了一个新版本的驱动程序(2.8.30.0,日期为 2013 年 7 月 12 日)以解决该问题。该驱动程序在 2013 年 8 月 1 日左右通过 WHQL,目前可通过 Windows 更新获得。
我重新测试了运行相同的测试代码,但无法重现新驱动程序的问题,所以目前修复似乎是“升级驱动程序”。
最初的问题:我有一个基于 FTDI FT2232D 芯片的 8 端口 USB 串行设备(来自 VsCOM)。当我从一个端口以某些设置进行传输,并且我使用硬件握手停止并从另一端启动数据流时,我得到两个症状:
1)输出数据有时会变成垃圾。会有 NUL 字符,以及几乎任何你能想到的随机事物。
2) WriteFile调用有时会返回比我要求它写入的字节数更大的字节数。那不是错字。我要求传输 30 个字节,发送的字节数返回 8192(是的,我在拨打电话之前将发送的数字清除为 0)。
相关事实:使用 FTDI 驱动程序 2.8.24.0,这是目前最新的。串口设置为 19200,7 个数据位,奇校验,1 个停止位。我在另一个基于 FTDI 的串行设备上得到了同样的行为,这次是一个单端口。我使用另一个相同类型的 8 端口设备得到相同的行为。在内置串行端口 (COM1) 上传输时,我没有出现这种情况。我有一个非常简单的“Writer”程序,它只是连续传输,一个非常简单的“Toggler”程序,每秒切换一次 RTS。这些似乎在 60 秒内触发了问题。我已经向设备制造商提出了问题,但他们还没有太多时间做出回应。编译器是 mingw32,它包含在 Qt 4.8.1 (gcc 4.4.0) 的 Qt 安装程序中
我想首先知道,如果有人能想到我可以做些什么来触发这种行为。我无法想象任何事情,但总有一些我不知道的事情。
其次,我附上了 Writer 和 Toggler 测试程序。如果有人能发现一些可能触发该程序的问题,我很想听听。我很难认为存在驱动程序错误(尤其是来自像 FTDI 芯片这样成熟的东西),但情况迫使我认为至少有一些驱动程序参与。至少,无论我对它做什么,它都不应该返回比我要求它写的更多的字节数。
写程序:
#include <iostream>
#include <string>
using std::cerr;
using std::endl;
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
cerr << "COM Writer, ctrl-c to end" << endl;
if (argc != 2) {
cerr << "Please specify a COM port for parameter 2";
return 1;
}
char fixedbuf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
std::string portName = "\\\\.\\";
portName += argv[1];
cerr << "Transmitting on port " << portName << endl;
HANDLE ph = CreateFileA( portName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if (ph == INVALID_HANDLE_VALUE) {
cerr << "CreateFile " << portName << " failed, error " << GetLastError() << endl;
return 1;
}
COMMCONFIG ccfg;
DWORD ccfgSize = sizeof(COMMCONFIG);
ccfg.dwSize = ccfgSize;
GetCommConfig(ph, &ccfg, &ccfgSize);
GetCommState(ph, &(ccfg.dcb));
ccfg.dcb.fBinary=TRUE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
ccfg.dcb.fAbortOnError=FALSE;
ccfg.dcb.fNull=FALSE;
// Camino is 19200 7-O-1
ccfg.dcb.BaudRate = 19200;
ccfg.dcb.Parity = ODDPARITY;
ccfg.dcb.fParity = TRUE;
ccfg.dcb.ByteSize = 7;
ccfg.dcb.StopBits = ONESTOPBIT;
// HW flow control
ccfg.dcb.fOutxCtsFlow=TRUE;
ccfg.dcb.fRtsControl=RTS_CONTROL_HANDSHAKE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
COMMTIMEOUTS ctimeout;
DWORD tout = 10;// 10 ms
ctimeout.ReadIntervalTimeout = tout;
ctimeout.ReadTotalTimeoutConstant = tout;
ctimeout.ReadTotalTimeoutMultiplier = 0;
ctimeout.WriteTotalTimeoutMultiplier = tout;
ctimeout.WriteTotalTimeoutConstant = 0;
SetCommConfig(ph, &ccfg, sizeof(COMMCONFIG));
SetCommTimeouts(ph, &ctimeout);
DWORD nwrite = 1;
for(;;) {
nwrite++;
if (nwrite > 30) nwrite = 1;
DWORD nwritten = 0;
if (!WriteFile(ph, fixedbuf, nwrite, &nwritten, NULL)) {
cerr << "f" << endl;
}
if ((nwritten != 0) && (nwritten != nwrite)) {
cerr << "nwrite: " << nwrite << " written: " << nwritten << endl;
}
}
return 0;
}
切换程序:
#include <iostream>
#include <string>
using std::cerr;
using std::endl;
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
cerr << "COM Toggler, ctrl-c to end" << endl;
cerr << "Flips the RTS line every second." << endl;
if (argc != 2) {
cerr << "Please specify a COM port for parameter 2";
return 1;
}
std::string portName = "\\\\.\\";
portName += argv[1];
cerr << "Toggling RTS on port " << portName << endl;
HANDLE ph = CreateFileA( portName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if (ph == INVALID_HANDLE_VALUE) {
cerr << "CreateFile " << portName << " failed, error " << GetLastError() << endl;
return 1;
}
COMMCONFIG ccfg;
DWORD ccfgSize = sizeof(COMMCONFIG);
ccfg.dwSize = ccfgSize;
GetCommConfig(ph, &ccfg, &ccfgSize);
GetCommState(ph, &(ccfg.dcb));
ccfg.dcb.fBinary=TRUE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
ccfg.dcb.fAbortOnError=FALSE;
ccfg.dcb.fNull=FALSE;
// Camino is 19200 7-O-1
ccfg.dcb.BaudRate = 19200;
ccfg.dcb.Parity = ODDPARITY;
ccfg.dcb.fParity = TRUE;
ccfg.dcb.ByteSize = 7;
ccfg.dcb.StopBits = ONESTOPBIT;
// no flow control (so we can do manually)
ccfg.dcb.fOutxCtsFlow=FALSE;
ccfg.dcb.fRtsControl=RTS_CONTROL_DISABLE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
COMMTIMEOUTS ctimeout;
DWORD tout = 10;// 10 ms
ctimeout.ReadIntervalTimeout = tout;
ctimeout.ReadTotalTimeoutConstant = tout;
ctimeout.ReadTotalTimeoutMultiplier = 0;
ctimeout.WriteTotalTimeoutMultiplier = tout;
ctimeout.WriteTotalTimeoutConstant = 0;
SetCommConfig(ph, &ccfg, sizeof(COMMCONFIG));
SetCommTimeouts(ph, &ctimeout);
bool rts = true;// true for set
for(;;) {
if (rts)
EscapeCommFunction(ph, SETRTS);
else
EscapeCommFunction(ph, CLRRTS);
rts = !rts;
Sleep(1000);// 1 sec wait.
}
return 0;
}