8-1 マトリクスLEDを表示する

「マトリクスLED」はLEDを規則的に配置した表示器で、身近なところでは鉄道の時刻表示器やバスの行先表示などに使われています。
またきぐるみ界隈で最近流行りの種族、Protogenの目や口の表示なんかにもよく使われています。
Protogenを作りたくて電子工作始める人もいるかな~~などと思ったので解説を書くことにしました。
  

  
ちなみにこちらの動画では8-1-2と8-1-4の動作の様子が見れます。
  

電子工作で最も一般的なマトリクスLEDは8*8=64個のLEDが配置された、上図のタイプのものです。
LEDの色は単色表示のものが一般的ですが、複数色表示できたり、マルチカラーのものもあります。今回使うのは単色表示のものです。
  
このマトリクスLEDでは、「ダイナミック点灯」と呼ばれる方式でLEDを制御しています。
「ダイナミック点灯」は、超高速(delayなどで散々出てきていますが、マイコンや電子部品の処理速度は人間が認識できないぐらい高速です)でLEDの点灯状態を切り替えることによって、人間の目には特定のLEDが同時に点灯しているように見せる点灯方式です。
この方式を採用することによって、本来であれば64個のLEDに対し64本の配線を必要とするところを、なんとたったの16本の配線で制御することができるようになります。(下記リンク参考)

 8×8マトリックスLEDを点灯させる(hiramine.com):
 https://www.hiramine.com/physicalcomputing/arduino/matrixled.html
  

さて、先程の参考リンクをご覧頂くとまあ実感できると思うのですが、配線は16本でも十分多いです!!
マトリクスLED一個でArduinoのソケットのほぼ全てが埋まってしまいます。
なので、チャレンジする分には全然いいのですが、到底実用的ではありません。
そこで!もっと便利にマトリクスLEDを制御できるのが「MAX7219モジュール」です。


↑マトリクスLEDが挿さっている、この青い基板に載っているのがMAX7219モジュールである!!
  
  
MAX7219は、このモジュールに搭載されている制御ICの名前です。
Amazonで買うと、マトリクスLEDがこのモジュールの上に挿してある状態で届くかと思います。
マトリクスLEDを取り外す場合は、ピンが曲がったり折れたりしないよう、垂直方向に静かに引き抜いてください。
また外す前に、どちらの辺が上側/下側かを印しておくことをオススメします。
  

講師が指定した商品は、MAX7219モジュールが4個分横方向に連結しているタイプです。
各モジュールの基板は基板上の回路で繋がっているので、一番端のピンが生えているモジュールをArduinoに接続するだけで、一度に4つのマトリクスLEDを動かすことが可能です。
  
もし4つも要らないよということであれば、モジュールごとに分割することもできます。
基板と基板の境目にある分割線に沿って、板チョコを割るようなイメージでニッパーで分割します。
分割したモジュールは、ピンやソケットをはんだ付けすることで独立して使うことができます。
それとは逆に、分割されているモジュール同士を連結させることもできます。
(はんだ付けしてもいいですし、ピンやソケットをはんだ付けしてそれ同士で繋いだり、ジャンパ線で繋ぐこともできます)
  
ピンやソケットをはんだ付けしたり、モジュール同士を繋ぐ際は、基板の向きに気を付けてください。
基板の下部をよく見ると電気の流れる向きを示す「→IN  OUT→」という印刷があります。
Arduinoに接続するピンやソケットは、必ず「→IN」側にはんだ付けしてください。
モジュール同士を繋ぐ場合は「(Arduino側のモジュール)OUT→|→IN(繋がれるモジュール)」の向きで繋ぎます。
  
  

MAX7219のライブラリはたくさんありますが、今回は下の2つのライブラリを使います。
ライブラリマネージャから探してインストールしてください。

  • 「MD_MAX72XX」 by majicDesigns
  • 「MD_Parola」   by majicDesigns

  


  
MAX7219モジュールはSPI接続で繋ぎます。
VCC→5V / GND→GND / DIN→D11 / CS→D10 / CLK→D13 に繋いでください。

今回MAX7219モジュールのピン 対 Arduinoのソケットの接続をします。
なので、ピン-ソケット(つまりオス-メス)のジャンパワイヤが必要となります。
  

