這一個 DIY 專案是「4×4×4 64 顆 LED 的互動式立方體」:用 Arduino Mega2560 做控制核心,外接麥克風模組與光感(LDR),讓立體光譜「聽得到、看得見」——音量越大,底層越亮;環境越暗,頂層抬頭;中層隨時間流動,像一顆會呼吸、會應答的水晶球。
為什麼要「多工」而不能直接 64 腳
64 顆 LED 若各自一個 IO,需要 64 腳——任何 Arduino 都給不起。解法是矩陣掃描(multiplexing):陣列在製作時把垂直方向的 4 顆 LED 陽極焊成一根「柱」(共 16 根),再把同一層 16 顆 LED 的陰極焊在一起成為「層」(共 4 層)。任一瞬間只導通一層,同時送出這一層 16 根柱子該亮的狀態;4 層輪流每秒掃幾百遍,人眼殘像就讀成「64 顆同時亮」——和螢幕的「逐行刷新」是同一個道理。
這樣只需要 16 柱 + 4 層 = 20 個數位腳位,而且每顆 LED 有 1/4 的時間在亮。Mega2560 有 54 個數位腳,從 D22 開始用,刻意避開 D0/D1(那是 USB Serial 的 RX/TX,接 LED 會干擾 Serial 和上傳程式)。
料件清單
| 料件 | 數量/規格 | 說明 |
|---|---|---|
| 4x4x4 LED 陣列 | 64 顆 3mm LED(本例清/藍)、16+4 pin 排針 | 陽極每 4 顆串成一柱(16 柱)、陰極每層共接(4 層) |
| Arduino Mega2560 | 1 片 | 用 20 個數位腳位:16 柱(D22~D37)+ 4 層(D38~D41) |
| 聲音感測器(咪頭模組) | 1 個(電子元件店的藍板電容咪) | 接 A0,類比值隨聲音大小跳動 |
| 光感 sensor(LDR) | 1 個(10kΩ 光電分壓) | 接 A1,亮度 → 電阻 → 模擬值 |
| ULN2003(或 P-MOS 陣列) | 1 片 | 層(陰極)一次要吸 16 顆的電流,IO 直接拉不動;ULN2003 每通道可吸 500mA |
| 電阻 | 220~330Ω × 16 | 柱(陽極)限流,每柱串一顆(1kΩ 會偏暗;依 LED 規格調整) |
| 杜邦線 / 麵包板或 PCB | — | 引腳 20+2,麵包板要夠大 |
| 5V 供電 | USB 即可 | 一次只亮一層,最多 16 顆 × 約 10mA ≈ 160mA;要調大電流更亮時再用獨立 5V |
接線總覽
| 物件 | 目標 |
|---|---|
| 16 根柱子(陽極) | 各串 220~330Ω → Mega D22~D37 |
| 4 層(陰極) | → ULN2003 OUT1~OUT4;IN1~IN4 ← Mega D38~D41;ULN2003 GND 接共地 |
| 咪頭模組 VCC/GND/OUT | 5V / GND / A0 |
| LDR 分壓(10kΩ 下拉) | 節點 → A1 |
| GND | 全部共地(最重要,浮地 = 亂亮) |
控制端程式(重點:掃描)
核心邏輯三件事:(1) 感測 每 30ms 讀一次咪頭/光感,把「音量→底 2 層,暗→頂層」寫進 cube[4][4][4] 模型;(2) 掃描 一次導通一層,送出該層 16 根柱子的狀態,4 層輪巡;(3) 每層停留 約 500µs,4 層一輪 2ms ≈ 500fps,每顆 LED 的亮度 ≈ 直驅的 1/4,所以限流電阻比一般直驅稍小——這是多工的公定代價:
/*
* 4x4x4 LED 立方體 — 聲音+光度互動版
* 硬體: Arduino Mega2560
* 16 根柱子(陽極) → D22..D37 (各串 220~330Ω 限流)
* 4 層(陰極) → ULN2003 OUT1..4,由 D38..D41 控制
* 麥克風模組 AIN → A0 (類比)
* 光感 (LDR 分壓) → A1
* D0/D1 保留給 Serial,不接 LED
*/
const uint8_t COL_PIN[16] = {22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37};
const uint8_t LAYER_PIN[4] = {38,39,40,41}; // HIGH = ULN2003 導通 = 該層接地
// cube[x][y][z]:x、y 決定哪一根柱子(柱 = x*4+y),z 是層(0 在最下面)
bool cube[4][4][4] = {0};
uint8_t layer = 0;
void setup() {
for (uint8_t i = 0; i < 16; i++) { pinMode(COL_PIN[i], OUTPUT); digitalWrite(COL_PIN[i], LOW); }
for (uint8_t i = 0; i < 4; i++) { pinMode(LAYER_PIN[i], OUTPUT); digitalWrite(LAYER_PIN[i], LOW); }
Serial.begin(115200);
}
void loop() {
/* ---------- 1. 讀感測 → 更新 cube ---------- */
static uint32_t lastSense = 0;
uint32_t now = millis();
if (now - lastSense > 30) { // 30ms 感測一次
lastSense = now;
int16_t mic = analogReadA0avg(20); // 平均20次降噪
int16_t light= analogReadA1avg(20);
uint16_t micDB = map(mic, 200, 800, 0, 1023); // 依你的音量範圍校正
uint16_t darkPct = map(light, 1023, 0, 0, 1023); // 越暗越大
/* 規則: 底層=音量, 頂層=光線, 中層=呼吸 */
uint8_t vol = (uint8_t)(micDB / 1024.0 * 4); // 0..3(音量4層)
uint8_t lig = (uint8_t)(darkPct/ 1024.0 * 4); // 0..3
uint32_t t = now / 200; // 呼吸節拍
for (uint8_t x = 0; x < 4; x++)
for (uint8_t y = 0; y < 4; y++)
for (uint8_t z = 0; z < 4; z++) {
if (z < 2) cube[x][y][z] = (z < vol); // 音量填底2層
else if (z == 3) cube[x][y][z] = (lig <= 1); // 暗 → 頂層亮
else cube[x][y][z] = ((t + x + y) % 4 == 0); // 中層流動
}
}
/* ---------- 2. 掃描:一次一層,4 層輪流,人眼殘像為「64 顆同時亮」 ---------- */
digitalWrite(LAYER_PIN[layer], LOW); // 先關掉上一層,避免換資料時殘影
layer = (layer + 1) & 3;
for (uint8_t c = 0; c < 16; c++) {
uint8_t x = c >> 2, y = c & 3;
digitalWrite(COL_PIN[c], cube[x][y][layer] ? HIGH : LOW); // 柱子拉高 = 供電
}
digitalWrite(LAYER_PIN[layer], HIGH); // 打開這一層
delayMicroseconds(500); // 4 × 0.5ms = 2ms/幀 → ~500 fps
}
/* 20 次類比取平均, 擋住模組的抖動 */
int16_t analogReadA0avg(uint8_t n){ long s=0; for(uint8_t i=0;i<n;i++){s+=analogRead(A0); delayMicroseconds(60);} return s/n; }
int16_t analogReadA1avg(uint8_t n){ long s=0; for(uint8_t i=0;i<n;i++){s+=analogRead(A1); delayMicroseconds(60);} return s/n; }
調試時最常遇到的三個坑
- 浮地=亂亮:陣列、ULN2003、感測模組的 GND 沒有共地,會看到「隨機一兩顆閃」——不是程式錯,是地線錯。共地是最優先的一條線。
- 太暗或某層特別暗:先看限流電阻是不是太大(1kΩ 在 1/4 佔空比下會偏暗,改 220~330Ω);某一層特別暗通常是 ULN2003 那一路沒接好或 GND 沒共到。
- 掃描太慢,閃爍肉眼可見:4 層一輪的總時間 > 20ms 就會看到「一層一層跑」。每層停留 300~800µs 之間都很穩;另外感測那段每次會卡住約 5ms,讀太頻繁也會讓畫面微閃。
玩法建議(感測→效果)
- 音量柱:底層亮 = 音量高;拍手/說話 = 立方體「長高」,最直觀的音頻視覺化。
- 暗室模式:LDR 越暗,頂層越亮 + 掃描頻率越慢,變成一盞「夜燈」;手遮光 → 瞬間熄滅。
- 風暴模式:音量 > 閾值 → 隨機閃爍 2 秒(類似警報燈),閾值在
map()裡調。 - 呼吸模式:中層隨
sin(t)慢速起伏,不用感測也好看,做「常亮桌面小物」用。
結語
這個專案把「多工、電流、時序」三個電子學核心一次走完:64 顆 LED 靠「快」而不是「多」跑起來——人眼看不見的是 500fps 的掃描,感覺到的是 64 顆同時亮。做完它,LED 矩陣、OLED、甚至 VGA framebuffer 的「逐行刷新」就全部通了。
更新紀錄
2026-10-28:修正接線架構。初版寫成「16 行 + 16 列、32 腳」,這和 64 顆 LED 對不起來(16×16 是 256 個交點),且用到了 Serial 專用的 D0/D1。現改為標準的「16 柱 + 4 層、20 腳」接法(D22~D41),程式、料件、電流估算與調試建議一併更新。
延伸閱讀



沒有留言:
張貼留言