【问题标题】:Cause of serial port transmitting bad data and WriteFile return wrong number of bytes written?串口传输坏数据和WriteFile返回错误写入字节数的原因?
【发布时间】:2015-11-20 00:35:33
【问题描述】:

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

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

原来的问题: 我有一个基于 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;
}

切换程序:

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

【问题讨论】:

  • 我出于一种爱好对 Microchip 芯片进行编程,发现其中一个具有板载串行端口的 Microchip 编程器无法使用 USB-RS232 电缆,因为 USB 端口产生的电压不在正确的范围内对于真正的 rs232 规范。这可能不是你的问题,但它是众所周知的问题。
  • 特别是我的问题是因为与握手有关 - 我从记忆中相信这与 USB 可以读取/写入握手线的速度太快/太慢导致'抖动'。我建议在electronics.stackexchange 上与@stevenh 交谈……他是神。 Olin Lathrop 在这类问题上也很出色
  • 众所周知,FTDI 驱动程序有问题。不要寄希望于修复,重新考虑你的握手策略。
  • @HansPassant - 哦哦。我正在模拟其他预先存在的设备,因此重新考虑我的策略不是一种选择。我没有找到对其他 FTDI Windows 驱动程序错误的任何引用 - 你对其他错误报告有任何引用吗?
  • 请注意,我正在与 FTDI 就这个问题进行沟通,同时也在探索替代方案(其中相对较少)。

标签: windows serial-port usb


【解决方案1】:

我还没有从 FTDI 得到好的答案,但我有以下建议给任何处理这个问题的人:

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

2) cmets 中的 Per Hans Passant - 重新考虑 RTS/CTS 信令的使用。如果写入没有因为阻塞而失败,那么你不应该触发这个错误。

3) 将所有写入设置为无限超时。同样,不会因阻塞而失败,也不会触发错误。当然,这可能并不适用于所有应用程序。

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

【讨论】:

  • 我在 windows ce 5.0 上的 D2XX 驱动程序有同样的问题。当我在一个 FT_Write() 命令中发送超过 4 个字节时,一些传输的字节不正确。当然,您在传输的 4 字节数据块之间会出现小间隙,但如果这没有问题,这可能是一个(临时)解决方案......
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-21
    • 1970-01-01
    • 2013-09-13
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    相关资源
    最近更新 更多