LED Cube作成② 【失敗】ダイナミック点灯型LED CubeのHW設計、実装、動作確認

ダイナミック点灯型 LED Cubeの作成

前回システム設計まで行っていたLED Cubeですが、いったんダイナミック点灯型のものを作成してみました。HW設計、実装、動作確認を説明した後で、ダイナミック点灯型のメリットデメリットに改めて触れていこうと思います。

HW設計

LED点灯(照度調節)回路

LEDの点灯は下記のような回路で行っています。
間違いがあるため、参考にはしないでください…

また、照度調節機能を下記で定めていましたが、各LEDに対して抵抗を挟んでいるだけです。以前のLED Cubeでは3つしか抵抗を接続していなかったため、点灯するLEDの個数によってLEDの照度が変化してしまうのは当然ですね…
LED Cube作成① システム設計


まずマイコンはATMEGA328Pを使用しています。Arduinoからソフトを書きこむことができ、マイコン単体で使用することができるため、とても使い勝手が良いです。秋月で買えます。

LEDは9個を同時に点灯できるようにしています。
LED_Stage1 = HIGH、LED_Stage2 = LOW、LED_Stage3 = LOWとしたばあい、LED1~LED9の任意のI/OポートをLOWにすることで、対応するD1~D9までのLEDが点灯します。
例えばLED1のみLOWとした場合は、LED_Stage1から供給される電圧がD1のLEDとR5の抵抗に印加され、D1のLEDのみが点灯します。
D10~D27のLEDには逆方向に電圧が印加されるため、LEDが点灯することはありません。

LED_Stage2のみをHIGHとした場合は、LED_Stage2から電圧が供給されるため、LED1~LED9の任意のI/OポートをLOWにすることで、対応するD10~D18までのLEDが点灯します。
LED_Stage3のみをHIGHとした場合も同様に、対応するD19~D27までのLEDが点灯します。
※後述しますが、ダイナミック点灯をさせたいため、HIGHとできるのはLED_Stage1、LED_Stage2、LED_Stage_3のうち一つのポートのみと想定ます。

図1 LED点灯回路

ここで電流の計算についてですが、ATMEGA328PのI/OポートはHIGHが5V、最大出力電流(定格)は一つのポートに付き40mAとなっています。

LEDの順方向電圧は2Vで、10mA程度の電流で十分に点灯するものを使用しています。

抵抗には5 - 2 = 3Vの電圧が印加されるため、270Ωを選定しました。これで抵抗に流れる電流は 3 / 270 = 11.1 mAとなります。
直列接続ですのでLEDにも11.1 mAの電流が流れ、I/OポートがHIGHの時、LEDを点灯させることができます。

はい、ここで間違いにお気づきでしょうか。
ATMEGA328Pの最大出力電流は一つのポートに付き40mAとなっています。
LED_Stage1 = HIGHのとき、LED1~LED9のすべてをLOWとしてしまうと、LED_Stage1では11.1 mA * 9 = 100 mAの電流を消費してしまうのです。

残念ながらATMEGA328Pは限界以上の電流を流し続けてしまうのです…
まだ壊れていませんがこのような使い方は良くありません。トランジスタを挟むなどして、電源系を別にしたほうがよさそうです。
そのうち改善版のLED Cubeを作成しようと思います。

点灯速度制御機能

下記の回路でLEDの照度を調節しています。
といってもPC0がアナログ入力となっているため、可変抵抗で変化させた入力電圧に応じて、LEDの点灯時間を調節しているだけです。

割り込み・点灯パターン選択回路

こちらも簡単で、INT0は割り込み入力のピンとなっており、スイッチが押された際に割り込みが走るようになっています。
割り込み処理の中で点灯モードの変更を行っています。

ソフトウェア設計

今回特にソフトウェア設計は行っていません…
思いつくままコードを書いているため、ひどいものになっています。
「//応急措置」などというコメントもありますね…
おすすめはしていませんが、万が一興味がある用でしたら解読してみてください。
※コードはArduinoで記載しています。

#include <stdio.h>
#include <stdarg.h>

