すこしふしぎ.

VR/HI系院生による技術ブログ.まったりいきましょ.(友人ズとブログリレー中.さぼったら焼肉おごらなきゃいけない)

【arduino】3 * 3 LED Cube 【光らせてみた】

こんばんは.1000chです. iOSのprovisioningfileを更新しますがなかなか終わらない->実はchromeだとできないよ☆ という流れに唖然としています. せめてアラートだしてくださいよappleさん...

さて,今回は前回作ったLEDキューブの制御プログラム紹介です! 大量のLEDを制御する参考になればと思います. なお,LEDキューブの作り方自体は前回の記事を参考にしてください.

それではいきます.

ダイナミック点灯とは

arduinoを使ってLEDを制御するにはどうすればよいでしょう?

答えは簡単.抵抗とLEDをつなぎ,アノード側をarduinoのdigital pinに,カソード側をGNDにつなぎ,digital pinのHIGH/LOWを切り替えるだけ.

では,LEDキューブのように大量のLEDを同時に制御するにはどうすればよいでしょう?

大量のdigital pinがあれば話は簡単なのですが,333でも27個のpinが必要になります. こりゃーとてもじゃないが対応できない.

このような話は何もLEDキューブに限った話ではありません. 例えばこんなやつ.

f:id:ism1000ch:20140422234514j:plain

いわゆる"7セグ"とよばれるLEDです.これ,数字の各辺が1つ1つのLEDになっており,それら7つ+小数点の8つのLEDのon/offを制御することで数字を表現しているんですね. この例では4桁なので,8 * 4 = 32個ものLEDを制御する必要があります. ...これ,1つ1つ全制御するのはなかなか大変ですね.

このように大量のLEDを制御するための手法が,”ダイナミック点灯”というものです.

一言で言うと,「各桁を高速に,交互に点灯させる」という手法です.

人間の目には光学的に慣性が働くので(この表現が正しいかは置いといて),高速に様々なモノが光った場合,そのすべてが常時点灯しているように見るのです. よく漫画などで「早すぎて分身しているようにみえる..」なんてのがありますね,ようはそれのことです.

写真の例で言うと,まず9を点灯,次に9を消灯して1 を点灯,そして,4,0,また9.. と続けて行く感じです. これは,各桁の8つのLEDのうち,アノード側を共有し,カソード側を独立させることで実現できます(その逆もあります).点灯させたい側のカソードだけをLOWに,他をHIGHにすることで,アノード側を共有していても,対象となる桁だけが点灯できる,という仕組みですね.

詳しくは様々に解説しているサイトがありますので,ググってみてください.

LED Cube 制御プログラム

さて今回の場合,各段でカソードを共有し,縦に重ねた3つのLED間でアノードを共有しています.

f:id:ism1000ch:20140415141444p:plain

回路図はこんな感じです. 1段目だけ制御したいときはBase-1をLOW,Base-2/3をHIGHにしてLED1~9を制御します. 1段目を微小時間点灯したのち,次に2段目,3段目と順に制御します. あとはこれをひたすら繰り返します. これら一連の流れが行われたとき,すべての段が同時に点灯しているように見える,ということになります.

さて,以下が制御プログラムになります.

/*
 * LEDCude
 * created_at:2014_04_14_01:17:05.
 * Last Change:2014_04_22_23:36:42.
 */

// 各pinの設定.定義後配列化しておく
int BASE_0 = 12;
int BASE_1 = 13;
int BASE_2 =  2;
int LED_0  =  3;
int LED_1  =  4;
int LED_2  =  5;
int LED_3  =  6;
int LED_4  =  7;
int LED_5  =  8;
int LED_6  =  9;
int LED_7  = 10;
int LED_8  = 11;
int BASE_LIST[3] = {BASE_0,BASE_1,BASE_2};
int LED_LIST[9] = {LED_0,LED_1,LED_2,LED_3,LED_4,LED_5,LED_6,LED_7,LED_8};

// LEDキューブの状態.HIGH or LOWを格納
int led_state[3][3][3];
int LED_NUM = 27;

int COUNT_MAX = 30;
int count = 0;

void setup(){
    // 各pinのIN/OUT設定
    pinMode(BASE_0,OUTPUT);
    pinMode(BASE_1,OUTPUT);
    pinMode(BASE_2,OUTPUT);
    pinMode(LED_0 ,OUTPUT);
    pinMode(LED_1 ,OUTPUT);
    pinMode(LED_2 ,OUTPUT);
    pinMode(LED_3 ,OUTPUT);
    pinMode(LED_4 ,OUTPUT);
    pinMode(LED_5 ,OUTPUT);
    pinMode(LED_6 ,OUTPUT);
    pinMode(LED_7 ,OUTPUT);
    pinMode(LED_8 ,OUTPUT);

    // LEDキューブの状態初期化
    int i=0,j=0,k=0;
    for(i=0; i<3; i++){
        for(j=0; j<3; j++){
            for(k=0; k<3; k++){
                led_state[i][j][k] = LOW;
            }
        }
    }
    randomSeed(analogRead(0));
}

// openFrameworks風にループ内を分割
void loop(){
    update();
    action();
}

// 状態の更新
void update(){
    if(count >= COUNT_MAX){
        // ランダムに状態変化
        led_toggle(random(LED_NUM));
        count = 0;
    } else {
        count++;
    }
 }

// 状態の反映
void action(){
    blink_cube();
}

// **今回のポイント**
// LEDキューブをダイナミック点灯
void blink_cube(){
    int i=0,j=0,k=0;
    for(i=0; i<3; i++){
        // i番目の段以外はHIGH
        digitalWrite(BASE_0,HIGH);
        digitalWrite(BASE_1,HIGH);
        digitalWrite(BASE_2,HIGH);
        digitalWrite(BASE_LIST[i],LOW);
        
        // 同一段内の9つのLEDを制御
        for(j=0; j<3; j++){
            for(k=0; k<3; k++){
                digitalWrite(LED_LIST[j+k*3], led_state[i][j][k]);
            }
        }
        // 気持ち程度delay.なくてもok
        delay(2);
    }
}

// 指定番号のLEDの状態をトグル
void led_toggle(int num){
    int i,j,k;
    i = num / 9;
    j = (num % 9) / 3;
    k = num % 3;
    if(led_state[i][j][k] == HIGH){
        led_state[i][j][k] = LOW;
    } else {
        led_state[i][j][k] = HIGH;
    }
}

こんなかんじでうごいてます.

コレだけって地味だ!!w まぁ動作確認ということで.

arduinoは一度setup関数を呼んだ後,loop関数をひたすら繰り返してくれます. 今回はloop関数内にupdate/actionという二つの関数を用意し,openFrameworks風の制御構造にしてみました.

LED Cube点灯のポイントはblink_cube関数です.対象となる段のカソードのみLOWにして9つのLEDを制御,を繰り返しています. 一応delayを挟んでいますが,特に必要ないかもしれません. delay無しにしてtoggleの周期も短くしたりすると,全体常時点灯のようにみえるかもしれませんねw

それではみなさん,楽しい電子工作ライフを〜