ArduinoでLED Matrixモジュールを複数使う

Arduino
DSC_2409.JPG

複数の LED Matrix モジュールを接続する

カスケード接続

LEDドライバMAX7219 の特徴の1つに、DOUT端子の存在があります。MAX7219は1つで8×8=64個のLEDを制御できますが、1つめのMAX7219のDOUT端子を2つめのMAX7219のDIN端子に、2つめのMAX7219のDOUT端子を3つめのMAX7219のDIN端子に…と接続することによって、複数のMAX7219を連携させて動作させることができます。

このような接続方法を、順にデータが流れていく様子からカスケード接続といいます(cascade、元の意味は『小さな滝』)。

または、デバイスが一列に並んでいる様子からデイジーチェーン接続ともいいます(daisy chain、元の意味は『デイジー(ヒナギク)で作った花飾り』ですが、その形状から日本語の『数珠つなぎ』のような意味になっています)。

『カスケード』=『デイジーチェーン』ではありません。デイジーチェーンは必ずデバイスが1列にならびますが、カスケードは途中で分岐する場合もあります。

複数での動作原理

MAX7219は、DINから入力されたシリアルデータを順次内部のシフトレジスタに格納していくことは前の記事で解説しました。シフトレジスタに一時格納されたデータは、LOAD端子がLOWからHIGHになる立ち上がりのタイミングで制御レジスタに反映されるのですが、実は16ビットを超えたデータを受信すると、最初に受信したデータから順にDOUTから出力されるようになっているのです。つまり、DOUTの先に2台めのMAX7219のDINを接続し、Arduinoから16ビット×2のデータを送信すると、2台のMAX7219モジュールにデータを与えることができるのです。

No-OPの意味

さてここで、2個のMAX7219をデージーチェーン接続した状態で2つ目のMAX7219だけにデータを送りたい場合を考えてみましょう。データはArduinoなどのマイコンに近い側から順に流れていきますので、2つ目のMAX7219にデータを送る場合はどうしても16ビット×2のデータを送信する必要があります。このとき、2つ目に送信したいデータの後、『なにもしない』16ビットのデータを送信すれば1つ目の動作に影響を与えることなく2つ目だけにデータを送ることができるのです。このために用意されたのが『No-OP』です。

3個以上のMAX7219モジュールがある場合

下図のように、n個(n≧3)以上のMAX7219モジュールがカスケード接続されている場合を考えます。モジュールの番号は、とりあえず Arduinoなどのマイコンから一番遠いものを0番、一番近いものをn-1番としておきます。

k番(0≦k≦n-1)のMAX7219のみにデータを送りたい場合、それ以外のMAX7219にはNo-OPを送ることになります。k番よりもマイコンから遠い側にはk個(0番~k-1番)のモジュールがあり、近い側には n-k-1個(k+1番~n-1番)のモジュールがあります。先に送ったデータが遠くのモジュールに届くので、データを送るための処理は以下のようになります。

C++
for(k回繰り返し){
  No-OPを送信
}

送りたいデータを送信

for(n-k-1回繰り返し){
  No-OPを送信
}

製作・実験

それでは実際に試してみましょう。今回は MAX7219+8×8 LED Matrixモジュール×4組を1枚の基板上にまとめたモジュールを使用します。

回路図

最初からMAX7219 LED Matrixモジュール×4組が1枚の基板に組み込まれているので実際にはArduinoからたった5本のジャンパ線を配線するだけですが、内部的には以下のような回路になっていると思われます。

使用部品

種類型番数量備考参考リンク参考価格
LEDディスプレイモジュール18×32ドットマトリクスモジュールWiki2個で2,000円程度
ジャンパ線オス-メス5Arduino – LEDディスプレイモジュールの配線用
LEDディスプレイモジュール

今回使用したLEDディスプレイモジュールは写真のようなものです。MAX7219+1088ASのモジュールが4つ横に連結された構造をしています。2つセットで2,000円弱でした。

基板のパターンからして、まったく同じ構造を4つ横に連ねただけの構造です。パターンを傷つけないように切り離せばそれぞれ独立して使えそうな感じです。試すのはオススメしませんが。

LEDマトリクス1088ASはソケットで刺さっているだけなので取り外すことも可能です。

