オープン・リード・ライト・クローズで見るAndroid FTDI公式 ドライバ


どんなデバイスプログラミングでも共通して言えることは、オープン・クローズ・リード・ライトさえしっかり押さえておけばOK。
ということで、公式のサンプルコードを元に、UARTの部分だけを抜き出したシンプルなコードを作りました。


バイスを動かすにはとにもかくにも動くソースというのは重要です。
こいつを叩き台にして肉付けしていけばひとまず動くアプリは出来るでしょう。


全てのコードはgithubに上げています。
https://github.com/ksksue/Android-FTDI-UART-Sample/

javaソースコードはこれ
https://github.com/ksksue/Android-FTDI-UART-Sample/blob/master/src/com/ksksue/app/ftdi_uart/MainActivity.java
Nexus 7で動作確認をしています。


テスト環境はこんなかんじで
FTDIチップのRxDとTxDを繋いでループバックさせています。


宣言部分

    private static D2xxManager ftD2xx = null;
    private FT_Device ftDev;

    static final int READBUF_SIZE  = 256;
    byte[] rbuf  = new byte[READBUF_SIZE];

使うのは D2xxManagerとFT_Deviceです。
D2xxManagerはUSBを介したFTDIデバイスとの接続を管理するクラス
FT_DeviceはFTDIデバイスと通信させるためのクラス

あと、readするときに一時的にバッファリングするbyte配列を用意しておきます。

        try {
            ftD2xx = D2xxManager.getInstance(this);
        } catch (D2xxManager.D2xxException ex) {
            Log.e(TAG,ex.toString());
        }

おまじないのようにD2xxManagerのインスタンスを作っておきます。



オープン
オープンするところは、インデックス(要はUSBを挿した順番)で検索してオープンさせるようにしました。
インデックスでオープンさせる方法以外にも
特定のSerialID(openBySerialNumber)で検索する方法もあるようなので
Hubを介しての接続先指定にはSerialID検索が良いかと。

        int devCount = 0;
        devCount = ftD2xx.createDeviceInfoList(this);

        Log.d(TAG, "Device number : "+ Integer.toString(devCount));

        D2xxManager.FtDeviceInfoListNode[] deviceList = new D2xxManager.FtDeviceInfoListNode[devCount];
        ftD2xx.getDeviceInfoList(devCount, deviceList);

        if(devCount <= 0) {
            return;
        }

USBのデバイスが1つもつながっていなかったらそのままreturnです。
下に続きます。

        if(ftDev == null) {
            ftDev = ftD2xx.openByIndex(this, 0);
        } else {
            synchronized (ftDev) {
                ftDev = ftD2xx.openByIndex(this, 0);
            }
        }

        if(ftDev.isOpen()) {
            if(mThreadIsStopped) {
                updateView(true);
                SetConfig(9600, (byte)8, (byte)1, (byte)0, (byte)0);
                ftDev.purge((byte) (D2xxManager.FT_PURGE_TX | D2xxManager.FT_PURGE_RX));
                ftDev.restartInTask();
                new Thread(mLoop).start();
            }
        }

ftD2xx.openByIndex(this, 0) でオープンします。このコードでは1つのデバイスを繋ぐことを想定しているため
インデックスは0固定にしています。

オープンしたら、FTDIデバイスの初期化処理にうつります。
SetConfigメソッドでまとめてボーレート 9600bps, データビット 8bit, ストップビット 1bit, パリティなし、フロー制御なし
にしていますが、SetConfigメソッド内では

        ftDev.setBaudRate(baud);
        ftDev.setDataCharacteristics(dataBits, stopBits, parity);
        ftDev.setFlowControl(flowCtrlSetting, (byte) 0x0b, (byte) 0x0d);

をそれぞれ呼んでます。

ftDev.purge((byte) (D2xxManager.FT_PURGE_TX | D2xxManager.FT_PURGE_RX));

でチップ内の送受信バッファをクリアしておきます。

ftDev.restartInTask();

でreadタスクがスタートします。
readタスクはおそらくFT_Deviceクラス内のスレッドで走っており
バックグランドタスクとして常にreadされたデータをバッファリングしています。


リード

                synchronized (ftDev) {
                    readSize = ftDev.getQueueStatus();
                    if(readSize>0) {
                        mReadSize = readSize;
                        if(mReadSize > READBUF_SIZE) {
                            mReadSize = READBUF_SIZE;
                        }
                        ftDev.read(rbuf,mReadSize);
                        ...
                    }
                }

readはwriteとftDevを共有するためsynchronizedで囲みます。
ftDev.getQueueStatus()はチップから読み出されたデータのbyte数を返します。
(このあたりArduinoのSerial.avilable()と同じですね)

一応、rbufのサイズ以上コピーされないようにREADBUF_SIZEでリミットをかけておきます。

ftDev.read(rbuf,mReadSize) でreadタスクのバッファからrbufへデータをコピーします。
以下省略しましたが、このあとrbufの内容をTextViewへ表示しています。


ライト

        ftDev.setLatencyTimer((byte)16);

        String writeString = etWrite.getText().toString();
        byte[] writeByte = writeString.getBytes();
        ftDev.write(writeByte, writeString.length());

ftDev.setLatencyTimer*1;で書込み。
書き込むためにString => byte[] 変換をしています。



クローズ

        if(ftDev != null) {
            ftDev.close();
        }

これでクローズします。
特筆すべきことはないです。



参考資料
D2XX for Android ライブラリとデモソース
http://www.ftdichip.com/Support/SoftwareExamples/Android/TN_147_Java_D2xx_for_Android_Demo_Source.zip

公式 D2XX for Android API Manual
http://www.ftdichip.com/Support/Documents/AppNotes/AN_233_Java_D2xx_for_Android_API_User_Manual.pdf


オススメ書籍

*1:byte)16);で適当にレイテンシを持たせておきます。 ftDev.write(writeByte, writeString.length(