カテゴリー

福井裕司 建築発明工作ゼミ|
1. マトリクス LEDの表示実験

四谷アート・ステュディウム2008年度「建築発明工作ゼミ」(講師=福井裕司)授業サブノートの一部です。「建築発明工作ゼミ2008」ブログより転載。

授業サブノートを抜粋して、2013年度「電子工作ゼミ」で教材として使用する、“Arduino”と“Processing”を用いた実践演習の内容を2回にわたってそれぞれ紹介します。第1回は“Arduino”です。ゼミの資料としてご参照ください。


>> 電子工作ゼミの講座案内はこちらです


Arduino マトリクスLED1


今回はマトリクス LEDの表示実験をします。秋月電子で購入した8×8マトリクスLEDを使用します。8×8なので合計64個のLEDが搭載されています。それぞれのLEDを直接点灯させるためには、64個分の端子が必要であり、Arduinoの端子の数以上になってしまいますが、ダイナミック点灯(説明以下)という方法で可能になります。
ArduinoにはマトリクスLED用ライブラリWiringのライブラリを利用)を使う方法もありますが、MAX7219というLEDディスプレイドライバICを必要とします。このICを使えば、Arduinoからはシリアル通信を通して3本の線で制御することができます(MAX7219との接続サンプル)。また、74HC595というICを二つ使う方法(サンプル)もあります。
今回はICを使わずに、マトリクスLEDの16個の端子にArduinoを接続する方法で制御します。16個の端子のうち8個がアノード(プラス)で残り8個がカソード(マイナス)の端子になります。LEDの点灯箇所と端子の対応は以下のようになります。LEDモジュールの4辺(側面)には小さな凹凸があるので、それを手掛かりに向きを合わせて下さい。


pin_Conf.png

Arduinoからはダイナミック点灯という方法で制御することになります。そうすることで、合計16本の端子で64個のLEDを個別に制御することができます。ダイナミック点灯は、順番に一列(8個のLED)ずつ高速点灯させ、人間の目には8列全部が同時に点灯しているように見せる方法です。列ごとに点灯させる順番やタイミングをdigitalWrite()のHIGH/LOWの組合わせで制御します。基本的には、横方向の端子にプラスを、縦方向の端子にマイナスを接続することで、その交差した部分のLEDが点灯する仕組みになっています。
Arduinoのデジタル出力ピンは、通常0から13番までしかないのですが、pinMode()で設定することで、アナログ入力の6個のピン(0から5番ピン)もデジタル出力用に切り替えることができます。その場合、順番にデジタル出力14から19番のピンとして扱うことができ、合計で20個のデジタル出力が可能になります。もともとデジタル入出力ピンの0番と1番はシリアル通信などで使うので(プログラムをアップロードするときにも干渉することがあるので)、できれば接続しないほうがいいでしょう。今回は2番ピンから17番ピンまでを使うことにします。
LEDのカソード側(マイナス側)には抵抗(1KΩ)を取付けます。Arduinoのデジタル出力13番ピンには既に1KΩの抵抗が内蔵されているので、それ以外の7端子に取付けることとします。抵抗をつけなくても多少負荷はかかりますが実験はできます。ただし、Arduinoの13番ピンを接続している列(4列目)だけが、暗くなってしまいます。
接続方法は以下のようになります。ブレッドボードで実験する場合、大きなもの(幅のあるもの)を用意するか、小さなブレッドボードを2枚用意して、2枚にまたがるようにLEDモジュールを差し込むと作業しやすいと思います。


ard_matrix.png


それぞれのArduinoの出力ピンで行と列で表せば、以下のようになります。


ard_pin.png


まず、一行ずつ点灯させていきます。
PIN_2行目のPIN_10列目とPIN_12列目の二つを点灯させるためには、

PIN_2:HIGH
PIN_10:LOW
PIN_12:LOW

となりますが、同時に消灯させる列もあるので

PIN_11:HIGH
PIN_13:HIGH
PIN_14:HIGH
PIN_15:HIGH
PIN_16:HIGH
PIN_17:HIGH

とします。行(PIN_2〜PIN_9)、列(PIN_10〜PIN17)とすれば、
点灯させるには、

行:HIGH、列:LOW

