串口传输坏数据和WriteFile返回错误字节数的原因是什么?

时间:2012-12-10 16:59:27

标签: windows serial-port usb

修正错误: 截至2013年6月,FTDI确实向我承认错误是真实的。他们已经发布了新版本的驱动程序(2.8.30.0,日期为2013年7月12日),解决了这个问题。驱动程序在2013年8月1日左右通过WHQL实现,目前可通过Windows Update获得。

我已经重新测试了运行相同的测试代码并且无法使用新驱动程序重现问题,因此目前修复程序似乎是“升级驱动程序”。

原始问题: 我有一个基于FTDI FT2232D芯片的8端口USB串行设备(来自VsCOM)。当我从其中一个端口发送某些设置时,我使用硬件握手停止并从另一端启动数据流,我有两个症状:

1)输出数据有时会变成垃圾。会有NUL角色,你可以想到任何随机的东西。

2)WriteFile调用有时会返回多个字节 GREATER ,而不是我要求它写的数字。这不是一个错字。我要求传输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;
}

Toggler计划:

#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;
}

2 个答案:

答案 0 :(得分:1)

我对FTDI没有一个好的答案,但对于处理这个问题的人我有以下建议:

1)考虑切换到非FTDI usb-serial转换器。这就是我的公司所做的,但当然这不是每个人的选择(我们将芯片放在我们自己的产品中)。我们现在正在使用Silicon Labs芯片,但我认为还有一两个其他供应商。

2)评论中每个Hans Passant - 重新考虑使用RTS / CTS信令。如果写入因阻塞而失败,则不应触发此错误。

3)将所有写入设置为无限超时。再次,没有因为阻塞而失败,没有触发错误。当然,这可能不适合所有应用程序。

请注意,如果采用策略#3,如果使用重叠IO进行写入,那么CancelIo及其较新的表兄CancelIoEx可用于在必要时终止写入。我没有尝试这样做,但我怀疑这样的取消也可能导致触发此错误。如果它们仅在关闭端口时使用,那么它可能是你可以逃脱它,即使它们确实触发了这个bug。

答案 1 :(得分:0)

如果其他人仍然看到这一点 - 将FTDI驱动程序更新为2.8.30.0或更高版本,因为这是由早期版本的FTDI驱动程序中的驱动程序错误引起的。