今回は、Iduino(二代目)スタータキットに入っていたLED表示装置の中から、8×8[ドット]マトリクス表示器(以下単に8×8 LED)を使ってみようと思います。
ドンっ、と8×8 LEDと、脇にコントローラ(マーク:MAX7219)が見えます。有名なチップなのでしょう。MAX7219で検索したら、すぐにデータシートは見つかりました。
ようやく、規格物のシリアルインターフェイス(SPI)でつながるモジュールにお目にかかることができました。ポートを直接叩かなくても、通信できそうです。
ELEGOOスタータキットが到着したとき、「入っていないモジュールのデータシートもCD-ROMに収録されていた」話をしました(「第2回 Arduino初心者スタータキット到着」を参照ください)。この中に、「8×8マトリクスディスプレイモジュール」のデータシートがありましたので、こちらも参照しておきました。すると、なんと!消費電流が320mA(MAX 2A)ですと!こんなもの、USBで駆動できるのか??
いや、普通に考えて無理でしょう。USBから引ける電流はMAX 500mAですから。500mAから、Arduinoの電流も使っているし……。と、言うことで、こちら(↓)の出番ですね。
Iduinoスタータキットに入っていた、ACアダプタ(9V)と、ブレッドボード用電源モジュールです。8×8 LEDの電源は、Arduino(USB)ではなく、こちらの電源モジュール(ACアダプタ)で供給します。
今度は、ArduinoのSPIですね。ATmega328が持っている、SPIユニットを使いましょうよ。Arduino HP ⇒ REFERENCE ⇒ LIBRARIESから、SPIの項目が見えますので、これを使ってみます。
#include <SPI.h>
void setup (void)
{
// initialize
SPI.begin ();
SPI.beginTransaction (SPISettings (10000000, MSBFIRST, SPI_MODE0));
// transfer data
digitalWrite (SS, LOW);
SPI.transfer16 (0x0b07);
digitalWrite (SS, HIGH); // 一旦HIGHに戻して、
digitalWrite (SS, LOW); // 改めてLOWにする。
SPI.transfer16 (0x0c01);
digitalWrite (SS, HIGH);
}
void loop (void)
{
// delay
delay (250);
}
何でも良いですが、「SPI.hを#includeしなさいね」っていうのは、どこに書いてあるのでしょう?REFERENCE見ましたが、見つからなかった……。どなたかのサンプルで見て、#includeしました。
※Arduino IDEから「スケッチメニュー ⇒ ライブラリをインクルード」と進み、使いたいライブラリを選択すると、プログラムの先頭に、必要なヘッダファイルの#includeが自動的に追加されるようです。あまり好みではないですが、Arduinoのお作法は、わかりました。
MAX7219データシートによると、最大通信周波数は10MHz(SPISettings()の第一パラメータ、10000000)、MSBファースト、SPI_MODE0なので、そのように設定しています。が、ATmega328のSPIは、最大周波数が動作周波数(16MHz)の1/2ですので、最大8MHzです。SPI.beginTransaction()は、指定した周波数に一番近い内輪の周波数を選んでくれる仕様みたいです。よって、上プログラムの設定で、おそらく、SPIのクロック周波数は8MHzが選択されるものと思われます。つまり、接続する相手側の最大周波数に合わせておけば、ライブラリが最適な周波数を選んでくれるということです。
また、データシートに「MAX7219内のシフトレジスタにシフトインされたデータは、CS端子の立ち上がりエッジでラッチされる」と、ありますので、上プログラムの様に、SPI.transfer16()を挟むようにSS端子をLOWレベルにする制御が必要です。※SS端子(UNOではD13)は8×8 LEDのCE端子に接続します。
連続してSPI.transfer16()する場合でも、一旦SS端子をHIGHに戻さないと、MAX7219はコマンドをラッチしてくれません(つまり、無視される)。
SPIというのは、出力したクロックSCKに同期して、MOSI端子にデータを送信します。SPIは双方向(フルデュプレックス、全二重)の通信方式なので、同時に、MISO端子からデータを受信します。従って、SPI.transfer16()は、カッコ内のデータを送信すると同時に、16ビットのデータを受信して値を返すので、普通は、uint dat=SPI.transfer16(0x1234);などの様な使い方をします。
今回の8×8 LEDは、データを送信して来ないため、受信データはありません。MISOも接続しません(接続先が無い)。
ちなみに、MOSI、MISOのネーミング、解り易くて好きです。MOSI=Master Output Slave Input、MISOはその逆です。「マスタ(Master)からすると出力(Output)で、通信相手(Slave)から見ると入力(Input)となる信号ですよ」と言う意味ですね。クロックを出力する方がマスタですので、この場合はArduinoがマスタです。
SPIは、「バス」形式のシリアル通信手法で、1つのマスタに複数のスレーブがぶら下がることができます。スレーブ毎に、SPISettings(10000000, MSBFIRST, SPI_MODE0)の値が変わる場合があるので、その場合は、setup(void)の中で実行可能なのは、SPI.begin()だけで、SPI.transfer()する時に、SPI.beginTransaction()とSPI.endTransaction()で挟み込む必要がある様です。今回は、8×8 LEDしかつながりませんので、setup(void)の中でSPI.beginTransaction()を実行してしまいます。
あとは、MAX7219のデータシートにあるように、アドレス(8ビット)+書き込みデータ(8ビット)の計16ビットを送信するだけです。この要領で、所望のレジスタに所望の値を書き込んで制御します。
8×8 LEDの電源をACアダプタを接続した電源モジュールから供給すること、8×8 LEDのDIN、CS、CLKをそれぞれ、ArduinoのMOSI(D11)、SS(D10)、SCK(D13)端子に接続すること、これだけです。おっと、Arduinoと電源モジュールのGNDをつなぐこともお忘れなく。
Arduino | 接続 | 8×8 LED | 接続 | 電源モジュール |
D11(MOSI) | → | DIN | ||
D10(SS) | → | CS | ||
D13(SCK) | → | CLK | ||
VCC | ← | 5V | ||
GND | ⇔ | GND | ⇔ | GND |
例によって、シリアルモニタから制御可能なプログラムにしておきます。以下、実装したコマンドです。
電源投入時の状態では、MAX7219の初期設定をいくつか通り抜けないと表示されないので、初期化を実行する【demo】コマンドを最初に実行するのが楽です。手動で初期化するには、下の実行サンプルを実行することになります。
プログラム:matrix8x8.ino
実行サンプル:【 】内はシリアルモニタからの送信データ
【who】
OK: matrix8x8 ver.1.00
【wrt,b,7】 …… 全rowスキャン
OK: B=7
【wrt,c,1】 …… シャットダウンモード解除
OK: C=1
【dsp,0,0】
OK: 0,0
これでもし、表示がおかしかった場合には、もしかしたらBCD復号モードが設定されてしまっているかもしれませんので、以下も叩いてみてください。
【wrt,9,0】 …… 復号無しモード
OK: 9=0
イジメてみます。確認できませんけど……。前節の実行サンプルからの続きです。【who】コマンドの次から、しばらくMAX7219の初期設定が続いていますので、この部分が実行されている前提で以下サンプルとなります。
実行サンプル:【 】内はシリアルモニタからの送信データ
【fnt,ffffffffffffffff】 …… 全LEDを点灯
OK: ff,ff,ff,ff,ff,ff,ff,ff
【dsp,0,0】
OK: 0, 0
【wrt,a,4】 …… 最大輝度の9/32に設定
OK: A=4
いや、最大輝度にしようかと思いましたが、止めました。電源モジュールの最大駆動電流は、<700mAと言うスペックでしたので、本当に8×8 LEDのデータシートにあったMAX 2Aを流されると、電源モジュールですらスペックオーバです。MAX 2Aの9/32なら、〜560mAでしょ?っていう、ざっくり計算ですので、ご注意ください。
テスタなどで、電流を計測可能な環境でない限り、最低輝度設定で使うのが無難です。晴れた日の屋外などで使わない限り、最低輝度でも十分視認可能です。
まさかとは思いますが、USBから電源取ってませんよね?筆者はやったことはありませんし、やってみようとも思いませんが、USBからもらえる電源は、最大500mAと決まっています。ArduinoのUSB電源には、500mAのヒューズが入っているので、消費電流が500mAを超えると、遮断されてしまうはずです。
くれぐれも、USB電源で8×8 LEDを駆動しないようにしてください。
段々と、パタンが見えてきました。1)Arduino側のプログラムをシリアルモニタで制御可能に作る、2)PCでOlive+のスクリプトを使ってArduinoを制御する、こんなパタン。
Olive+には、グラフィック描画機能がありますので、グラフィック画面に、フォントの高さが8ビットに収まるように文字列を描画します。描画した画像データを拾いながら、8×8[ドット]のデータを生成し、【fnt】コマンドでArduinoへコンパイル&書き込みします。【dsp】コマンドで8×8 LEDの表示に反映させます。
この作業を、1ドットづつずらしながら、ひたすら繰り返すと、8×8[ドット]の窓に、文字列が流れるように表示されます。
Olive+のスクリプト:matrix8x8.olv
実行するには、PCにOlive+のインストールが必要です。最新版をダウンロードしてインストールしておいてください。
しかし、ベクターフォント、ひどいな……。特に、英数字等ASCII 7ビットコード範囲がダメ、高さ8ドット程度だと汚すぎ。8ビットの綺麗なドットフォントが無いとダメかも?意外と、ひらがな・カタカナは読めます。でも、漢字は無理。英数字は、少しフォントサイズを大きくして調整してあります。
スクロールスピードをスクリプト上の方で宣言している、dpsと言う変数値で制御しようとしているのですが、最大で〜11dot/sくらいまでしか上がりません……。Serialの通信速度で律速しているようです。Arduino側で「Serial.begin(38400);」にして、スクリプト(matrix8x8.olv)の方も「serialmon open, "COM3", baud=38400;」と合わせると、24dot/sくらいまで上がるのを確認しました。
ただ、表示が読み切れないほど速くスクロールしても無意味なので、〜11dot/sくらいでちょうど良いとも言えます。
スクリプトの変数dpsを設定すると、遅くすることはできるので、お好みのスピードに設定してください。
筆者の環境には、テスタすらないので、電流が測れません(1^^;)。ので、本当にどの程度流れているのかわかりませんが、どうなのでしょうね?念のため、最大消費電流スペックに合わせて外部電源を使いましたが、一番低輝度な状態なら、それほど電流は流れない気もします。