const int DELAY_TIME_MILLIS = 1;
const int V_VAL_MAX = 515;
const int V_VAL_MIN = 0;
const double MAGNIFICATION_MAX = 2;
const double MAGNIFICATION_MIN = 0.5;
const int MODE_MAX = 3;

typedef struct
{
    bool timer_flg = false;
    unsigned long ref_time_millis = 0;
} Timer;

Timer light;
Timer one_millis;
Timer delay_for_interrupt;

double a, b;
int mode_num;

va_list args;

void led_light(int , ...);
void led_off();
unsigned long calc_elapsed_time(unsigned long ref_time_millis);
bool timer_millis(unsigned long time_millis, Timer *timer);
void mode_increment();

void mode_1();
void mode_2();
void mode_3();

void setup() { 
    pinMode(0, OUTPUT);
    pinMode(1, OUTPUT);
    pinMode(2, INPUT);
    pinMode(3, OUTPUT);
    pinMode(4, OUTPUT);
    pinMode(5, OUTPUT);
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    pinMode(8, OUTPUT);
    pinMode(9, OUTPUT);
    pinMode(10, OUTPUT);
    pinMode(11, OUTPUT);
    pinMode(12, OUTPUT);

    a = (MAGNIFICATION_MAX - MAGNIFICATION_MIN) / (V_VAL_MAX - V_VAL_MIN);
    b = MAGNIFICATION_MAX - a * V_VAL_MAX;

    mode_num = 1;
    attachInterrupt(0, mode_increment, RISING);
    
    mode_1();
}

void loop() {
}

void mode_1(){
    while(1){
        for(int i = 1; i <= 27; i++){
            led_light(1, 100, i);
        }
    }
}

void mode_2(){
    while(1){
        led_light(9, 100, 1, 2, 3, 4, 5, 6, 7, 8, 9);
        led_light(9, 100, 10, 11, 12, 13, 14, 15, 16, 17, 18);
        led_light(9, 100, 19, 20, 21, 22, 23, 24, 25, 26, 27);
    }
}

void mode_3(){
    while(1){
        int i = random(1, 10);
        led_light(1, 50, i);
        led_light(1, 50, i + 9);
        led_light(1, 100, i + 18);
        led_light(0, 300);
    }
}

//指定したLEDを点灯
void led_light(int arg_num, ...){    
    int arg_val[27];
    int tmp;
    int light_time;
    int led_cnt[3];
    int v_val;
    int mode_num_temp = mode_num;

    for (int i = 0 ; i < 3 ; i++){
        led_cnt[i] = 0;
    }
    
    //if (arg_num < 1) return;
    va_start(args , arg_num);

    //点灯時間格納
    light_time = va_arg(args , int);
    //点灯時間倍率変更0.5倍~2倍
    v_val = analogRead(A0); 
    light_time = int(light_time * (a * v_val + b));
    
    //数字を配列に格納
    for (int i = 0 ; i < arg_num ; i++) {
        arg_val[i] = va_arg(args , int);
    }

    //配列内の数字を昇順でソート
    for (int i = 0 ; i < arg_num ; i++) {
        for (int j = 0 ; j < arg_num ; j++) {
            if(arg_val[i] < arg_val[j]){
                tmp = arg_val[i];
                arg_val[i] = arg_val[j];
                arg_val[j] = tmp;
            }
        }
    }

    //段数が変わるタイミングを保存
    for (int i = 0 ; i < arg_num ; i++) {
        if(arg_val[i] <= 9){
            led_cnt[0]++;
        } else if(arg_val[i] <= 18){
            led_cnt[1]++;
        } else if(arg_val[i] <= 27){
            led_cnt[2]++;
        }
    }

    //点灯
    while(timer_millis(light_time, &light) == false){
        //9以下の数字が指定されていたら
        if(led_cnt[0] != 0){
            led_off();
            for (int i = 0 ; i < led_cnt[0] ; i++) {

                //応急措置
                if(arg_val[i] - 1 == 2){
                    digitalWrite(12, LOW);
                }else{
                    digitalWrite(arg_val[i] - 1, LOW);
                }  
            }
            digitalWrite(9, HIGH);
            while(timer_millis(DELAY_TIME_MILLIS, &one_millis) == false){
            }
        }

        //10以上18以下の数字が指定されていたら
        if(led_cnt[1] != 0){
            led_off();
            for (int i = 0 ; i < led_cnt[1] ; i++) {
                //応急措置
                if(arg_val[i + led_cnt[0]] - 1 - 9 == 2){
                    digitalWrite(12, LOW);
                }else{
                    digitalWrite(arg_val[i + led_cnt[0]] - 1 - 9, LOW);
                } 
            }
            digitalWrite(10, HIGH);
            while(timer_millis(DELAY_TIME_MILLIS, &one_millis) == false){
            }
        }

        //19以上27以下の数字が指定されていたら
        if(led_cnt[2] != 0){
            led_off();
            for (int i = 0 ; i < led_cnt[2] ; i++) {
                //応急措置
                if(arg_val[i + led_cnt[0] + led_cnt[1]] - 1 - 18 == 2){
                    digitalWrite(12, LOW);
                }else{
                    digitalWrite(arg_val[i + led_cnt[0] + led_cnt[1]] - 1 - 18, LOW);
                } 
            }
            digitalWrite(11, HIGH);
            while(timer_millis(DELAY_TIME_MILLIS, &one_millis) == false){
            }
        }
        
        //1つも数字が指定がされていなかったら
        if(led_cnt[0] == 0 && led_cnt[1] == 0 && led_cnt[2] == 0){
            led_off();
            while(timer_millis(DELAY_TIME_MILLIS, &one_millis) == false){
            }
        }
    }
    va_end(args);

    //ボタンを押されていたらモード移行
    if(mode_num_temp != mode_num){
        if(mode_num == 1){
            mode_1();
        } else if(mode_num == 2){
            mode_2();
        } else if(mode_num == 3){
            mode_3();
        }
    }
}

