// 🚦 ATmega2560 交通號誌 + 4 位七段倒數顯示 + 雙向通行
// ---- 交通號誌 LED 腳位 ----int TG1 = 24, TY1 = 23, TR1 = 22;int TG2 = 27, TY2 = 26, TR2 = 25;int TG3 = 32, TY3 = 31, TR3 = 30;int TG4 = 35, TY4 = 34, TR4 = 33;
// ---- 按鈕腳位 ----int sw1 = 50, sw2 = 51, sw3 = 52, sw4 = 53;
// ---- 四位七段模組設定 ----// 段腳(A ~ G)int segPins[7] = {38, 39, 40, 41, 42, 43, 44};// 位元選擇腳(digit 控制腳),共四個,但我們只用前兩個int digitPins[4] = {46, 47, 48, 49};
// 數字對照表(共陰型)byte numPatterns[10][7] = { {0,0,0,0,0,0,1}, //0 {1,0,0,1,1,1,1}, //1 {0,0,1,0,0,1,0}, //2 {0,0,0,0,1,1,0}, //3 {1,0,0,1,1,0,0}, //4 {0,1,0,0,1,0,0}, //5 {0,1,0,0,0,0,0}, //6 {0,0,0,1,1,1,1}, //7 {0,0,0,0,0,0,0}, //8 {0,0,0,0,1,0,0} //9};
// ---- 控制變數 ----unsigned long previousMillis = 0;unsigned long displayMillis = 0;int mode = 1; // 模式:預設自動int state = 0; // 狀態機(0,1,2,3)int subState = -1; // 協助每個狀態第一次初始化int countdown = 0; // 倒數秒數(0~99)
bool blinkState = false;
// 黃綠燈時間設定(秒)int greenTime = 20;int yellowTime = 5;
// ---- setup() ----void setup() { // 設定交通號誌為輸出 pinMode(TG1, OUTPUT); pinMode(TY1, OUTPUT); pinMode(TR1, OUTPUT); pinMode(TG2, OUTPUT); pinMode(TY2, OUTPUT); pinMode(TR2, OUTPUT); pinMode(TG3, OUTPUT); pinMode(TY3, OUTPUT); pinMode(TR3, OUTPUT); pinMode(TG4, OUTPUT); pinMode(TY4, OUTPUT); pinMode(TR4, OUTPUT);
// 段腳輸出 for (int i = 0; i < 7; i++) { pinMode(segPins[i], OUTPUT); } // digit 腳輸出 for (int i = 0; i < 4; i++) { pinMode(digitPins[i], OUTPUT); } // 按鈕腳位輸入(內建上拉) pinMode(sw1, INPUT_PULLUP); pinMode(sw2, INPUT_PULLUP); pinMode(sw3, INPUT_PULLUP); pinMode(sw4, INPUT_PULLUP);
// 預設關閉所有燈,顯示 00 allOff(); countdown = 0; // 顯示初始化:先全部位元關閉 for (int d = 0; d < 4; d++) { digitalWrite(digitPins[d], HIGH); // 共陰:HIGH 表關 }}
// ---- loop() ----void loop() { // 掃描按鈕(防彈跳) if (digitalRead(sw1) == HIGH) mode = 1; else if (digitalRead(sw2) == HIGH) mode = 2; else if (digitalRead(sw3) == HIGH) mode = 3; else if (digitalRead(sw4) == HIGH) mode = 4;
switch (mode) { case 1: autoCycle(); break; case 2: blinkMode(); break; case 3: manualMode(); break; case 4: allOff(); break; }
// 更新顯示(multiplex 顯示兩位倒數數字) updateDisplay();}
// ---- 顯示更新:multiplex 四位顯示(但實際只顯示 two 位) ----void updateDisplay() { // 取得十位與個位數字 int tens = countdown / 10; int ones = countdown % 10;
unsigned long now = millis(); // 我們每次顯示一位,間隔大約 5 ms(視覺恆亮) static int currentDigit = 0; static unsigned long lastSwitch = 0; unsigned long interval = 5; // 每 5ms 切換一次位元
if (now - lastSwitch >= interval) { lastSwitch = now;
// 關掉所有位元 for (int d = 0; d < 4; d++) { digitalWrite(digitPins[d], HIGH); // 共陰:HIGH 關閉 }
// 只顯示我們用的那兩位:假設使用 digitPins[0](個位)與 digitPins[1](十位) if (currentDigit == 0) { // 顯示個位 lightDigit(0, ones); digitalWrite(digitPins[0], LOW); // 共陰:LOW 開啟該位 } else if (currentDigit == 1) { // 顯示十位 lightDigit(1, tens); digitalWrite(digitPins[1], LOW); } // 若有第三位或第四位未使用,可保持關閉 // 下一次顯示下一位 currentDigit++; if (currentDigit >= 2) currentDigit = 0; }}
// 點亮某一位數字(但尚未啟用該 digitPin,要在外部切換位元腳)// digitIndex 0 表個位;1 表十位void lightDigit(int digitIndex, int num) { // 根據 numPattern 設定各段 for (int i = 0; i < 7; i++) { bool on = numPatterns[num][i]; // 共陰:段腳設為 HIGH 表示點亮 digitalWrite(segPins[i], on ? HIGH : LOW); }}
// ---- 模式 1:自動循環 + 倒數顯示 ----void autoCycle() { unsigned long currentMillis = millis();
// 每秒更新倒數顯示 if (currentMillis - displayMillis >= 1000) { displayMillis = currentMillis; if (countdown > 0) countdown--; // 若 countdown = 0 並且狀態機在等待切換,也可強制進入下階段 }
switch (state) { case 0: if (subState != 0) { allOff(); digitalWrite(TG1, HIGH); digitalWrite(TR2, HIGH); digitalWrite(TG3, HIGH); digitalWrite(TR4, HIGH); countdown = greenTime; previousMillis = currentMillis; subState = 0; } if (currentMillis - previousMillis >= greenTime * 1000UL) { digitalWrite(TG1, LOW); digitalWrite(TY1, HIGH); digitalWrite(TG3, LOW); digitalWrite(TY3, HIGH); countdown = yellowTime; previousMillis = currentMillis; state = 1; subState = -1; } break; case 1: if (subState != 1) { // 進入黃燈階段時不需要做額外動作 subState = 1; } if (currentMillis - previousMillis >= yellowTime * 1000UL) { digitalWrite(TY1, LOW); digitalWrite(TR2, LOW); digitalWrite(TR1, HIGH); digitalWrite(TG2, HIGH); digitalWrite(TY3, LOW); digitalWrite(TR4, LOW); digitalWrite(TR3, HIGH); digitalWrite(TG4, HIGH); countdown = greenTime; previousMillis = currentMillis; state = 2; subState = -1; } break; case 2: if (subState != 2) { // 進入方向2綠燈時 subState = 2; } if (currentMillis - previousMillis >= greenTime * 1000UL) { digitalWrite(TG2, LOW); digitalWrite(TY2, HIGH); digitalWrite(TG4, LOW); digitalWrite(TY4, HIGH); countdown = yellowTime; previousMillis = currentMillis; state = 3; subState = -1; } break; case 3: if (subState != 3) { subState = 3; } if (currentMillis - previousMillis >= yellowTime * 1000UL) { digitalWrite(TY2, LOW); digitalWrite(TR1, LOW); digitalWrite(TY4, LOW); digitalWrite(TR3, LOW); state = 0; subState = -1; } break; }}
// ---- 模式 2:閃爍警示 ----void blinkMode() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= 500) { previousMillis = currentMillis; blinkState = !blinkState; digitalWrite(TY1, blinkState); digitalWrite(TY2, blinkState); digitalWrite(TY3, blinkState); digitalWrite(TY4, blinkState); } // 顯示 “00” countdown = 0;}
// ---- 模式 3:手動放行 ----void manualMode() { allOff(); digitalWrite(TR1, HIGH); digitalWrite(TG2, HIGH); digitalWrite(TR3, HIGH); digitalWrite(TG4, HIGH); countdown = 0;}
// ---- 全部熄滅(不亮任何燈) ----void allOff() { digitalWrite(TG1, LOW); digitalWrite(TY1, LOW); digitalWrite(TR1, LOW); digitalWrite(TG2, LOW); digitalWrite(TY2, LOW); digitalWrite(TR2, LOW); digitalWrite(TG3, LOW); digitalWrite(TY3, LOW); digitalWrite(TR3, LOW); digitalWrite(TG4, LOW); digitalWrite(TY4, LOW); digitalWrite(TR4, LOW);}