最近これと言って個人的に熱いネタもなく、秋月でぶらりしていた時になんとなく FT232HL を購入しました。このモジュールの特徴である MPSSE を使って SPI 通信をやってみましたので、ご紹介します。
浮気
この前確かこの記事とこの記事に堂々と FT232RL は最強だ!という記事を書きましたね。あれは嘘だ((
実は前々から気になっていたので買ってしまいました。ややお高めで 1,200円。以下が商品ページのリンクです。
このモジュール、何が良いかというと、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 さんが解釈して操作を行ってくれるらしいです。
実際に 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 で使えたらいいなぁ。