今回は 4x4x4 の LEDキューブの作成に挑戦しました。製作はちょっと大変ですが、完成したときの達成感とその綺麗さは素晴らしいものです。
今回作ったものがこちら↓
写真で見るよりも実際はもっと綺麗に見えます。今回はこの LEDキューブの製作過程を紹介したいと思います。
制御の仕組み
作り方の前に LEDキューブの制御方法を簡単に説明したいと思います。
4x4x4 = 64 個の LED を直接制御するのは現実的ではないので、各縦列でアノードを共通化、各層でカソードを共通化します。(アノードとカソードはどちらでもいいです)
例えば1番上の層のカソードを GND に、縦の 16本のうち次の 5本を電源に繋げると(抵抗は忘れずに)その部分だけが点灯します。
これで1層分の表示ができますが、このまま他の層も一緒に点けようとすると他の場所まで点灯してしまいます。
ではどうするかというと、各層を一層ずつ表示して高速で切り替えることで、人間の目にはすべての層が点灯しているように見えます。
ちなみにこのような点灯方法をダイナミック点灯と呼び、複数の 7セグLED を点灯させるときなどにも使います。
これによって制御に必要なピンは、カソード4本+アノード 16本の 20本となります。しかし、アノード 16本はマイコンにはまだ少し多いので、シフトレジスタを使用します。
シフトレジスタの詳細については省略しますが、こんな感じでマイコンからのシリアルデータをパラレル出力してくれます。
シフトレジスタの制御は、データ、クロック、ラッチの3本だけでできるので、キューブの制御に必要なピンは4+3=7本で足りるということになります。
このくらいであればピンの少ないマイコンでも十分に足ります。
材料
今回使用した主なパーツ一覧です。値段は購入時の物なので変わっている可能性もあります。また、手元にあるパーツを優先して使ったので、他のパーツを使った方が簡単だったりすることもあるので参考程度にお願いします。
パーツ | 個数 | 値段 |
青色LED 3mm | 64 | \224 |
抵抗 100Ω | 16 | \16 |
PIC16F18326 | 1 | \130 |
シフトレジスタ SN74HC595D | 2 | \80 |
トランジスタアレイ 7chシンク TD62003APG | 1 | \40 |
加速度センサー MMA8452Q | 1 | \350 |
ジェスチャーセンサー APDS9960 | 1 | \600 |
LCDモジュール 8×2 | 1 | \600 |
薄型ボリューム | 1 | \60 |
電源用microUSBコネクタ&DIP化基板 | 1 | \80 |
三端子レギュレータ 3.3V | 1 | \50 |
コンデンサ 470μF 0.1μF | 1 | \20 |
スライドスイッチ | 3 | \80 |
ピンソケット 2×7 | 1 | \30 |
L型ピンソケット 1×6 | 1 | \20 |
ICソケット | 2 | \20 |
片面紙エポキシユニバーサル基板 Bタイプ | 2 | \240 |
SOP16ピン DIP変換基板 | 2 | \60 |
ねじ・ナット・袋ナット・スペーサー・ | 4 | \200 |
計 | \2900 |
多少値の張るセンサーや LCD は LEDキューブ自体には必要ないのでシンプルな機能だけならばもっと安くなります。
製作の流れ
続いて大まかな製作の流れを説明します。LEDキューブの製作は以下のような流れになっています。
- 治具製作
- LED加工
- キューブ組み立て
- 回路組み立て
- プログラミング
それぞれの過程についてはこれから詳しく説明していきます。
治具製作
まず初めに LED を綺麗に並べるための治具を製作します。
今回はダイソーで買ってきた MDF で作りました。それがこちら↓
このように MDF に等間隔に穴をあけていきます。穴の直径は LED と同じで大丈夫です。治具が歪んでいるとキューブも歪んでしまうので手を抜かずに作りましょう。
今回は(設計の問題もありますが)この治具の作りが甘かったために後々痛い目を見ることに・・・
LED加工
続いて LED を加工していきます。
この過程でやることは次の2つです。
- 下からの光透過対策
- LEDの足曲げ
まず光透過とは何かというと、LED に真下から光を当てると下の写真左のように光っていない LED も光って見えてしまいます。
その対策として LED の底面にシールを貼ったものが右側です。
見栄えはよくなりますが、かなり面倒な作業なのであまり気にしない人は何もしなくても良いと思います。
3mm のシールがあればよかったのですが、百均で探してみても1番小さいもので 5mm の物しかありませんでした。
5mm の LED を使う場合はこれに穴を開ければそのまま使えるので良いですが、今回は 3mm なので、これをさらに小さく切りました。
こんな感じに裏に 3mm の円を並べたものを印刷してそれを目安に切りました。めちゃくちゃ面倒臭かったので正直別の方法を考えた方が良いと思います。
これを貼れば透過対策は完了です。
続いて足曲げ加工。今回はアノードを縦に繋ぐので、このようにアノードを外側に少し曲げます。
・・パッと見で分かると思いますが、ここは完全に目分量でやったので幅がバラバラです・・綺麗なキューブを作るためにはここもきちんとそろえた方が良いでしょう。
キューブ組み立て
治具を製作し、LED の加工が終わったらいよいよキューブを組み立てていきます。
まず治具に LED を植えます。
続いてカソードを曲げます。
足りない1辺はスズメッキ線なんかで繋ぎましょう。
ちなみに先程治具を作った段階で後々痛い目を見たと書きましたが、その様子がこちら。
結構隙間が空いています。まあギリギリの設計にしたせいでもあるんですけど、治具の精度の重要性にこのときようやく気が付きました。結局頑張って無理やりはんだ付けしました。
はんだ付けをしたら壊さないように気を付けて治具から外すと、このようにキューブの1層が出来上がります。
これを4層作ります。
この後は、先に4層を繋げてキューブを作ってから基板に乗せるか、基板の上でキューブを組み立てるかのどちらかです。今回は後者にしましたのでキューブは一旦置いといて回路の製作に移ります。
回路の組み立て
とても見にくいと思いますが、回路図を載せておきます。
PIC と PICKit3 の電源ラインが周りと切り離せるようにしているのは、基板に乗せたままプログラムの書き込みが出来るようにするためです。(切り離さないとセンサーなどにも 5V がかかってしまう)
RA0 は普段は可変抵抗に繋げる入力ピンとしますが、プログラムを書き込むときには同様に切り離します。
これを基板に実装していきますが、今回は上下2層の基板で構成することとしました。
なんとなく回路が出来上がる様子を GIF にしてみました。
回路が出来たらキューブを組み立てます。内側のはんだ付けは結構大変ですが頑張りましょう。
キューブを組んだら各層を基板に繋げるための足も忘れずに伸ばして完成です。
プログラミング
キューブが出来上がったらプログラミングに移ります。
ここでは LEDキューブのデータをキューブに表示するところまで解説したいと思います。
まずデータの形式について説明しますが、今回は配線の都合上シフトレジスタの出力と LED の対応は下図のようになっています。(上から見た図)
15が先頭ビット、0 が最終ビットです。
1層が 16bit = 2byte なので4層で 8byte のデータが必要なことになります。
キューブ1つのデータを char[8] の配列で管理することにしました。配列の各バイトとキューブの対応関係は下図のようにします。
ここで、例としてこちら↓の縁取りキューブのデータを考えてみると次のようになります。
次に、このデータを出力する関数を作ります。
が、その前に層を指定する関数と、16ビットをシフトレジスタに送信する関数を低レベル関数として用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
void SetLayer(char layer){ //指定した層だけON switch(layer){ case 0: L0 = 1; L1 = 0; L2 = 0; L3 = 0; break; case 1: L0 = 0; L1 = 1; L2 = 0; L3 = 0; break; case 2: L0 = 0; L1 = 0; L2 = 1; L3 = 0; break; case 3: L0 = 0; L1 = 0; L2 = 0; L3 = 1; break; } } void Shift16(int data){ for(signed char i=15; i>=0; i--){ //上位ビットから出力 SI = (data>>i)&1; SCK = 1; SCK = 0; } RCK = 1; RCK = 0; } |
L0~L3, SI, SCK, RCK は#define で該当のピンを指定しておきます。
ちなみに層はカソードコモンなのに出力が Low ではなく High なのは、トランジスタで吸い込むようにしているためです。
この関数を使って先程のデータとそのビットを反転した2つのデータを交互に表示するプログラムを書いてみたのがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#pragma config FEXTOSC = OFF // FEXTOSC External Oscillator mode Selection bits (Oscillator not enabled) #pragma config RSTOSC = HFINT1 // Power-up default value for COSC bits (HFINTOSC (1MHz)) #pragma config CLKOUTEN = OFF // Clock Out Enable bit (CLKOUT function is disabled; I/O or oscillator function on OSC2) #pragma config CSWEN = ON // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed) #pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled) #pragma config MCLRE = ON // Master Clear Enable bit (MCLR/VPP pin function is MCLR; Weak pull-up enabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config WDTE = OFF // Watchdog Timer Enable bits (WDT disabled; SWDTEN is ignored) #pragma config LPBOREN = OFF // Low-power BOR enable bit (ULPBOR disabled) #pragma config BOREN = OFF // Brown-out Reset Enable bits (Brown-out Reset disabled) #pragma config BORV = LOW // Brown-out Reset Voltage selection bit (Brown-out voltage (Vbor) set to 2.45V) #pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit (The PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a Reset) #pragma config DEBUG = OFF // Debugger enable bit (Background debugger disabled) #pragma config WRT = OFF // User NVM self-write protection bits (Write protection off) #pragma config LVP = OFF // Low Voltage Programming Enable bit (High Voltage on MCLR/VPP must be used for programming.) #pragma config CP = OFF // User NVM Program Memory Code Protection bit (User NVM code protection disabled) #pragma config CPD = OFF // Data NVM Memory Code Protection bit (Data NVM code protection disabled) #include <xc.h> #define _XTAL_FREQ 16000000 #define L0 RA2 #define L1 RC0 #define L2 RC1 #define L3 RC2 #define SI RC3 #define RCK RC4 #define SCK RC5 const char data[2][8] = {{0x9F,0xF9,0x09,0x90,0x09,0x90,0x9F,0xF9} ,{0x60,0x06,0xF6,0x6F,0xF6,0x6F,0x60,0x06}}; char pattern = 0; void init(){ OSCFRQ = 0x06; //3 : 4MHz/ 4 : 8MHz/ 6 : 16MHz/ 7 : 32MHz OSCCON1bits.NDIV = 0x0; //Clock Divider 1/1 TRISA = 0b00111001; TRISC = 0b00000000; ANSELA = 0b00000001; ANSELC = 0b00000000; WPUA = 0b00000000; WPUC = 0b00000000; PORTA = 0b00000000; PORTC = 0b00000000; } void SetLayer(char layer){ switch(layer){ case 0: L0 = 1; L1 = 0; L2 = 0; L3 = 0; break; case 1: L0 = 0; L1 = 1; L2 = 0; L3 = 0; break; case 2: L0 = 0; L1 = 0; L2 = 1; L3 = 0; break; case 3: L0 = 0; L1 = 0; L2 = 0; L3 = 1; break; } } void Shift16(int data){ for(signed char i=15; i>=0; i--){ SI = (data>>i)&1; SCK = 1; SCK = 0; } RCK = 1; RCK = 0; } void Describe(){ static char z = 0; z = (z+1) % 4; Shift16((data[pattern][z*2 + 1]<<8) + data[pattern][z*2]); SetLayer(z); } void interrupt isr(){ if(TMR1IF == 1){ //Description TMR1IF = 0; Describe(); } } int main() { init(); GIE = 1; PEIE = 1; TMR1IE = 1; T1CON = 0b01000001; //Timer 1 4ms while(1){ __delay_ms(1000); pattern = (pattern+1) % 2; } } |
Describe関数をタイマ1で4ms毎に呼び出し、各層のデータを出力します。
表示をタイマ割り込みで行うことによって、main関数では自由にパターンを切り替えることができます。
その他のポイントとしては、data変数を const宣言することと、Describe関数中の z変数を static宣言することです。
データは定数なので、const宣言をすることで PIC のデータメモリではなくプログラムメモリに書き込まれるようになります。これでより多くのデータを保存できます。
staticについては C言語の基本的事項ですが、これをつけないと関数が終了される度にローカル変数は破棄されてしまうので毎回 z = 0 になってしまいます。
私が製作した LED キューブでは、加速度センサーやジェスチャーセンサー、LCD などを搭載していますが、プログラムの解説はこの基本部分だけにしたいと思います。
また、プログラムとは直接は関係ありませんが、この先を進めるにあたって問題となる点があります。それはデータの作成です。
先程は手計算でデータを作りました。少しなら別に良いですが、たくさんのデータを作ろうと思うととても大変です。
そのため、データを自動で作成してくれるソフトを Unity で作りました。
出力したデータはメモ帳にコピペして保存するというお粗末なものですが、自分で使うだけなので細かいことは気にせず作りました。
ソフトを作るのは手間ですが、それ以上にデータを手動で作るのは手間なので、作る価値は十二分にあると思います。
今回私が制作したものはこちらからダウンロードできるようにしておいたので、気になる方は使ってみてください。
今回の記事は以上です。ここまで見ていただきありがとうございます。最後に、動画をニコニコにあげたのでそちらも見てくれると嬉しいです。
初めまして。
きっちーさん
KT といます。
サイトためになり、大変参考にさせて頂いています。
大変厚かましいお願いですが、
LEDキューブの作り方!4x4x4 LED CUBE を作ってみた
で作成された Unity プログラム
を教えていただくことは出来ないでしょうか?
対価も支払うことも、検討しています。
よろしくお願いいたします。
コメントありがとうございます。
対価はこの記事を読んで実際に作ろうと思ってもらえるだけで十分です。
完成品でよろしければダウンロードできるようにしておいたのでご活用ください。
プログラムの中身が必要等ありましたらまた相談してください。
きっちーさん
kTです。
お忙しいところ恐縮です。
早速のご返答ありがとうございます。
早速ダウンロードさせていただきます。
厚かましいお願い聞いていただき
ありがとうございました。
また、何か有れば、相談させて頂きます。