FT232Hの方が最強かもしれない(1) – シリアル、SPI通信編

最近これと言って個人的に熱いネタもなく、秋月でぶらりしていた時になんとなく FT232HL を購入しました。このモジュールの特徴である MPSSE を使って SPI 通信をやってみましたので、ご紹介します。

浮気

この前確かこの記事この記事に堂々と FT232RL は最強だ!という記事を書きましたね。あれは嘘だ((

実は前々から気になっていたので買ってしまいました。ややお高めで 1,200円。以下が商品ページのリンクです。

電子部品,通販,販売,半導体,IC,マイコン,電子工作FT232HL ハイスピードUSBシリアル変換モジュール秋月電子通商 電子部品の通販・販売

このモジュール、何が良いかというと、MPSSE によって高速なシリアル通信が出来るということですね。この MPSSE は高速なだけでなく、SPI や I2C、はたまた JTAG なんかもサポートしているのでアドがデカイです。つまり適切な MPSSE コマンドを叩くだけで、例えば SPI でしたらクロックに合わせてデータ送出等をやってくれるというわけです。いいね。ただ、I2C は手続きを書かなきゃいけないのでつらそうですが。
なんでこのモジュールに浮気をしてみることにします。

電源周り

こちらは FT2232D にあった、3.3V の供給がないと言う問題は発生しません。基板上にジャンパが2つあるのですが、内1つをショートさせると(どちらかは説明書をご覧ください)信号及び Vcc が 3.3V になります。逆にオープンにすると IC の方に電圧が入力されませんので、5V 端子からジャンパケーブル等で VIO に入れてやる、と言った方法を取ります。
どうやら VIO は 3.3V 限定のようです。出力される電圧レベルは常に 3.3V。5V トレラントなのですが、出力を 5V レベルにしたい場合は別途レベル変換回路に通す必要がありそう。むむ。

使ってて気づいたんですが、ちょっと IC が熱を持つのが気になります。消費電力大きいんですかねぇ。通常のシリアル通信でも温度は変わらないみたいなんですが、そこら辺どうなんでしょう。

UART やってみる

それではまず普通の仮想 COM ポートとしての通信です。とはいっても何もせずとも TXD を RXD を繋いでやるだけで OK です。僕は TXD(ADBUS0) と RXD(ADBUS1) をショートさせてループバックしてみたんですが、普通に動作しています。説明書によると、その他 RTS# や RI# 等もあるので、FT232R の代わりにもなりますね。

MPSSE の始め方

こちらの機能は BitBang モードで利用可能なものです。ですので libftd2xx を用いるということになります。僕は C言語で記述しました。
こちらのためのアプリケーションノートはここ(AN108)とかここ(AN180)で見つかりました。なおこの英語は節区切りのカンマがない British な英語なので丁寧に読んで下さいね。僕は British English 好きですよ。

どうやら MPSSE モードになった場合、通常の FT_Write がそのままコマンド発行関数になるということらしいです。例えばデータを送るコマンドを引数とともに発行すると、MPSSE さんが解釈して操作を行ってくれるらしいです。それなんて CPU アーキテクチャ

では実際に FT232HL デバイスを初期化する方法ですが、こんな記載があります。

ですので D2XX ライブラリの関数をひとまず以下のように呼び出しました。#include <ftd2xx.h> はしといてくださいね。

if( FT_Open(0, &ft) != FT_OK ) {
    fprintf(stderr, "Error\n");
    return 1;
}

FT_SetBitMode(ft, 0x00, FT_BITMODE_RESET);
FT_SetBitMode(ft, (UCHAR)0xFF, FT_BITMODE_MPSSE); // MPSSE, all output
FT_SetLatencyTimer(ft, 16);
FT_SetTimeouts(ft, 1000, 1000);

これでデバイスは MPSSE モードに入りました。これ以降 FT_Write および FT_Read でコマンド発行、読み出しができます。

今回は FT_Write の方を使いますが、いちいち引数指定が面倒だったので、自作関数を作りました。

FT_STATUS write_mpsse(int num, ...) {
    FT_STATUS ret;
    va_list args;
    UCHAR outBuffer[10];
    ULONG written;
    va_start(args, num);
    for(int i=0; i<num; i++) {
        outBuffer[i] = va_arg(args, int);
    }
    ret = FT_Write(ft, outBuffer, num, &written);
    va_end(args);
    return ret;
}

可変長引数を使っています。最初の引数には総データ数を渡します。第2引数以降はコマンド及びコマンド引数です。これを用いて MCP4922 という SPI を用いた DAコンバータ を動かしてみました。波形はのこぎり波です。

uint16_t dat = 0;
while(1){
    write_mpsse(3, 0x80, 0x00, 0x0B); // set pin direction SK, DO, CS for output
    write_mpsse(5, 0x10, 2, 0, 0x20 | (dat >> 8), dat & 0xFF);
    write_mpsse(1, 0x87); // set values immediately
    write_mpsse(3, 0x80, 0x08, 0x0B); // set pin direction SK, DO, CS for output
    dat += 128;
    if( dat >= 0x1000 ) {
        dat = 0;
    }
}

これでできます。コンパイル時には -lftd2xx をお忘れなく。実際の動作の様子は写真をご覧ください。

本記事で使ったコードです。

コマンド発行手順や flush のタイミング等癖がありましたが(慣れてないだけ?)、一度やり方が分かってしまえばさくっと SPI 通信できちゃいましたね。驚きです。
近いうちにavrNerdをこの MPSSE 用にスッキリと書き直してみたいとは思います。

I2C は面倒くさそうなのでパスしたいお気持ちです。気が向いたらやるということで。(え?気が向かないまま「その1」で終わっている記事が多い?知りませんねぇ)
というわけで JTAG の調査をしたいと思います。OpenOCD で使えたらいいなぁ。