電子工作で定番の電子部品といえばドットマトリクスLEDです。この部品を使えば、少ないピンで大量のLEDを制御できます。今回は、ドットマトリクスLEDの定番の制御方式であるダイナミック点灯制御を紹介します。
目次
- 大量のLEDで文字や画像を表示!電子工作でどうやって動かす?
- いくつものLEDを制御「ダイナミック点灯制御」
- ArduinoとドットマトリクスLEDの配線
- まずはダイナミック点灯の基本的な動作から
- LEDの点灯を配列で制御
- 文字をスライド表示して電光掲示板のように動かす
- もっとLEDの数を増やして複雑な制御を行うには
大量のLEDで文字や画像を表示!電子工作でどうやって動かす?
街角や駅にある電光掲示板をよく見てみると、格子状に並べた多数のLEDを個別に点灯させて、文字や映像を表示していることに気づくと思います。
Arduinoのようなマイコンボードを少しでも触ったことがある方なら疑問に思うと思いますが、数十個のピンしかないマイコンでどうやって数百や数千のLEDを制御しているのでしょうか。
今回は、電子工作で定番の電子部品、ドットマトリクスLEDを使って、少ないピンで大量のLEDを制御するダイナミック点灯制御について解説します。
いくつものLEDを制御「ダイナミック点灯制御」
ダイナミック点灯制御とは、高速にLEDの点灯状態を切り替えて複数のLEDを同時に点灯させているように見せる点灯方式です。たとえば、複数のLEDの点灯を、一秒間に数百回切り替えると、人の目にはLEDが同時に点灯しているように見えます。この点灯制御を使うと、複数のLEDを少ないマイコンボードのピンで制御できるようになります。
このダイナミック点灯制御によく使われるのが、複数のLEDをマトリクス状に配線したドットマトリクスLEDです。ダイナミック点灯制御と組み合わせることで少ない配線でさまざまなLEDの点灯パターンを実現することができます。
ArduinoとドットマトリクスLEDの配線
今回は、電子工作でよく使われている8列×8行・計64個のLEDを搭載するドットマトリクスLEDの使い方を解説します。このドットマトリクスLEDの配線は、列と行それぞれの数を合わせた16本だけです。
ドットマトリクスLEDは電流制限用の抵抗を搭載していません。データシートを見ながら、アノード側またはカソード側のどちらかにそれぞれ8本の抵抗を接続しましょう。
抵抗値は、Arduinoのデジタル入出力ピンの電圧(5V)・LED固有の順方向電圧降下(VF)・LEDの明るさ(電流)で決定します。ただし、Arduinoにはピンから取り出せる電流値に制限があるので注意してください。抵抗の接続はアノード側・カソード側どちらにつないでも問題ありません。
Arduino Unoを使用する場合、スケッチの書き込みに使用するデジタル0,1(TX、RX)や、内蔵LEDが接続されているデジタル13を避けて配線します。デジタル端子が不足しますが、アナログA0~A5を、それぞれデジタルD14~D19として使用し、16ピン分を配線します。
まずはダイナミック点灯の基本的な動作から
ドットマトリクスLEDの配線が終わったら、早速スケッチを書いてみましょう。まずは、ドットマトリクスLEDの全てのLEDを点灯します。方法としては、1行のLEDを全て点灯し、列を素早く切り替えることで見かけ上全てのLEDが点灯しているように見えます。
//マトリクスLED ピン順列 int anode[8] = { 15, 4, 5, 11, 7, 12, 17, 18 }; int cathode[8] = { 10, 16, 9, 14, 2, 8, 3, 6 }; void setup() { //ピンの初期化 for ( int i = 0; i < 8; ++i ) { pinMode( anode[i], OUTPUT ); digitalWrite( anode[i], LOW ); } for ( int i = 0; i < 8; ++i ) { pinMode( cathode[i], OUTPUT ); digitalWrite( cathode[i], HIGH ); } } void loop() { for ( int i = 0; i < 8; ++i ) { digitalWrite( cathode[i], LOW ); for ( int j = 0; j < 8; ++j ) { digitalWrite( anode[j], HIGH ); } //delay(100); for ( int j = 0; j < 8; j++ ) { digitalWrite( anode[j], LOW ); } digitalWrite( cathode[i], HIGH ); } }
ドットマトリクスLEDには配列とfor文を使用するとシンプルなスケッチになります。
プログラムの流れを簡単に解説します。
一番初めのanode配列とcathode配列には、それぞれマトリクスLEDのピンとArduinoピンの配線情報を入力します。anode配列には、マトリクスLEDの列のピンを順番に入力。cathode配列には、マトリクスLEDの行のピンを順番に入力します。マトリクスLEDの行列番号の順で、接続するArduinoのピンが配列に格納されるよう注意しましょう。
次のsetup関数では、Arduinoで使うピンの状態を定義するため、先ほどのanode・cathode配列とfor文を使って、それぞれのピンを1つずつ初期化しています。
マトリクスLEDの点灯状態はloop関数で制御しています。マトリクスLEDの点灯は、二重のfor文にするとスッキリ収まります。
外側のfor文では、点灯する行のcathode側をLOWにして、電流が行全体に流れるようにします。その後、内側のfor文によって点灯させたい列を順に指定します。今回は、全ての列のLEDを点灯させるので、anode側は全てHIGHにします。この処理が全て終わると、1行すべてが点灯します。この動作を全ての行に対して素早く繰り返すことで全てのLEDが同時に光っているように見えます。
29行目にコメントアウトしているdylay関数を有効にすると、行単位で表示を切り替えている様子がわかると思います。このように、ダイナミック点灯とは、LEDのON・OFFを素早く切り替えて複数のLEDが同時に点灯しているように見せる方式です。
LEDの点灯を配列で制御
ここまでくれば、ドットマトリクスLEDの制御をほとんど理解できたようなものです。しかし、LEDをただ点灯するだけでは物足りないので、LEDの点灯状態を自由自在に切り替えてみましょう。今回の例では、DEVICE PLUSの”D”の1文字を表示させてみます。
LEDのON・OFF制御には、0と1のデータ(boolean型)だけあれば良いので、以下のように配列を使って”D”の文字の点灯データを定義します。点灯するLEDが”1”,消灯するLEDが”0”としてドット絵の要領で配列を定義していきましょう。使用しているのは8×8のドットマトリクスなので、配列も8×8です。
boolean matrix[8][8] = { { 0, 1, 1, 1, 1, 0, 0, 0 }, { 0, 1, 1, 1, 1, 1, 0, 0 }, { 0, 1, 1, 0, 1, 1, 1, 0 }, { 0, 1, 1, 0, 0, 1, 1, 0 }, { 0, 1, 1, 0, 0, 1, 1, 0 }, { 0, 1, 1, 0, 1, 1, 1, 0 }, { 0, 1, 1, 1, 1, 1, 0, 0 }, { 0, 1, 1, 1, 1, 0, 0, 0 } };
配列の中身が表示に反映するように、スケッチを変えましょう。二重for文の中身のdigitalWrite関数を、定義した配列を参照するように変更します。
digitalWrite( anode[j], HIGH );
↓
digitalWrite( anode[j], matrix[i][j] )
これらの変更を反映させたスケッチは下のようになります。
//マトリクスLED ピン順列 int anode[8] = { 15, 4, 5, 11, 7, 12, 17, 18 }; int cathode[8] = { 10, 16, 9, 14, 2, 8, 3, 6 }; void setup() { //ピンの初期化 for ( int i = 0; i < 8; ++i ) { pinMode( anode[i], OUTPUT ); digitalWrite( anode[i], LOW ); } for ( int i = 0; i < 8; ++i ) { pinMode( cathode[i], OUTPUT ); digitalWrite( cathode[i], HIGH ); } } boolean matrix[8][8] = { { 0, 1, 1, 1, 1, 0, 0, 0 }, { 0, 1, 1, 1, 1, 1, 0, 0 }, { 0, 1, 1, 0, 1, 1, 1, 0 }, { 0, 1, 1, 0, 0, 1, 1, 0 }, { 0, 1, 1, 0, 0, 1, 1, 0 }, { 0, 1, 1, 0, 1, 1, 1, 0 }, { 0, 1, 1, 1, 1, 1, 0, 0 }, { 0, 1, 1, 1, 1, 0, 0, 0 } }; void loop() { // 文字データの出力処理 for ( int i = 0; i < 8; i++ ) { digitalWrite( cathode[i], LOW ); for ( int j = 0; j < 8; j++ ) { digitalWrite( anode[j], matrix[i][j] ); } //delay(50); for ( int j = 0; j < 8; j++ ) { digitalWrite( anode[j], LOW ); } digitalWrite( cathode[i], HIGH ); } }
スケッチを実行すると配列の内容通りにLEDが点灯しているはずです。先ほどのスケッチと同じように、コメントアウトしているdelay関数を有効にすると、Dの形を一行ずつなぞりながらLEDを切り替えている様子がわかると思います。
スケッチの処理は、配列の”0”と”1”をそれぞれ読み込んで、対応する箇所のLEDの点灯状態を制御するシンプルなものです。このように、配列を読み込む形にすれば、自由自在に点灯状態を制御できるようになります。
ここまでくれば、ドットマトリクスLEDのダイナミック点灯制御は理解できたと思います。
文字をスライド表示して電光掲示板のように動かす
ドットマトリクスLEDを自由自在に点灯できるようになれば、次は電光掲示板のように動かしてみたいと思うかもしれません。もちろん、ドットマトリクスLEDとArduinoを使えば、点灯中の表示状態を切り替えたり、電光掲示板のようにスクロールしたりすることもできますが、ここからはスケッチのテクニックが必要になります。
例えば、ダイナミック点灯制御を行いながら文字を1秒ずつスクロールするにはどうすれば良いでしょうか。ここで、Lチカによく使うdelay関数を使うと、マイコンが停止します。ダイナミック点灯制御も止まってしまい、文字が表示できません。
絶え間なくLEDを制御するには、マイコンの動きを一切止めずに処理を行う考え方が必要になります。さらに、電光掲示板のように多くの文字を表示するには、多くの配列データが必要となり、そこから表示する部分の抜き出し方を考えなくてはいけません。
そこで今回は、マイコンを止めないように、millis関数を使って経過時間を測定しながら、文字をスクロールする処理を追加します。
if (tm + SCROLL_TIME <= millis()) { for (int i = 0; i < 8 ; i++) { for (int j = 0; j < 8 ; j++) { matrix[i][j] = matrix_data[i][j + slide]; } } //格納場所をスライド、末端に到達したら始めに戻る if (slide < 49) { ++slide; } else { slide = 0; } //経過時間を再計測 tm = millis(); }
文字データも、スクロールするとなると配列が大きくなります。今回は、”DEVICE PLUS”の文字をスクロール表示できるような配列データを作ります。
boolean matrix_data[8][57] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0 } };
スクロールするための機能を追加したスケッチは以下のようになります。全体のスケッチの流れとしては、文字データを格納した配列から、表示する部分の配列を抜き出して、指定した時間ごとにスクロールさせます。Arduinoは一切停止しないため、常にダイナミック点灯制御を行ないます。
//マトリクスLED ピン順列 int anode[8] = { 15, 4, 5, 11, 7, 12, 17, 18 }; int cathode[8] = { 10, 16, 9, 14, 2, 8, 3, 6 }; //文字データ boolean matrix_data[8][57] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0 } }; boolean matrix[8][8] = {}; //出力用配列変数 int slide = 0; unsigned long tm = 0; //スクロール時間 #define SCROLL_TIME 90 //ミリ秒 void setup() { //ピンの初期化 for ( int i = 0; i < 8; ++i ) { pinMode( anode[i], OUTPUT ); digitalWrite( anode[i], LOW ); } for ( int i = 0; i < 8; ++i ) { pinMode( cathode[i], OUTPUT ); digitalWrite( cathode[i], HIGH ); } // 出力用配列を初期化 for (int i = 0; i < 8 ; ++i) { for (int j = 0; j < 8 ; ++j) { matrix[i][j] = 0; } } //初期経過時間を格納 tm = millis(); } void loop() { //経過時間ごとに文字データを出力用配列に格納 if (tm + SCROLL_TIME <= millis()) { for (int i = 0; i < 8 ; i++) { for (int j = 0; j < 8 ; j++) { matrix[i][j] = matrix_data[i][j + slide]; } } //格納場所をスライド、末端に到達したら始めに戻る if (slide < 49) { ++slide; } else { slide = 0; } //経過時間を再計測 tm = millis(); } // 文字データの出力処理 for ( int i = 0; i < 8; i++ ) { digitalWrite( cathode[i], LOW ); for ( int j = 0; j < 8; j++ ) { digitalWrite( anode[j], matrix[i][j] ); } //delay(50); for ( int j = 0; j < 8; j++ ) { digitalWrite( anode[j], LOW ); } digitalWrite( cathode[i], HIGH ); } }
このスケッチは、23行目のSCROLL_TIMEの数字を変えることでスクロール速度を調整できます。動画は光を反射しているので少し見難くいですが、70msまでスピードを上げても文字の形を確認できます。
もっとLEDの数を増やして複雑な制御を行うには
Arduinoを使ってドットマトリクスLEDを直接制御する方法では、8×8のドットマトリクスLEDを動かすのが限界です。より大きなドットマトリクスLEDの制御には、専用ICを使った通信制御や、外部メモリなどが必要になります。
一見すれば簡単に見えるドットマトリクスLEDは、なかなか奥の深い電子部品です。うまく使うには、電子部品やマイコン制御の技術が必要になります。
ドットマトリクスを扱う技術は、ほかの電子部品を使う時にも応用できるので、ドットマトリクスLEDは、学習にピッタリな電子部品かもしれません。
電子工作としても学習用途としても、入手しやすい電子部品なので、Arduinoを買ったら一緒にそろえるのも良いでしょう。