1

バグ修正の更新: 2013 年 6 月の時点で、FTDI はバグが本物であることを私に認めました。その後、問題を修正する新しいバージョンのドライバー (2.8.30.0、日付は 2013 年 7 月 12 日) をリリースしました。ドライバーは 2013 年 8 月 1 日頃に WHQL を介して作成され、現時点では Windows Update から入手できます。

同じテストコードを実行して再テストしましたが、新しいドライバーで問題を再現できないため、現時点での修正は「ドライバーのアップグレード」のようです。

元の質問: FTDI FT2232D チップに基づく 8 ポート USB シリアル デバイス (VsCOM から) を持っています。ポートの 1 つから特定の設定で送信し、ハードウェア ハンドシェイクを使用してもう一方の端からのデータ フローを停止および開始すると、次の 2 つの症状が発生します。

1) 出力データが時々ゴミになる。NUL 文字があり、考えられるほぼすべてのランダムなものがあります。

2) WriteFile 呼び出しは、書き込みを要求した数よりも大きいバイト数を返すことがあります。それはタイプミスではありません。30 バイトの送信を要求したところ、送信されたバイト数が 8192 に戻ってきました (そして、電話をかける前に、送信された数を 0 にクリアします)。

関連する事実: FTDI ドライバー 2.8.24.0 を使用しています。これは、現時点で最新です。シリアル ポートの設定は、19200、7 データ ビット、奇数パリティ、1 ストップ ビットです。別の FTDI ベースのシリアル デバイス、今回はシングル ポートのシリアル デバイスでも同じ動作が発生します。同じタイプの別の 8 ポート デバイスでも同じ動作が得られます。内蔵シリアル ポート (COM1) で送信する場合、この動作は発生しません。継続的に送信するだけの非常に単純な「ライター」プログラムと、RTSを1秒に1回切り替える非常に単純な「トグル」プログラムがあります。これらを合わせると、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;
}
4

2 に答える 2

1

FTDI からはまだ良い回答が得られていませんが、この問題に対処しているすべての人に次の提案があります。

1) FTDI 以外の USB シリアル コンバーターへの切り替えを検討してください。これは私の会社が行ったことですが、確かにこれはすべての人にとっての選択肢ではありません (私たちは自社製品にチップを搭載しています)。現在は Silicon Labs のチップを使用していますが、他のベンダーも 1 つまたは 2 つあると思います。

2) コメントの Hans Passant ごとに - RTS/CTS シグナリングの使用を再考してください。ブロッキングが原因で書き込みが失敗しない場合は、このバグをトリガーしないでください。

3) すべての書き込みを無限タイムアウトに設定します。繰り返しますが、ブロックによる失敗はなく、バグのトリガーもありません。もちろん、これはすべてのアプリケーションに適しているわけではありません。

戦略 3 を追求する場合、オーバーラップ IO が書き込みに使用される場合、必要に応じて、 CancelIoとその新しいいとこであるCancelIoExを使用して書き込みを停止できることに注意してください。私はそうしようとしたことはありませんが、そのようなキャンセルもこのバグを引き起こす可能性があると思います. とにかくポートを閉じるときにのみ使用された場合は、バグを引き起こしたとしても、それを回避できる可能性があります.

于 2013-05-08T13:55:03.900 に答える
0

他の誰かがまだこれを見ている場合は、FTDI ドライバーの以前のバージョンのドライバーのバグが原因であるため、FTDI ドライバーを 2.8.30.0 以降に更新してください。

于 2015-11-19T16:35:33.490 に答える