使う部品リスト

部品 個数
MAX7219モジュール 4連結しているものを1個

  

サンプルコード8-1-1_MAX7219_HelloWorld.inoを開いてください。

// Program to demonstrate the MD_Parola library
//
// Simplest program that does something useful - Hello World!
//
// MD_MAX72XX library can be found at https://github.com/MajicDesigns/MD_MAX72XX
//

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted

//このライブラリは複数の種類のMAX7219製品に対応しています。
//そのため使う製品によってハードウェアの構成を変える必要があります。
//下記の4つの設定のうち、自分のモジュールで正常に表示される設定一つだけを有効にしてご利用ください。
//ちなみに講師が使用しているモジュールはFC16_HWで動作したので、これを初期値としています。

//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
//#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

#define MAX_DEVICES 4 //繋げるモジュールの個数。4連結をそのまま使うなら4

#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary output pins
// MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

void setup(void)
{
  P.begin();
}

void loop(void)
{
  if (P.displayAnimate())
    P.displayText("Hello", PA_CENTER, P.getSpeed(), 1000, PA_SCROLL_DOWN, PA_SCROLL_DOWN);
}

このコードはMD_Parolaのサンプルコード「Parola_HelloWorld」と同じで、多少設定をいじった
ものです。設定をいじった部分は以下の2か所です。

21~24行目:ハードウェアの構成の設定をする
//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
//#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

16行目~にも書いていますが、このライブラリは複数のMAX7219製品に対応しています。
なので使う製品によってハードウェアの構成を変えてやる必要があります。
この4つの設定のうち、自分のモジュールで正常に表示される設定一つを有効にしてください。
ちなみに講師のモジュールと同じであればFC16_HWで正常に表示されるはずです。
(Arduinoと接続しているピンを右側にした状態で見てください)

参考リンク:https://qiita.com/nayuki_eng/items/066d8bda972b7b5248d7

26行目:繋げるモジュールの個数を設定する
#define MAX_DEVICES 4 //繋げるモジュールの個数。4連結をそのまま使うなら4

あなたがArduinoに接続しているモジュールの連結数に書き換えてください。
講師と同じ製品を購入して、購入時のままの4連結であれば4です。
  


応用編なのでサンプルコードの逐一解説はしません。
45行目のテキストの表示をしている行だけ解説します。

45行目
 P.displayText("Hello", PA_CENTER, P.getSpeed(), 1000, PA_SCROLL_DOWN, PA_SCROLL_DOWN);

第一引数:表示したい文字です。半角英数字のみ対応しています。
第二引数:表示する文字の配置です。PA_CENTERはディスプレイに対し中央揃えで表示します。
第三引数:アニメーション開始時の速度(フレーム数)を数値で指定します。
第四引数:アニメーション終了時の速度(フレーム数)を数値で指定します。
第五引数:表示開始時のエフェクトです。スクロールダウンに設定されています。
第六引数:表示終了時のエフェクトです。文字を消したくない場合はPA_NO_EFFECTを入れます。
  
正常に表示されていれば、4連結したモジュールの中心に「Hello」という文字が上からスクロールダウンで表示され、下にスクロールダウンする形で消え、次の文字が上からスクロールダウンで表示され…をループするかと思います。
  
関数の引数などの詳細解説は公式のドキュメントを参照してください。
 Parola for Arduino:
 https://majicdesigns.github.io/MD_Parola/class_m_d___parola.html

文字表示の仕方のバリエーションも公式ドキュメントで説明されています。
例えばこの文を下記のように変更すると、表示の仕方がかなり変わるはずです。

P.displayText("Hello", PA_CENTER, 100, 100, PA_BLINDS, PA_WIPE_CURSOR);

文字表示のバリエーションについては次の8-1-2もご参照ください。
  
  

いくつかのテキストアニメーションのパターンで順番に文字表示するサンプルコードを、8-1-2_MAX7219_TextVariation.inoとして収録しました。
こちらもぜひ試してみてください。配線は8-1-1のままでOKです。
  

サンプルコード8-1-2_MAX7219_TextVariation.inoを開いてください。