表示面側から、基板上の文字が読める方向(1088ASを外さないと文字は見えませんが)に置いたとき、左側に入力端子(L字ピン)があります。

反対側には基板上に出力端子となるホールのみ用意されています。L字ピンなどを半田付けすれば、さらにこの4連モジュールを接続できるようです。

ちょっと注意

よく見たら…前回のLED Matrix1つのモジュールと同じように基板上の文字が読める向き(入力端子が左側に来る向き)に置くと、1088ASの型番が上側、つまりROWが下から上に並ぶようになっています。当然各行のビットパターンも逆で、右が上位ビットになります。

つまりこれは、基板上の文字が上下逆さま(入力端子が右側にくる向き)に使うことになるようです。

配線図

LEDの数が増えても、カスケード接続なのでArduinoとの配線はたった5本だけです。

実物の写真もご覧下さい。スッキリしています。

プログラム

C++
// 定数の宣言
#define DATAPIN 12
#define CSPIN 11
#define CLKPIN 10

// 文字パターン設定
byte pattern[8][8]={
  {0B00111000,0B01000100,0B10000010,0B10000010,0B11111110,0B10000010,0B10000010,0B00000000},  // A
  {0B00000000,0B00000000,0B10011100,0B10100000,0B11000000,0B10000000,0B10000000,0B00000000},  // r
  {0B00000100,0B00000100,0B00000100,0B00111100,0B01000100,0B01000100,0B00111100,0B00000000},  // d
  {0B00000000,0B00000000,0B10000100,0B10000100,0B10000100,0B10001100,0B01110100,0B00000000},  // u
  {0B00001000,0B00000000,0B00011000,0B00001000,0B00001000,0B00001000,0B00011100,0B00000000},  // i
  {0B00000000,0B00000000,0B10111000,0B11000100,0B10000100,0B10000100,0B10000100,0B00000000},  // n
  {0B00000000,0B00000000,0B01111000,0B10000100,0B10000100,0B10000100,0B01111000,0B00000000},  // o
  {0B00000000,0B00000000,0B00000000,0B00000000,0B00000000,0B00000000,0B00000000,0B00000000}   // 空白
};

// LED Matrixデバイスの数を定義
int devCount=4;

// devNo番目のデバイスに16ビットのデータを送信
void sendSPIData(int devNo, uint16_t data){
  byte highByte=data>>8, lowByte=data&0xFF;
  byte sendData[devCount*2];
  for(int i=0; i<devCount*2; i++){
    sendData[i]=0;
  }
  sendData[devNo*2]   = highByte;
  sendData[devNo*2+1] = lowByte;

  digitalWrite(CSPIN, LOW);                       // CSはLOWの時セレクト
  for(int i=0; i<devCount*2; i++){
    shiftOut(DATAPIN, CLKPIN, MSBFIRST, sendData[i]);  // データをシリアル送信
  }
  digitalWrite(CSPIN, HIGH);                      // CSをHIGHに戻しておく
}

// devNo番目のLED Matrix のrow行目に値:dataをセットする
void setDigitData(int devNo, byte row, byte data){
  uint16_t sendData = (row+1)*256|data;
  sendSPIData(devNo, sendData);
}

void setup(){
  // 関係する端子を出力にセット
  pinMode(DATAPIN, OUTPUT);
  pinMode(CLKPIN, OUTPUT);
  pinMode(CSPIN, OUTPUT);

  // とりあえずCSは非選択にセット
  digitalWrite(CSPIN, HIGH);

  // 各レジスタに初期値をセット
  for(int devNo=0; devNo<devCount; devNo++){
    sendSPIData(devNo, 0x0F00);    // ディスプレイテスト;normal operation
    sendSPIData(devNo, 0x0900);    // デコードモード:no decode
    sendSPIData(devNo, 0x0A07);    // 輝度最大
    sendSPIData(devNo, 0x0B07);    // 全桁使用
  }
  // 点灯データクリア
  for(int devNo=0; devNo<devCount; devNo++){
    for(int row=0; row<8; row++){
      setDigitData(devNo, row, 0);
    }
  }

  // 初期設定が終わったらシャットダウン解除
  for(int devNo=0; devNo<devCount; devNo++){
    sendSPIData(devNo, 0x0C01);
  }
}