という組合わせになり、
消灯させるには、

行:HIGH、列:HIGH
行:LOW、列:HIGH
行:LOW、列:LOW

という3つの組合わせがあります。列(縦)側の端子と行(横)の端子の両方をHIGH(5V)にすると消灯するということを覚えておいて下さい(電位差が0Vになるので)。それ以外の「LOW:HIGH」、「LOW:LOW」の組合わせでも消灯します。

次のPIN_3行目に移る前に(ある程度の時間点灯させた後に)PIN_2行目をLOWにすることで、次回PIN_3行目を制御するときにPIN_2行目が点灯しないように後処理しておきます。PIN_3行目についても同様の手順で行い、合計8回高速に繰り返すことで、全体が点灯しているように見えます。

まずは、以下のプログラムで、64個のLEDが順番に個別に点灯するか実験してみます。

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
void setup(){
//16本のピン(2~17)を出力に設定
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
}
}
void loop(){
//行(横)の繰り返し処理
for(int i=2;i<=9;i++){ //行(2~9番ピン)
digitalWrite(i,HIGH); //HIGHで点灯

//列(縦)の繰り返し処理
for(int j=10;j<=17;j++){ //列(10~17番ピン)
digitalWrite(j,LOW); //LOWで点灯
delay(100); //点灯時間
digitalWrite(j,HIGH); //列をオフにする
}

digitalWrite(i,LOW); //行をオフにする
}
}

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

それぞれ一つずつ順番に点灯していけば、配線などに間違いがないということになります。プログラムの順番としては、1行目の中の1列目から8列目までを順番に点灯し、次に2行目の中の1列目から8列目までを順番に点灯し、同様に8行目まで繰り返します。delay(100)の部分が一つのLEDの点灯時間であり、0.1秒に設定されています。この点灯時間を短くしていくと、残像現象により一度に複数のLEDが点灯しているように見え始めます。次のサンプルでは、点灯時間を0.03秒に設定し、二次元配列を用いて、予め用意しておいた表に基づいて点灯させる方法を行います。

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
boolean matrix[8][8]={
{0,0,0,1,1,0,0,0},
{0,0,1,0,0,1,0,0},
{0,1,0,0,0,0,1,0},
{0,1,0,0,0,0,1,0},
{0,1,0,0,0,0,1,0},
{0,1,1,1,1,1,1,0},
{0,1,0,0,0,0,1,0},
{0,1,0,0,0,0,1,0}
};

void setup(){
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
}
void loop(){
for(int i=2;i<=9;i++){
digitalWrite(i,HIGH); //行:HIGHで点灯
for(int j=10;j<=17;j++){
if(matrix[i-2][j-10]==1){//点灯条件
digitalWrite(j,LOW); //列:LOWで点灯
}
//上のif文のかわりに以下でも可
//digitalWrite(j,!matrix[i-2][j-10]);

delayMicroseconds(300);//0.03秒点灯
digitalWrite(j,HIGH);//オフにする
}
digitalWrite(i,LOW);//オフにする
}
}

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


8×8の二次元配列matrix(名前は任意)を用意して、その配列内に0か1で消灯/点灯の表をつくります。matrix[行][列]という対応になります。matrix[0][3]であれば、0行3列目の値となります。
このフォーマットをもとに、とりあえず「A」という文字をつくってみました。表の「1」のところを点灯させるために、if文で条件設定し、表座標の値が「1」なら、その箇所をLOWで出力します。今回接続しているピンの番号と配列の順番の数値のつじつまを合わせるために、「matrix[i-2][j-10]」としています(行:2番ピンが0番目の内部配列に対応するので[i-2]、列:10番ピンが内部配列内の0個目の値に対応するので[j-10]になります)。if文を使わずに、digitalWrite(j,!matrix[i-2][j-10])と書いても同じことになります。今回は、LOWで点灯するので、「!」を使って表座標の値が「0」のとき「1」(HIGH)になり、「1」のとき「0」(LOW)になるように反転します。
delayMicroseconds(300)は、一つずつ高速に点滅する時間です。高速なので点滅しているようには見えませんが、もし点滅しているように見えてしまう場合は数値を低くして、点滅のスピードを上げて下さい。
このように二次元配列matrixを使うことで、64個分のLEDの点灯/消灯状態を指定して表示可能になります。

