LEDキューブを作ってみる
4x4x4の青色単色のLEDキューブ作りに挑戦です。
マイコンボードと接続してLEDを制御します。
LEDキューブとは
名前の通りLEDで立方体を作ります。
今回は、縦4個、高さ4個、奥行き4個の計64個のLEDで作ります。
回路設計
概要
シフトレジスタを2個使い、LEDを8個づつ計16個を点灯させます。
16個のLEDを1段として、4段構成でLEDキューブを作ります。
4段を高速に切り替えて表示をするダイナミック点灯で実現させます。
使用部品
マイコンボード:秋月電子「AE-RX210」
LED:秋月電子で購入した100本入りの青色LED
シフトレジスタ:TC74HC595AP 2個
トランジスタ:1815YCA25 4個
抵抗:390Ω 16本、2.4KΩ 4本
シフトレジスタICを使う
マイコンからのポートには限りがあるので、ポートから直接出力でLEDを制御するのではなく、シフトレジスターICを使用します。
シフトレジスタICを利用すると、マイコンからシリアル入力→パラレル出力してくれます。パラレル出力した先(8ビット)に、LED8本が出力されるように回路を設計します。
SCKにクロックを入力して、クロック立ち上がりごとにSIからのシリアルデータを、最上位ビットから順に1ビットづつシフトレジスタへ入力していきます。
8ビット分入力したところで、RCKにクロックを入力するとシフトレジスタのデータがストレージレジスタへ転送されて、QA~QHピンから8ビットのパラレルデータとして出力されます。
SCLR: シフトレジスタをクリアする。通常は「H」。
SCK: シフトレジスタへのクロック信号。
RCK:ストレージレジスタへのクロック信号。
G: 出力の有効無効設定。「L」で出力する。
SI :データのシリアル入力。SCKの立ち上がりでデータを取得(ラッチ)する。
回路図
フリーで使える「BSch3V」という回路図を書くソフトがあったので、このソフトで回路図を作成しました。
制作開始
最初にLEDキューブのキモであるLED部分の工作です。
まず、LEDを並べてはんだ付けするための治具の作成です。
今回は紙粘土で作成してみました。
1cm間隔でLEDをはめる穴を設けました。
LEDの足を曲げてLED同士をはんだ付けしていきます。
カソード側は共通で全てアースと接続、アノード側がシフトレジスタICと接続します。
LEDを治具にセットするとこんな感じです。
この4個のLEDセットを4列作成してはんだ付けすると1段分の作成完了です。
ここでちょっと動作確認してみましょう。
ブレッドボード上で回路を試作して、点灯実験です。
(シフトレジスタICが1つしかないので、8個づつの点灯で確認です。)
やった!無事に点灯です。
(後で見返したら抵抗を忘れてますね…)
前回作成したLED回路を3段目まで作成しました。
そして3段目まで重ねてはんだ付けしたまではよかったのですが…
LEDとLEDの隙間が狭すぎて、はんだ付けできない!
というわけ、1cm間隔ではなく、2cm間隔で作り直しです。
左が作り直した2cm間隔のもの、右が1cm間隔のもの。
制御回路を作成
LED部分を作ったので、次に制御回路を作成です。
シフトレジスターIC2個とLEDの電流制限用の抵抗です。それに、ダイナミック点灯用のトランジスタもはんだ付けします。
テスト的にマイコンボードと接続して、作り直したLEDキューブ1段分を接続して正しく点灯することを確認です。
点灯確認ができたので、1段ずつ制作したLEDキューブを重ねてはんだ付けします。はんだ付けの際は仮止め用のテープを貼って仮止め後、はんだ付けをします。
無事4段分LEDキューブをはんだ付け完了。これを基板へはんだ付けします。
最後に配線を行えば完成です。
最後にマイコンから点灯パターンのデータを出力して完成!
最後にソースコードです。
/***********************************************************************/
/* */
/* FILE :Test.c */
/* DATE :Tue, Oct 06, 2020 */
/* DESCRIPTION :Main Program */
/* CPU TYPE :RX210 */
/* */
/* This file is generated by Renesas Project Generator (Ver.4.53). */
/* NOTE:THIS IS A TYPICAL EXAMPLE. */
/* */
/***********************************************************************/
//#include "typedefine.h"
#ifdef __cplusplus
//#include <ios> // Remove the comment when you use ios
//_SINT ios_base::Init::init_cnt; // Remove the comment when you use ios
#endif
volatile int OneMsTimer01, OneMsTimer02, OneMsTimer03;
char serial_data[8][2];
#include "iodefine.h"
void init_mtu(void);
void main(void);
void set_pattern_data(char* data, char serial_data[8][2]);
void output_LED_data(char serial_data[8][2]);
void set_serial_data(char serial_data[8][2]);
void output_parallel_data(void);
void select_floor(int sel);
void timer_func(void);
void wait_msec(int set_msec);
#ifdef __cplusplus
extern "C" {
void abort(void);
}
#endif
/****************************************/
/* 点灯パターンデータ */
/****************************************/
char led_pattern[][4][17] =
{
//1段目 2段目 3段目 4段目
//ABCDEFGHABCDEFGH ABCDEFGHABCDEFGH ABCDEFGHABCDEFGH ABCDEFGHABCDEFGH
{"0001000100010001", "0001000100010001", "0001000100010001", "0001000100010001"}, // 0
{"0011000100010000", "0011000100010000", "0011000100010000", "0011000100010000"}, // 1
{"0111000100000000", "0111000100000000", "0111000100000000", "0111000100000000"}, // 2
{"1111000000000000", "1111000000000000", "1111000000000000", "1111000000000000"}, // 3
{"1110100010000000", "1110100010000000", "1110100010000000", "1110100010000000"}, // 4
{"1100100010000000", "1100100010000000", "1100100010000000", "1100100010000000"}, // 5
{"1000100010001000", "1000100010001000", "1000100010001000", "1000100010001000"}, // 6
{"0000100010001100", "0000100010001100", "0000100010001100", "0000100010001100"}, // 7
{"0000000010001110", "0000000010001110", "0000000010001110", "0000000010001110"}, // 8
{"0000000000001111", "0000000000001111", "0000000000001111", "0000000000001111"}, // 9
{"0000000000010111", "0000000000010111", "0000000000010111", "0000000000010111"}, // 10
{"0000000100010011", "0000000100010011", "0000000100010011", "0000000100010011"}, // 11
{"0001000100010001", "0001000100010001", "0001000100010001", "0001000100010001"}, // 12
{"0000000000000000", "0000000000000000", "0000000000000000", "0000000000000000"}, // 13
{"1111111111111111", "0000000000000000", "0000000000000000", "0000000000000000"}, // 14
{"0000000000000000", "1111111111111111", "0000000000000000", "0000000000000000"}, // 15
{"0000000000000000", "0000000000000000", "1111111111111111", "0000000000000000"}, // 16
{"0000000000000000", "0000000000000000", "0000000000000000", "1111111111111111"}, // 17
{"0000000000000000", "0000000000000000", "0000000000000000", "1111111111111111"}, // 18
{"0000000000000000", "0000000000000000", "1111111111111111", "0000000000000000"}, // 19
{"0000000000000000", "1111111111111111", "0000000000000000", "0000000000000000"}, // 20
{"1111111111111111", "0000000000000000", "0000000000000000", "0000000000000000"}, // 21
{"0000000000000000", "0000000000000000", "0000000000000000", "0000000000000000"}, // 22
{"0000000000000000", "0000000000000000", "0000000000000000", "0000000000000000"}, // 23
{"0010000010000000", "0000010000000000", "0000000000000000", "0000000000000000"}, // 24
{"0000000010010000", "0000000001100000"," 0001000000000000", "0000000000000000"}, // 25
{"0100100010010000", "0010000001100000"," 0001000010000000", "0000000000000000"}, // 26
{"0100101110010000", "0010010101100000"," 0001000010001000", "0000010000000000"}, // 27
{"0100101110010101", "0010010101101010"," 0001000010001000", "0010010000000000"}, // 28
{"1110101110010101", "0010010101101011"," 0001000010001001", "0010010001000000"}, // 29
{"1110101110011111", "0010110111101011"," 0001010110001001", "0010011001000000"}, // 30
{"1111101111011111", "0011110111111011"," 0001010110011001", "0010011001001000"}, // 31
{"1111111111111111", "0011111111111011"," 0001010110011001", "0010011001001000"}, // 32
{"1111111111111111", "0011111111111111"," 0001010110011101", "0010011001001000"}, // 33
{"1111111111111111", "1111111111111111"," 0001010110011101", "0010111001001000"}, // 34
{"1111111111111111", "1111111111111111"," 0011010111011101", "0010111001001000"}, // 35
{"1111111111111111", "1111111111111111"," 0011010111011111", "0010111001001100"}, // 36
{"1111111111111111", "1111111111111111"," 0011110111111111", "0010111011001100"}, // 37
{"1111111111111111", "1111111111111111"," 1011111111111111", "0010111011001100"}, // 38
{"1111111111111111", "1111111111111111"," 1111111111111111", "0010111011011100"}, // 39
{"1111111111111111", "1111111111111111"," 1111111111111111", "0011111011011101"}, // 40
{"1111111111111111", "1111111111111111"," 1111111111111111", "1011111111011111"}, // 41
{"1111111111111111", "1111111111111111"," 1111111111111111", "1111111111111111"}, // 42
{"1111111111111111", "1111111111111111"," 1111111111111111", "1111111111111111"}, // 43
{"1111111111111111", "1111111111111111"," 1111111111111111", "1011111111011111"}, // 44
{"1111111111111111", "1111111111111111"," 1111111111111111", "0011111011011101"}, // 45
{"1111111111111111", "1111111111111111"," 1111111111111111", "0010111011011100"}, // 46
{"1111111111111111", "1111111111111111"," 1011111111111111", "0010111011001100"}, // 47
{"1111111111111111", "1111111111111111"," 0011110111111111", "0010111011001100"}, // 48
{"1111111111111111", "1111111111111111"," 0011010111011111", "0010111001001100"}, // 49
{"1111111111111111", "1111111111111111"," 0011010111011101", "0010111001001000"}, // 50
{"1111111111111111", "1111111111111111"," 0001010110011101", "0010111001001000"}, // 51
{"1111111111111111", "0011111111111111"," 0001010110011101", "0010011001001000"}, // 52
{"1111111111111111", "0011111111111011"," 0001010110011001", "0010011001001000"}, // 53
{"1111101111011111", "0011110111111011"," 0001010110011001", "0010011001001000"}, // 54
{"1110101110011111", "0010110111101011"," 0001010110001001", "0010011001000000"}, // 55
{"1110101110010101", "0010010101101011"," 0001000010001001", "0010010001000000"}, // 56
{"0100101110010101", "0010010101101010"," 0001000010001000", "0010010000000000"}, // 57
{"0100101110010000", "0010010101100000"," 0001000010001000", "0000010000000000"}, // 58
{"0100100010010000", "0010000001100000"," 0001000010000000", "0000000000000000"}, // 59
{"0000000010010000", "0000000001100000"," 0001000000000000", "0000000000000000"}, // 60
{"0010000010000000", "0000010000000000", "0000000000000000", "0000000000000000"}, // 61
{"0000000000000000", "0000000000000000", "0000000000000000", "0000000000000000"}, // 62
{"0000000000000000", "0000000000000000", "0000000000000000", "0000000000000000"}, // 63
{"0000000000000000", "0000000000000000", "0000000000000000", "0000000000000000"}, // 64
{"0000000000000000", "0000000000000000", "0000000000000000", "0000000000000000"}, // 65
};
/****************************************/
/* タイマー(MTU0)の初期化 */
/****************************************/
void init_mtu(void)
{
// 割り込み優先順位を最高にする P309、P327
ICU.IPR[114].BYTE = 0x0F; // MTU0のTGIA割り込み
// 割り込み要求許可設定 P308、P325
ICU.IER[3].BYTE = 0x10;
// 割り込み要求許可設定 P308、P327
ICU.IER[0x0E].BIT.IEN2 = 1; // MTU0のTGIAの割り込み要求許可
// タイマーの設定の全体はP619を参照
// MTU0のTGRAのコンペアマッチ(P569)
// 立ち上がりエッジでカウント
// 内部クロック64分周(PCLKB/64) = 20000000 / 64 = 312500Hz
MTU0.TCR.BYTE = 0x23;
// ノーマルモードで動作(P572)
// MTU0のTGRAとTGRCレジスタは通常動作
MTU0.TMDR.BYTE = 0x00;
// MTU0のMTIOC0B、MTIOC0A端子の出力禁止(P574)
MTU0.TIORH.BYTE = 0x00;
// MTU0のTGIAの割り込み許可(P586)
MTU0.TIER.BYTE = 0x01;
// MTU0のTGRにコンペアマッチする値を設定
// カウントクロック = 312500Hz = 周期は0.0000032秒 = 31(0x001F) * 0.0000032秒 = 0.0000992秒 = 1m秒
MTU0.TGRA = 0x001F;
// MTU0のタイマカウンタの設定(P594)
MTU0.TCNT = 0x0000;
// MTU0のTCNTのカウント動作開始(P595)
MTU.TSTR.BYTE = 0x01;
}
/****************************************/
/* main関数 */
/****************************************/
void main(void)
{
int pat = 0, sel = 0;
OneMsTimer01 = -1;
OneMsTimer02 = -1;
OneMsTimer03 = -1;
// クロックソース選択の保護解除
SYSTEM.PRCR.WORD = 0x0A50B;
// クロックソースをメインクロック(20MHz)に設定(P203)
SYSTEM.SCKCR3.WORD = 0x0200;
// 周辺モジュールクロックB(PCLKB)とメインクロックの分周比を64分周に設定(P200)
SYSTEM.SCKCR.LONG = 0x00000000;
// メインクロック発振器動作設定(P208)
SYSTEM.MOSCCR.BYTE = 0x00;
// MTUモジュールストップ状態解除(P247)
SYSTEM.MSTPCRA.BIT.MSTPA9 = 0;
// クロックソース選択の保護
SYSTEM.PRCR.WORD = 0x0A500;
// MTU0の初期化
init_mtu();
// ポートを汎用IOとして設定(P508)
PORT1.PMR.BYTE = 0x00;
PORT3.PMR.BYTE = 0x00;
PORTH.PMR.BYTE = 0x00;
PORTC.PMR.BYTE = 0x00;
// ポートの出力に設定(P505)
PORT1.PDR.BYTE = 0xFF; // P17~P10
PORT3.PDR.BYTE = 0x1F; // P34~P30
PORTH.PDR.BYTE = 0x0F; // PH3~PH0
PORTC.PDR.BYTE = 0x0F; // PC3~PC0
// SCLRの設定
PORTH.PODR.BIT.B3 = 1;
// Gの設定
PORT3.PODR.BIT.B4 = 0;
// クロック出力の初期化
PORTH.PODR.BIT.B1 = 0; // SCK
PORTH.PODR.BIT.B2 = 0; // RCK
// 出力用クロック信号の初期化
PORTH.PODR.BIT.B0 = 0; // SI(CHIP 1)
PORT3.PODR.BIT.B3 = 0; // SI(CHIP 2)
while(1)
{
if(pat++ > 65)
{
pat = 0;
}
while(1)
{
// 段数を切り替えながらダイナミック点灯
select_floor(sel);
if(sel++ > 3)
{
sel = 0;
break;
}
set_pattern_data(led_pattern[pat][sel], serial_data);
output_LED_data(serial_data);
// ダイナミック点灯の周期
wait_msec(5);
}
wait_msec(500);
}
}
/****************************************/
/* 点灯パターンを設定 */
/****************************************/
void set_pattern_data(char* data, char serial_data[8][2])
{
int fig = 0, count = 0;
// CHIP2のデータセット
for(count = 7; count >= 0; count--)
{
if(data[fig++] == '1')
{
serial_data[count][1] = 1;
}
else
{
serial_data[count][1] = 0;
}
}
// CHIP1のデータセット
for(count = 7; count >= 0; count--)
{
if(data[fig++] == '1')
{
serial_data[count][0] = 1;
}
else
{
serial_data[count][0] = 0;
}
}
}
/****************************************/
/* LEDへデータを出力して発光させる */
/****************************************/
void output_LED_data(char serial_data[8][2])
{
set_serial_data(serial_data);
output_parallel_data();
}
/****************************************/
/* シリアルデータをシフトレジスタへ入力 */
/****************************************/
void set_serial_data(char serial_data[8][2])
{
int bit_count = 0;
// シフトレジスタへのクロック信号生成
while(bit_count < 8)
{
// 1msec周期用のウェイト
wait_msec(1);
if(PORTH.PODR.BIT.B1 == 0)
{
// CHIP1 1ビットずつのシリアルデータ出力
PORTH.PODR.BIT.B0 = serial_data[bit_count][0];
// CHIP2 1ビットずつのシリアルデータ出力
PORT3.PODR.BIT.B3 = serial_data[bit_count][1];
// クロック信号立上がり
PORTH.PODR.BIT.B1 = 1;
bit_count++;
}
else
{
// クロック信号立下がり
PORTH.PODR.BIT.B1 = 0;
}
}
}
/************************************************/
/* シフトレジスタに入力したデータをパラレル出力 */
/************************************************/
void output_parallel_data(void)
{
// ストレージレジスタへのクロック信号生成
wait_msec(1);
PORTH.PODR.BIT.B2 = 1;
wait_msec(1);
PORTH.PODR.BIT.B2 = 0;
}
/************************************************/
/* LEDキューブの発光させたい段数を指定 */
/************************************************/
void select_floor(int sel)
{
PORTC.PODR.BIT.B0 = 0;
PORTC.PODR.BIT.B1 = 0;
PORTC.PODR.BIT.B2 = 0;
PORTC.PODR.BIT.B3 = 0;
switch(sel)
{
case 0:
PORTC.PODR.BIT.B0 = 1;
break;
case 1:
PORTC.PODR.BIT.B1 = 1;
break;
case 2:
PORTC.PODR.BIT.B2 = 1;
break;
case 3:
PORTC.PODR.BIT.B3 = 1;
break;
}
}
/************************************************/
/* タイマーカウンター */
/************************************************/
void timer_func(void)
{
if(OneMsTimer01 >= 0) OneMsTimer01++;
if(OneMsTimer02 >= 0) OneMsTimer02++;
if(OneMsTimer03 >= 0) OneMsTimer03++;
}
/************************************************/
/* 指定秒数ウエイトする */
/************************************************/
void wait_msec(int set_msec)
{
OneMsTimer01 = 0;
while(OneMsTimer01 < set_msec)
;
OneMsTimer01 = -1;
}
#ifdef __cplusplus
void abort(void)
{
}
#endif