// Program to demonstrate the MD_Parola library
//
// Simplest program that does something useful - Hello World!
//
// MD_MAX72XX library can be found at https://github.com/MajicDesigns/MD_MAX72XX
//

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted

//このライブラリは複数の種類のMAX7219製品に対応しています。
//そのため使う製品によってハードウェアの構成を変える必要があります。
//下記の4つの設定のうち、自分のモジュールで正常に表示される設定一つだけを有効にしてご利用ください。
//ちなみに講師が使用しているモジュールはFC16_HWで動作したので、これを初期値としています。

//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
//#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

#define MAX_DEVICES 4  //繋げるモジュールの個数。4連結をそのまま使うなら4

#define CLK_PIN 13
#define DATA_PIN 11
#define CS_PIN 10

// Hardware SPI connection
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// Arbitrary output pins
// MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

int animation = 0;

void setup(void) {
  P.displayReset();
  P.begin();
}

void loop(void) {
  P.displayAnimate();
  delay(10);
  if (P.getZoneStatus(0)) {  //指定されたアニメーションの描画が終わったら
    if (animation == 0) {
      P.displayText("Hello", PA_CENTER, 0, 1000, PA_SCROLL_DOWN, PA_SCROLL_UP);
      animation = 1;
    } else if (animation == 1) {
      P.displayText("Hello", PA_CENTER, 50, 300, PA_BLINDS, PA_FADE);
      animation = 2;
    } else if (animation == 2) {
      P.displayText("Hello", PA_CENTER, 50, 300, PA_WIPE , PA_MESH);
      animation = 3;
    } else if (animation == 3) {
      P.displayText("Hello", PA_CENTER, 50, 300, PA_SCAN_VERT  , PA_SCAN_VERT);
      animation = 4;
    } else if (animation == 4) {
      P.displayText("Hello", PA_CENTER, 0, 300,  PA_RANDOM, PA_GROW_DOWN);
      animation = 5;
    } else {
      P.displayText("Bye-Bye", PA_CENTER, 50, 50, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
      animation = 0;
    }
  }
}

そんなわけで、いくつかのバリエーションで文字表示するコードです。
色々詰めてみましたが、もっとたくさんの文字表示の仕方が公式ドキュメントには掲載されています。

 Parola for Arduino:
 https://majicdesigns.github.io/MD_Parola/class_m_d___parola.html
  
なおこの方法では日本語などの2バイト文字を表示することはできません。
日本語を表示したい場合、カスタムフォントの作成と設定が必要ですが本資料では割愛します。
ちなみにカタカナであればライブラリに標準でインストールされているそうなので、下記のリンクを参考に、表示にチャレンジしてみてもいいと思います。

  Arduinoでドットマトリクス表示 Part4 フォント設定(ProgramResource.net):
  https://programresource.net/2020/03/08/3095.html
  
  

さて、文字表示には成功しましたが、それだけでProtogenを作るのは困難です。
Protogenの目や口は任意の形状に集合したピクセル、わかりやすく言うところの“ドット絵”のような形で表現されています。
もちろんそういったドットの描画も、Parolaはサポートしています。

Arduinoの配線等は引き続きそのままでOKです。
  

サンプルコード8-1-3_MAX7219_CustomCharacter1.inoを開いてください。

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted

//このライブラリは複数の種類のMAX7219製品に対応しています。
//そのため使う製品によってハードウェアの構成を変える必要があります。
//下記の4つの設定のうち、自分のモジュールで正常に表示される設定一つだけを有効にしてご利用ください。
//ちなみに講師が使用しているモジュールはFC16_HWで動作したので、これを初期値としています。

//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
//#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

#define MAX_DEVICES 4  //繋げるモジュールの個数。4連結をそのまま使うなら4

#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

#define  delay_t  50  // in milliseconds

// Hardware SPI connection
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

byte heart[8] = {0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18};
byte face[8] = {0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C};
byte face2[8] = {0x00, 0x24, 0x24, 0x24, 0x00, 0x42, 0x3C, 0x00};
byte arrow[8] = {0x18, 0x0C, 0x06, 0xFF, 0xFF, 0x06, 0x0C, 0x18};

void setup() {  
  mx.begin();
  //mx.control(MD_MAX72XX::INTENSITY, 0);//輝度調整
  mx.clear();
  }

void loop() {
  drawShape();
}

void drawShape() {
  for (int i = 0; i <= 7; i++) {
    mx.setRow(0, 0, i, heart[i]);
  }
  delay(delay_t);
  for (int i = 0; i <= 7; i++) {
    mx.setRow(1, 1, i, face[i]);
  }
  delay(delay_t);
  for (int i = 0; i <= 7; i++) {
    mx.setRow(2, 2, i, face2[i]);
  }
  delay(delay_t);
  for (int i = 0; i <= 7; i++) {
    mx.setRow(3, 3, i, arrow[i]);
  }
  delay(delay_t);
}

こちらのコードの大部分は下記のサイトから拝借しました。
  MAX7219 LED Matrix Display with Arduino Tutorial(Circuit Geeks):
  https://www.circuitgeeks.com/arduino-max7219-led-matrix-display/
  
コードを書き込むと、Arduinoに繋がれた側から順にハート/スマイリー/顔面だけスマイリー/矢印の4つのドット絵が表示されます。
これらのドット絵は、JPGやGIF、PNGのような画像ファイルではなく、数値のデータで構成されています。
コード中30~33行目の部分が、ドットの配置を数値として格納している変数です。

byte heart[8] = {0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18};
byte face[8] = {0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C};
byte face2[8] = {0x00, 0x24, 0x24, 0x24, 0x00, 0x42, 0x3C, 0x00};
byte arrow[8] = {0x18, 0x0C, 0x06, 0xFF, 0xFF, 0x06, 0x0C, 0x18};

これではなんのこっちゃです。ちょっと大事な内容なので解説します。
 
これらのグラフィックは、LEDの点灯と消灯により描かれています。
点灯状態を「1」、消灯状態を「0」とすることで、下の図のようなかわいいチューリップを描くことができます。

このかわいいチューリップの絵を、上から順番に一行ごとに2進数のデータに変換します。
例えば1行目は「01011010」という数値が横並びになっているのがわかります。2行目なら「01111110」です。
これらのデータを、今度は16進数に変換します。1行目の「01011010」という2進数は、16進数に変換すると「5A」になります。
この「5A」の頭に16進数であることを示す「0x(ゼロエックス)」を付ければ完成です。
これを8行分繰り返して得られるのが、上の図の一番右端の数値です。
最終的に、この数値を配列という形でbyte型の変数「turip」の中に格納すると、こうなります。

byte turip[8] = {0x5A, 0x7E, 0x7E, 0x3C, 0x99, 0xDB, 0x7E, 0x3C};

先程のコードの30~33行目をこの変数turipに置き換えて、loop部の変数名を全部turipに書き換えてArduinoに書き込めば、あなたの卓上にかわいいチューリップ畑が爆誕します。
  

講師が作っちゃったやつ

このドット絵をbyte型へ変換する作業はなんと言っても超めんどくさいです。
なので、変数名を入れてドット絵を描けば自動でbyte型の変数を生成するExcelツールを作っちゃいました!!


かわいいちゅーりっぷ🌷
  

パネル4枚分のドット絵を一気に描けるシートもあります。べんり!!!

  
Excelツールのダウンロードはこちらから→ dot_e_maker.xlsx
なおサンプルコード8-1-3のフォルダにも同じファイルが入れてあります。
Excelを使える環境の方はぜひ使ってみてください。

Excelなんてなかった方のためのツール

PCにExcelが入っていない哀れな子羊は下記のサイトさんなどが役に立つかと思います。

LED Matrix Tool(GurgleApps):
https://gurgleapps.com/tools/matrix

8×8 ドットマトリクスLED 16進数変換ツール(製作物置き場):
https://koniko2.web.fc2.com/ElectronicKit/DotMatrixLed.html
  
  

8-1-3のように、MAX7219に任意のドット絵を表示できることがわかりました。
ここまでできれば勝ったも同然です…というのは言い過ぎですが、少なくともProtogenの目や口を表示させることはできます。
出血大サービスでProtogenの目っぽいサンプルコード、8-1-4_MAX7219_CustomCharacter_Protoeye.inoを作ってみました。
ついでなのでランダムにまばたきする機能もつけてみました。
  

サンプルコード8-1-4_MAX7219_CustomCharacter_Protoeye.inoを開いてください。

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted

//このライブラリは複数の種類のMAX7219製品に対応しています。
//そのため使う製品によってハードウェアの構成を変える必要があります。
//下記の4つの設定のうち、自分のモジュールで正常に表示される設定一つだけを有効にしてご利用ください。
//ちなみに講師が使用しているモジュールはFC16_HWで動作したので、これを初期値としています。

//#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
//#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
//#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

#define MAX_DEVICES 4  //繋げるモジュールの個数。4連結をそのまま使うなら4

#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

#define  delay_t  1  // in milliseconds

// Hardware SPI connection
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

byte protoeyeR1[8] = {0x00, 0x80, 0xE0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF0};
byte protoeyeR2[8] = {0x00, 0x0F, 0x1F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F};
byte protoeyeR1B[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x78, 0x00};
byte protoeyeR2B[8] = {0x00, 0x00, 0x00, 0x00, 0x38, 0x0F, 0x00, 0x00};

long randNumber;

void setup() {  
  mx.begin();
  //mx.control(MD_MAX72XX::INTENSITY, 0);//輝度調整
  mx.clear();
  randomSeed(analogRead(0));
  }

void loop() {
  randNumber = random(1, 31); // 10から19の乱数を生成
  if(randNumber >= 2){
    drawShape_nomal();
    delay(100);
  }
  else {
    drawShape_blink();
    delay(80);
    drawShape_nomal();
    delay(100);
  }
}

void drawShape_nomal() {
  for (int i = 0; i <= 7; i++) {
    mx.setRow(0, 0, i, protoeyeR1[i]);
  }
  delay(delay_t);
  for (int i = 0; i <= 7; i++) {
    mx.setRow(1, 1, i, protoeyeR2[i]);
  }
  delay(delay_t);
}

void drawShape_blink() {
  for (int i = 0; i <= 7; i++) {
    mx.setRow(0, 0, i, protoeyeR1B[i]);
  }
  delay(delay_t);
  for (int i = 0; i <= 7; i++) {
    mx.setRow(1, 1, i, protoeyeR2B[i]);
  }
  delay(delay_t);
}

この目のまばたきの仕組みは下記のようになっています。

①予め“目を開けている状態”と“閉じた状態”の2パターンのデータを用意しておきます

②乱数を生成する。今回は1~30の乱数を生成しています

③生成した乱数で特定の数値が出現した時だけ目を閉じた状態→開けた状態を表示します
 今回は1~30の乱数のうち、「1」が出現した時だけこの判定に入る=まばたきをします
 この時実行するのが、自作の関数drawShape_blink();です。
 このスケッチの最下部にvoid drawShape_blink() {~(中略)~}という部分がありますが、ここで関数を作っています。
 この関数の中身にはモジュール2個分にわたる、目を閉じたドット絵を表示する処理が入っています。
 ちなみにこの関数の上にあるdrawShape_nomalも、同じく自作関数です。こちらには通常の目を表示する処理が入っています

④乱数が2~30だった場合、は、通常の目を表示するdrawShape_nomalだけを実行します

⑤まばたきの頻度はまばたき判定を出す乱数の比率を、
 まばたき時に目を閉じている時間の長さはdrawShape_blink();の後のdelay(80);の数値を変えて調整します。
 またこのままではArduinoの処理が速すぎて、目まぐるしく表情が変わってしまうため、
 通常の目を表示した後にdelay(100);を挟むことで、自然に見えるようにしています。
 今回はランダム操作にしていますが、例えばこれにタクトスイッチを繋げて、スイッチを押すことで表情が切り替わったり、
 タッチセンサなどを使えばセンサのあるところを触られた時に表情を変えたりすることができます。
  
これらのヒントをもとに、残りの目や口や鼻は自力で作ってみてください。
ちなみにもう一つヒントになりそうなこととしては、目鼻のパーツをカスタムフォントとして作って扱うのもありかなと思います。
この方法だと、テキスト表示の関数で目鼻を操作することが可能になるかと思います。