//全てのLEDを消灯
void led_off(){
    digitalWrite(0, HIGH);
    digitalWrite(1, HIGH);
    digitalWrite(3, HIGH);
    digitalWrite(4, HIGH);
    digitalWrite(5, HIGH);
    digitalWrite(6, HIGH);
    digitalWrite(7, HIGH);
    digitalWrite(8, HIGH);
    digitalWrite(9, LOW);
    digitalWrite(10, LOW);
    digitalWrite(11, LOW);
    digitalWrite(12, HIGH);
}

//計測開始時刻からの経過時間を返す
unsigned long calc_elapsed_time(unsigned long ref_time_millis){
    unsigned long cur_time_millis = millis();
    return cur_time_millis - ref_time_millis;
}

//指定時間が経過すると1を返す
bool timer_millis(unsigned long time_millis, Timer *timer){
    if(timer->timer_flg == false){
        timer->ref_time_millis = millis();
        timer->timer_flg = true;
    }
    if(calc_elapsed_time(timer->ref_time_millis) < time_millis){
        return false;
    } else {
        timer->timer_flg = false;
        return true;
    }
}

//モード番号切替(割り込み)
void mode_increment(){
    noInterrupts();
    bool v[5];
    for(int i = 0;  i < 5; i++){
        v[i] = 1;
    }
    while(v[0] == 1 || v[1] == 1 || v[2] == 1 || v[3] == 1 || v[4] == 1){
        v[4] = digitalRead(2);
        for(int i = 0; i < 4; i++){
            v[i] = v[i + 1];
        }
    }
    interrupts();//割り込み開始
    if(mode_num != MODE_MAX){
        mode_num++;
    } else {
        mode_num = 1;
    }
}

//

実装・動作確認

実際にLED Cubeを作成し、動作させてみました。
基板にはんだ付けするのは手間がかかるため、今回はブレッドボードを使用しています。

点灯モードの変更や速度制御ができていますが、ジャンパワイヤが邪魔ですね。
また作る機会があれば、もう少し見栄えが良くなるように、電流が定格値を超えないようにしようと思います。

まとめ

LED CubeのHW設計、実装、動作確認を行いました。自分のやりたかったことはできたため満足です。
そのうちもっと見栄え良いものを作りたいです。LED Cube作成に関する疑問点があれば、いつでもコメントください。
最後まで読んでくださりありがとうございました!


コメントを残す

メールアドレスが公開されることはありません。