次は表示文字をカスケーディング(文字が流れるように動く)してみたいと思います。左向きに文字が流れるようにするには、

matrix[k][l]=matrix[k][l+1];

というように、配列内の縦一列の値を右隣の値(+1の列の値)に移し替えればいいことになります。さらに、左側へ流れた文字が再び右側から出てくるように繰り返して表示されるようにするためには、画面右端の8列目の値(配列内7番目の値)が、1列目(配列内0番目の値)になるようにします。使用している二次元配列は8×8ですが、余白をもう一列つけたして8×9にしておきます。それで、

matrix[k][8]=matrix[k][0];

とすれば、余白である9列目(配列内の8番目の値)に1列目(配列内の0番目の値)が代入され、繰り返し表示されることになります。しかしこのままでは、横に流れるスピードが速すぎるので、while文を用いて表示される時間を引き延ばします。while文では、以下のように( )内に条件を入れ、その条件が満たされている限り繰り返し処理を行います。

int count=5;
while(count>0){
//繰り返される内容をここに書く

count--; //カウント数を減らしていく
}

という書き方をすれば、while( ){...}内の処理を5回繰り返すということになります。つまり、先ほどのLEDを順番に点灯させるプログラム全体をwhile( ){...}で括ってしまうということになります。以下のプログラムでは、1ループの中で、LEDを表示させる処理を5回繰り返し、それからカスケーディングのための処理を1回行う内容になります。


::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//余白の列を付けたし配列を8x9にしておく
boolean matrix[8][9]={
{0,0,0,1,1,0,0,0,0},
{0,0,1,0,0,1,0,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,1,1,1,1,1,0,0},
{0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0}
};

void setup(){
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
}
void loop(){
int count=5;//この値を大きくすればゆっくり流れる
while(count>0){
for(int i=2;i<=9;i++){
digitalWrite(i,HIGH); //行:HIGHで点灯
for(int j=10;j<=17;j++){
if(matrix[i-2][j-10]==1){//点灯条件
digitalWrite(j,LOW); //列:LOWで点灯
}
//上のif文のかわりに以下でも可
//digitalWrite(j,!matrix[i-2][j-10]);

delayMicroseconds(300);
digitalWrite(j,HIGH);//オフにする
}
digitalWrite(i,LOW);//オフにする
}
count--;//回数カウント1回減らす
}

//カスケーディング
for(int k=0;k<=7;k++){
//0列目から余白の8列目まで計算する
for(int l=0;l<=8;l++){
if(l==8){ //配列8列目は0列目の値を代入
matrix[k][8]=matrix[k][0];
}else{ //それ以外の列は+1列の値を代入
matrix[k][l]=matrix[k][l+1];
}
}
}
}

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


また、配列を大きくして以下のようにすれば、複数の文字を表示できます。


::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//8x25の配列にする(25列目は余白)
boolean matrix[8][25]={
{0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0},
{0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0},
{0,1,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0},
{0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0}
};

void setup(){
for(int i=2;i<=17;i++){
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
}
void loop(){
//変更なし
int count=5;
while(count>0){
for(int i=2;i<=9;i++){
digitalWrite(i,HIGH);
for(int j=10;j<=17;j++){
digitalWrite(j,!matrix[i-2][j-10]);
delayMicroseconds(300);
digitalWrite(j,HIGH); //LED OFF
}
digitalWrite(i,LOW); //LED OFF
}
count--;
}

//カスケーディング
for(int k=0;k<8;k++){
//以下の配列数の値を変更しておく
for(int l=0;l<=24;l++){
if(l==24){
matrix[k][24]=matrix[k][0];
}else{
matrix[k][l]=matrix[k][l+1];
}
}
}
}

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


今回のプログラムでは、loop(){...}内に、while(){...}という小さなループがあり、その中に、for(){...}で横1行ずつの繰り返し処理を行い、さらにその中にもうひとつのfor(){...}で縦1列ずつの繰り返し処理を行うというように、何重にも繰り返しループの処理が組み込まれています。結果的なコードを見ると分かりにくいかもしれませんが、最初から順を追って考えていけば、その仕組みが見えてくると思います。