Bugfix更新:截至2013年6月,FTDI确实向我承认了这个bug是真实的。自那以后,他们发布了一个新版本的驱动程序(2.8.30.0,日期为2013年7月12日),解决了这个问题。驱动程序在2013年8月1日前后通过WHQL,并在此时通过Windows提供。
我已经重新测试了运行相同的测试代码,并且无法重现新驱动程序的问题,因此目前的修复似乎是“升级驱动程序”。
最初的问题:我有一个8端口USB串行设备(来自VsCOM),是基于FTDI FT2232D芯片。当我从其中一个端口在特定设置下发送,并使用硬件握手停止并启动另一端的数据流时,我会出现两个症状:
1)输出数据有时会变成垃圾。会有无字符的,几乎任何你能想到的随机的东西。
2) WriteFile调用有时会返回比我要求它编写的数字更大的字节数。那不是错字。我要求发送30个字节,发送的字节数返回8192个字节(是的,在调用之前,我确实清除了发送到0的字节数)。
相关事实:使用FTDI驱动程序2.8.24.0,这是目前的最新情况。串口设置为19200,7个数据位,奇偶校验,1个停止位。对于另一个基于FTDI的串行设备,我也有同样的行为,这一次只有一个端口。对于另一个相同类型的8端口设备,我也有相同的行为。在内置的串行端口(COM1)上传输时,我不会得到这种行为。我有一个非常简单的‘作家’程序,只是不断传输和一个非常简单的‘切换’程序,切换RTS每秒一次。这些因素加在一起,似乎会在60秒内引发问题。我已经向设备制造商提出了一个问题,但他们还没有多少时间作出回应。编译器是mingw32,Qt安装程序中包含的Qt4.8.1 (gcc 4.4.0)
首先,我想知道,如果有人能想到什么,我可以做什么来触发这种行为。我什么都想不出来,但总有一些事情我不知道。
其次,我附上了作者和切换测试程序。如果有人能发现可能触发程序的问题,我很想听听。我有很多困难,认为有一个驱动程序错误(特别是从某种成熟的东西,如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;
}
发布于 2013-05-08 13:55:03
我还没有从FTDI那里得到一个很好的答案,但对于任何处理这个问题的人,我都有以下建议:
1)考虑切换到非FTDI usb-串行转换器。这是我的公司所做的,但这肯定不是每个人的选择(我们把芯片放在我们自己的产品中)。我们现在使用的是硅实验室芯片,但我认为还有一两家供应商。
2)每个汉斯过路人在评论-重新考虑使用RTS/CTS信号。如果写操作不会因为阻塞而失败,那么您就不应该触发这个错误。
3)将所有写设置为无限超时。同样,不会因为阻塞而失败,也不会触发错误。当然,这可能并不适合所有的应用程序。
请注意,如果采用策略3,如果使用重叠IO写入,那么CancelIo和它的新的近亲CancelIoEx可以在必要时用来消除写入。我没有尝试过这样做,但我怀疑这种取消也可能导致触发此错误。如果它们只是在关闭端口时才使用,那么您就可以逃脱它,即使它们确实触发了错误。
发布于 2015-11-19 16:35:33
如果其他人仍然看到这种情况--将您的FTDI驱动程序更新为2.8.30.0或更高版本,因为这是由早期版本的FTDI驱动程序中的驱动程序错误引起的。
https://stackoverflow.com/questions/13805555
复制相似问题