void loop() {
  for(int i=0; i<8; i++){
    for(int row=0; row<8; row++){
      for(int devNo=0; devNo<devCount; devNo++){
        setDigitData(devNo, (byte)row, pattern[(devNo+i)%8][row]);
      }
    }
    delay(500);
  }
}

プログラムの解説

前回のプログラムを拡張した構造です。差分のみ解説します。

デバイスの数の設定
C++
// LED Matrixデバイスの数を定義
int devCount=4;

モジュールの増設も可能なようなので、数は変数で設定しています。

デバイス番号を指定してデータを送信
C++
// devNo番目のデバイスに16ビットのデータを送信
void sendSPIData(int devNo, uint16_t data){
  byte highByte=data>>8, lowByte=data&0xFF;
  byte sendData[devCount*2];
  for(int i=0; i<devCount*2; i++){
    sendData[i]=0;
  }
  sendData[devNo*2]   = highByte;
  sendData[devNo*2+1] = lowByte;

  digitalWrite(CSPIN, LOW);                       // CSはLOWの時セレクト
  for(int i=0; i<devCount*2; i++){
    shiftOut(DATAPIN, CLKPIN, MSBFIRST, sendData[i]);  // データをシリアル送信
  }
  digitalWrite(CSPIN, HIGH);                      // CSをHIGHに戻しておく
}

前回のプログラムにあったsendSPIData()関数を、デバイス番号を指定できるように拡張しました。

まずデバイスの個数×2のbyte型配列を用意し、いったんすべて0(No-OP)で初期化します。

次に、デバイス番号に対応する要素にデータを書き込みます。byte型要素2つでデバイス1つ分なので、

devNo*2 番目の要素に送信データの上位バイト
devNo*2+1番目の要素に送信データの下位バイト

となります。

デバイス番号はArduinoから一番遠い側が0番です。こうするとデバイス番号が左から右に向かって大きくなるので、文字列を送信する場合に文字の順とデバイス番号が一致して直感的に処理を記述できます。

諸々の初期設定の流れなどは前回と同じ
C++
// devNo番目のLED Matrix のrow行目に値:dataをセットする
void setDigitData(int devNo, byte row, byte data){
  uint16_t sendData = (row+1)*256|data;
  sendSPIData(devNo, sendData);
}

void setup(){
  // 関係する端子を出力にセット
  pinMode(DATAPIN, OUTPUT);
  pinMode(CLKPIN, OUTPUT);
  pinMode(CSPIN, OUTPUT);

  // とりあえずCSは非選択にセット
  digitalWrite(CSPIN, HIGH);

  // 各レジスタに初期値をセット
  for(int devNo=0; devNo<devCount; devNo++){
    sendSPIData(devNo, 0x0F00);    // ディスプレイテスト;normal operation
    sendSPIData(devNo, 0x0900);    // デコードモード:no decode
    sendSPIData(devNo, 0x0A07);    // 輝度最大
    sendSPIData(devNo, 0x0B07);    // 全桁使用
  }
  // 点灯データクリア
  for(int devNo=0; devNo<devCount; devNo++){
    for(int row=0; row<8; row++){
      setDigitData(devNo, row, 0);
    }
  }

  // 初期設定が終わったらシャットダウン解除
  for(int devNo=0; devNo<devCount; devNo++){
    sendSPIData(devNo, 0x0C01);
  }
}

このあたりは、sendSPIData()関数の仕様変更に伴って、データを送信する際にデバイス番号devNoを指定する記述や、すべてのデバイスにデータを送信するためにデバイスの数だけループする記述を追加してあります。

loop()関数
C++
void loop() {
  for(int i=0; i<8; i++){
    for(int row=0; row<8; row++){
      for(int devNo=0; devNo<devCount; devNo++){
        setDigitData(devNo, (byte)row, pattern[(devNo+i)%8][row]);
      }
    }
    delay(500);
  }
}

今回は『Arduino 』(最後の空白を入れて8文字)の文字列がスクロールするようにしてみました。

実行結果

やはり複数文字を一度に表示できた方が単語の可読性ははるかに高いですね。スクロールについては文字単位なので滑らかさはありません。次はドット単位でのスクロールも試してみようと思います。

まとめ

  • MAX7219は、カスケード接続によって複数のデバイスをまとめて扱う機能があり、これによって多数のLEDを制御できる

コメント

タイトルとURLをコピーしました