8

FT2232H USB/RS232 コンバーター経​​由でデバイスと通信するアプリケーションを作成しています。通信には、FTDI Web サイトの FTD2XX_NET.dll ライブラリを使用しています。
私は2つのスレッドを使用しています:

  • 最初のスレッドは、デバイスからデータを継続的に読み取ります
  • 2 番目のスレッドは、Windows フォーム アプリケーションのメイン スレッド

    です。レシーバーのスレッドの実行中にデバイスにデータを書き込もうとすると、問題が発生します。メイン スレッドは単に ftdiDevice.Write 関数でハングアップします。

    同時に 1 つのスレッドのみが読み取り/書き込み機能を使用できるように、両方のスレッドを同期しようとしましたが、役に立ちませんでした。

    以下のコードは、通信を担当します。以下の関数は FtdiPort クラスのメソッドであることに注意してください。

    受信者のスレッド

    
            private void receiverLoop()
            {
                if (this.DataReceivedHandler == null)
                {
                    throw new BackendException("dataReceived delegate is not set");
                }
    
                FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
                byte[] readBytes = new byte[this.ReadBufferSize];
    
                while (true)
                {
                    lock (FtdiPort.threadLocker)
                    {
                        UInt32 numBytesRead = 0;
    
                        ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
    
                        if (ftStatus == FTDI.FT_STATUS.FT_OK)
                        {
                            this.DataReceivedHandler(readBytes, numBytesRead);
                        }
                        else
                        {
                            Trace.WriteLine(String.Format("Couldn't read data from ftdi: status {0}", ftStatus));
                            Thread.Sleep(10);
                        }                    
                    }
                    Thread.Sleep(this.RXThreadDelay);
    
                }
            }
    


    メインスレッドから呼び出された書き込み関数

        public void Write(byte[] data, int length)
        {
            if (this.IsOpened)
            {
                uint i = 0;
    
                lock (FtdiPort.threadLocker)
                {
                    this.ftdiDevice.Write(data, length, ref i);
                }
    
                Thread.Sleep(1);
                if (i != (int)length)
                {
                    throw new BackendException("Couldnt send all data");
                }
            }
            else
            {
                throw new BackendException("Port is closed");
            }
        }
    


    2 つのスレッドの同期に使用されるオブジェクト

    
    static Object threadLocker = new Object();
    

    受信者のスレッドを開始するメソッド

    
            private void startReceiver()
            {
                if (this.DataReceivedHandler == null)
                {
                    return;
                }
                if (this.IsOpened == false)
                {
                    throw new BackendException("Trying to start listening for raw data while disconnected");
                }
                this.receiverThread = new Thread(this.receiverLoop);
                //this.receiverThread.Name = "protocolListener";
                this.receiverThread.IsBackground = true;
                this.receiverThread.Start();
            }
    
    次の行にコメントすると、ftdiDevice.Write 関数はハングアップしません。

    ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
  • 4

    4 に答える 4

    6

    別の方法は、FTDI からのイベント通知メカニズムを使用することです。この方法では、データを読み取るためにブロッキング スレッドは必要ありません。

    public FTDISample()
    {
        private AutoResetEvent receivedDataEvent;
        private BackgroundWorker dataReceivedHandler;
        private FTDI ftdi;
    
        public FTDISample(string serialNumber){
            ftdi = new FTDI();
            FTDI.FT_STATUS status = ftdi.OpenBySerialNumber(serialNumber);
            receivedDataEvent = new AutoResetEvent(false);
            status = mFTDI.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR, receivedDataEvent);
            dataReceivedHandler = new BackgroundWorker();
            dataReceivedHandler.DoWork += ReadData;
            if (!dataReceivedHandler.IsBusy)
            {
                dataReceivedHandler.RunWorkerAsync();
            }
        }
    
        private void ReadData(object pSender, DoWorkEventArgs pEventArgs)
        {
            UInt32 nrOfBytesAvailable = 0;
            while (true)
            {
                // wait until event is fired
                this.receivedDataEvent.WaitOne();
    
                // try to recieve data now
                FTDI.FT_STATUS status = ftdi.GetRxBytesAvailable(ref nrOfBytesAvailable);
                if (status != FTDI.FT_STATUS.FT_OK)
                {
                    break;
                }
                if (nrOfBytesAvailable > 0)
                {
                    byte[] readData = new byte[nrOfBytesAvailable];
                    UInt32 numBytesRead = 0;
                    status = mFTDI.Read(readData, nrOfBytesAvailable, ref numBytesRead);
    
                    // invoke your own event handler for data received...
                    //InvokeCharacterReceivedEvent(fParsedData);
                }
            }
        }
    
        public bool Write(string data)
        {
            UInt32 numBytesWritten = 0;
            ASCIIEncoding enconding = new ASCIIEncoding();
            byte[] bytes = enconding.GetBytes(data);
            FTDI.FT_STATUS status = ftdi.Write(bytes, bytes.Length, ref numBytesWritten);
            if (status != FTDI.FT_STATUS.FT_OK)
            {
                Debug.WriteLine("FTDI Write Status ERROR: " + status);
                return false;
            }
            if (numBytesWritten < data.Length)
            {
                Debug.WriteLine("FTDI Write Length ERROR: " + status + " length " + data.Length +
                                " written " + numBytesWritten);
                return false;
            }
            return true;
        }
    
    于 2010-04-16T09:17:37.743 に答える
    4

    いくつかのこと:

    1. Read 呼び出しがブロックされているかどうかを確認します。その場合、Read がブロックされて応答を待機している間は、Write を呼び出すことができない可能性があります。API ドキュメントには、これに関する詳細が記載されている場合があります。

    2. 一部の API は、アクセスを同期している場合でも、複数のスレッドをうまくサポートしていません。その場合は、書き込みコマンドを通信スレッドに委譲する設計を使用できます。過去にこのパターンを使用したときは、通常、書き込みたい情報を含むコマンド クラスのようなものをキューに入れ、スレッド シグナル クラスを使用して、呼び出し元の「コマンド」メソッドをブロックまたは提供できるようにします。非同期通知。

    于 2010-03-13T16:59:14.093 に答える
    2

    より詳細な API ドキュメントを見つけました。実際、readTimeout 値を 0 以外に設定しない限り、ftdiDevice.read 関数はブロックされています。このタイムアウト値を設定すると、問題が解決しました。
    早速の対応、ありがとうございました。
    よろしく

    于 2010-03-13T17:34:29.227 に答える
    1

    APIを確認すると、ドライバーがCOMポートをエミュレートできるように見えます。GetComPort()メソッドが「COMx」文字列を返すのがわかります。これにより、System.IO.Ports.SerialPortクラスを使用できる可能性が高くなります。ラッパーが行おうとしていることをすでに実行しているので、DataReceivedイベントをサポートします。試してみる価値。

    于 2010-03-13T17:50:35.197 に答える