搜尋此網誌

2013年10月31日 星期四

LED 小玩具之 4x4x4 Cube LED

幾年前剛玩 8051 時,想找個題目來實做,那時就打算來做個 Cube LED ,但由於經常性的加班,不但, Cube LED 完全沒起頭,連 8051 也只做了一塊燒錄版就停工了…XD

去年,開始接觸 arduino 這塊版子,又讓我興起實做 Cube LED 的念頭,不過,還是沒開工,理由是,那時有個更吸引我的題目 "四軸直昇機" ,所以, Cube LED 又被晾在一旁

前幾天,又在網路上看到別人發表的 Cube LED ,突然心一橫,我的 Cube LED 就開工了…….(話說,這也醞釀太久了吧…..






根據以前查的資料, Cube LED  最重要的有二個部份:
一個是 LED 的控制電路,就是要能佔用 MCU 最少的 PIN 腳數,然後能控制所有的 LED
另一個是 Cube LED 焊接的方式,要讓 LED 站的又直有整齊,這絕對是適合的工具和苦工搭配才能達到的

材料:
翠綠色 LED 64 ( 4x4x4 )
Shift Register (74HC595n) 3
Atmega328 1
NPN 電晶體 (1A) 4
洞洞版 1
塑膠柱 4
電容 4
16MHz 振盪器 1
排座 (5 ) 1

由於這個題目已經在腦中想很久了,所以電路的設計上很清楚的在腦海中 (自以為),就打算跳過畫電路圖這一步,直接上版子,不過,後來覺的還是應該要先把電路圖畫出來比較好,至於原因,後頭會再說明

正所謂"工欲善其事,必先利其器",想把 LED 組的又正又直,一個好的固定座是少不了的。於是乎拿出了一塊架子的腳座。利用原本有的孔,再畫出該挖洞的位置。


用鑽頭挖出剩下的 14 個孔 (有幾個孔似乎有點小偏)

再來就是把 LED 的腳依照不同的位置折出相對的角度,小小顆 LED ,不好固定,折了 64 顆後,手超痛的...

先把 LED 放上固定座試看看,擺放的方式儘量把腳都往內部折,避免 LED 的腳出現在立方體的外圍,還得考慮立方體的穩定度,所以打算把外圍 LED 的地線接成一圈,剛好可以保護內部那四顆 LED

組完一層後的樣子。

從固定座取出後的樣子。

終於四層都焊好了。

開始堆疊囉
二層

三層...

四層

這裡 LED 的控制方式是將 LED 的地線共接,每層的地線由一根 PIN 控制,而每層 LED 的正極再和下一層的 LED 正極相接,由一根 PIN 控制。所以,每次只亮一層的 LED (導通某一層的地線),然後再靠 16 PIN 控制要亮的 LED ,再循序換下一層,以極短的時間快速切換每一層的控制,就能達到以 20 PIN 控制 64 LED ,不過,由於是快速在每一層中切換,所以,每顆 LED 亮度會比原來減少 3/4 ,因此,我並沒有對每顆 LED 多接限流電阻。

LED, MCU, Shift Register 放上洞洞版。

最左邊是 Atmega328 的晶片座,中間底下那二顆是 Shift Register ,為什麼是二顆呢,這就是一開始談到,還是要先畫電路圖比較好的原因,由於沒有畫電路圖,所以,一直以為只要二顆 74HC595n 就可以控制 64 LED
一顆 74HC595 8 IO ,每一層是 16 LED ,所以只要 二顆 74HC595 就行了…..XD
這裡完全乎略了 LED 的地線控制,每層有一根地線,共需要 4 IO 來控制。
原本打算以 atmega328 IO 來控制,不過,會有控制訊號同步的問題。所以只好再放一顆 74HC595 來協同控制。
如果一開始就把線路圖畫出來,就能想到這個問題,那我就不會做 4x4x4 Cube LED 了,會改成 5x5x5 ,一層 5x5 = 25 再加上 5 根地線控制,總共 30 IO ,四顆 74HC595 只會浪費 2 IO ,做 4x4x4 Cube LED ,用三顆 74HC595 會浪費 4 IO……XD

再把振盪器和程式燒錄接口焊到版子上

由於擔心地線會有 16 LED 灌進來,所以用 NPN 電晶體來控制

完成品的樣子,正面只有元件

完成品的底部,所有的線路都走在底部

硬體設計完成後,接下來就是程式設計了..

測試程式:

/*
  Project : 
    Cube LED
  Module :
    LED module
  Description : 
    This module implement how to display information by led.
    
Version 0.1 :
  2013/10/29 -[Alen Chen] - Initial

*/

#define LED_DATA_PIN 2
#define LED_LATCH_PIN 3
#define LED_CLOCK_PIN 4

#define LED_ENABLE 7
#define LED_ALL_CLEAR 8

#define LED_DISPLAY_ON digitalWrite(LED_ENABLE, LOW);
#define LED_DISPLAY_OFF digitalWrite(LED_ENABLE, HIGH);


#define LED_MAX_NUM 24
#define LED_SHIFT_WIDTH 3  // 2^x
#define LED_SET_ONE_BIT(data,index) (data |= ((uint32_t)1 << index))

//  0       7 8       15        23
// |---- ----|---- ----|---- ----|

// [ 01 | 02 | 03 | 04 ]
// [ 05 | 06 | 07 | 08 ]
// [ 09 | 10 | 11 | 12 ]
// [ 13 | 14 | 15 | 16 ]

#define LED_FALT_MASE 0x00FFFF
#define LED_FLAT_CLEAR(x) (x &= ~LED_FALT_MASE)


// Layer  [High -> Low] : [ 1 | 2 | 3 | 4]
#define LED_LAYER_MASK 0x0F0000
#define LED_LAYER_CLEAR(x) (x &= ~LED_LAYER_MASK)
#define LED_LAYER_1_BIT  16
#define LED_LAYER_2_BIT  17
#define LED_LAYER_3_BIT  18
#define LED_LAYER_4_BIT  19

#define LED_LAYER_NUM 4
uint8_t ledLayerMap[LED_LAYER_NUM] = { LED_LAYER_1_BIT, LED_LAYER_2_BIT, LED_LAYER_3_BIT, LED_LAYER_4_BIT };

#define LED_FLAT_NUM 16
uint8_t ledStatus[LED_LAYER_NUM][LED_FLAT_NUM];
uint32_t durationTimer = 0;

#define LED_DELAY_TIME 20
uint32_t flushTime = 0;
uint32_t systemTime = 0;

void LED_send_data(uint32_t data)
{
 digitalWrite(LED_LATCH_PIN, LOW);
 for(int index = 0; index < (LED_MAX_NUM >> LED_SHIFT_WIDTH); index++)
 {
  shiftOut(LED_DATA_PIN, LED_CLOCK_PIN, MSBFIRST, (data >> (((LED_MAX_NUM >> LED_SHIFT_WIDTH) - index - 1) << LED_SHIFT_WIDTH)));
 }
 digitalWrite(LED_LATCH_PIN, HIGH);
}

void setup()
{
 pinMode(LED_DATA_PIN, OUTPUT);
 pinMode(LED_LATCH_PIN, OUTPUT);
 pinMode(LED_CLOCK_PIN, OUTPUT);
 pinMode(LED_ENABLE, OUTPUT);
 pinMode(LED_ALL_CLEAR, OUTPUT);

 for(int layer_index = 0; layer_index < LED_LAYER_NUM; layer_index++)
  for(int flat_index = 0; flat_index < LED_FLAT_NUM; flat_index++)
   ledStatus[layer_index][flat_index] = 0;

 LED_reset();
}

void LED_reset()
{
 digitalWrite(LED_ALL_CLEAR, LOW);
 delay(10);
 digitalWrite(LED_ALL_CLEAR, HIGH);
}

void LED_setOne(uint8_t layerIndex, uint8_t flatIndex, uint8_t value)
{
 ledStatus[layerIndex][flatIndex] = value;
}

void LED_setFlat(uint8_t layerIndex, uint8_t * value)
{
 for(int index = 0; index < LED_FLAT_NUM; index++)
  LED_setOne(layerIndex, index, value[index]);
}

void LED_setCube(uint8_t ** value)
{
 for(int layerIndex; layerIndex < LED_LAYER_NUM; layerIndex++)
  for(int flatIndex = 0; flatIndex < LED_FLAT_NUM; flatIndex++)
   LED_setOne(layerIndex, flatIndex, value[layerIndex][flatIndex]);
}

void LED_flushFlat(uint8_t layerIndex)
{
 uint32_t data = 0;

 if( ((flushTime & 0xC0) >> 6) == layerIndex)
  data = (uint32_t)1 << ((flushTime & 0x3C) >> 2);
  
 LED_SET_ONE_BIT(data, ledLayerMap[layerIndex]);
 LED_send_data(data);
}

void loop()
{
    systemTime++;
    if((systemTime % LED_DELAY_TIME) == 0)
    {
 flushTime++;
      if(flushTime > 0xFF) flushTime = 0;
    }

    for(int index = 0; index < LED_LAYER_NUM; index++)
 LED_flushFlat(index);
}



最後一步,把程式燒進去測試囉

~~~亮起來….
漂亮啦,自己做出來的就是不一樣,光是看起他這樣閃,就超滿足的

測試影片:


線路圖如下:


8 則留言:

  1. 您好
    請問方便請教您幾個問題嗎?

    回覆刪除
    回覆
    1. 想要加入配合 音樂閃爍
      要怎麼做到呢?

      刪除
    2. 若要配合音樂達到閃爍的目的,我想到的方式有二個,一個是由軟體來解析音樂,再送出閃爍的命令給 Cube LED ;另一個是在 Cube LED 上加置感測聲音的裝置,色析聲音後再直接控制 Cube LED。 例如: http://ant2sky.blogspot.tw/2014/05/blog-post.html

      大致上是這樣,歡迎討論。

      刪除
  2. 大哥你好 小弟我跟你一樣 led cube醞釀很久 直到最近下定決心要開工了 只是網路上沒辦法找到電路圖
    不知道大哥你是否可以給個電路圖讓我去研究一下((因為我要做rgb的 先從3*3*3開始 最終目標8*8*8 阿看到你這篇的圖文解說真的讓我相當的開心啊~~~~

    回覆刪除
  3. 請問可以給個信箱 方便問問題嗎~
    我現在是學生,也想做這個~

    回覆刪除
  4. 不好意思 請問一下 可以提供電路圖嗎? 感謝大大

    回覆刪除