Arduino:有紅燈倒數計時的紅綠燈控制電路

 




功能


一開始縱向的紅色 LED 亮,伴隨著從 120 秒開始的倒數計時 ( 倒數秒數為 3 位數時不顯示,倒數至 2 位數即 99 秒時才開始顯示 ),且橫向的綠色 LED 同時跟著亮。當縱向的紅色 LED 倒數至 3 秒時,橫向的綠色 LED 熄滅,且橫向的黃色 LED 亮;當縱向的紅色 LED 倒數至 0 秒時 ( 不顯示 ),縱向的紅色 LED 熄滅,轉而縱向的綠色 LED 亮,且橫向的紅色 LED 亮,伴隨著從 120 秒開始的倒數計時( 一樣倒數秒數為 3 位數時不顯示,倒數至 2 位數即 99 秒時才開始顯示 )。當橫向的紅色 LED 倒數至 3 秒時,縱向的綠色 LED 熄滅,縱向的的黃色 LED 亮;當橫向的紅色 LED 倒數至 0 秒時 ( 不顯示 ),橫向的紅色 LED 熄滅,縱向的綠色 LED 亮,如此周而復始。



電路圖


有紅燈倒數計時的紅綠燈控制電路圖
圖一  有紅燈倒數計時的紅綠燈控制電路圖



程式碼


 C/C++
bool RedOnLongitudinal =  true;     /* 初始狀態為縱向紅燈亮 */
const int scanPin[] = {5, 6, 7, 8};     /* 七段顯示器掃描訊號的輸出接腳為第 5、6、7、8 腳 */
const int dataPin = 10, latchPin = 11, clockPin = 12;  /* 串列資料輸出接腳為第 10 腳;栓鎖訊號輸
                                                                                       出接腳為第 11 腳;移位脈波輸出接腳為第
                                                                                       12 腳 */

/* 數字 0 至 9 的共陽極七段顯示碼  */
const int sevenSegCode[ ] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90};

int dataOut[4] = {0, 0, 0, 0};  /* 縱向兩位數以及橫向兩位數的輸出輸出緩衝區,由左至右順序為
                                                縱向的個位 (dataOut[0])、十位 (dataOut[1]) 、與橫向的個位
                                                (dataOut[2])、十位 (dataOut[3]) */

unsigned long previousTime;  /* 暫存系統時間的變數 */
unsigned long nowTime;         /* 暫存系統時間的變數 */
int second = 0;                        /* 紀錄秒數的變數 */

int GreenLight_1 = 2;              /*  輸出控制縱向綠色 LED 亮滅的輸出接腳為第 2 腳 */
int YellowLight_1 = 3;              /*  輸出控制縱向黃色 LED 亮滅的輸出接腳為第 3 腳 */
int RedLight_1 = 4;                  /*  輸出控制縱向紅色 LED 亮滅的輸出接腳為第 4 腳 */

int GreenLight_2 = A0;            /*  輸出控制橫向綠色 LED 亮滅的輸出接腳為第 A0 腳 */
int YellowLight_2 = A1;            /*  輸出控制橫向黃色 LED 亮滅的輸出接腳為第 A1 腳 */
int RedLight_2 = A2;                /*  輸出控制橫向紅色 LED 亮滅的輸出接腳為第 A2 腳 */

int RedLightLastingSecond = 120;         /*  紅色 LED 亮的持續秒數 */
int GreenLightLastingSecond = 117;      /*  綠色 LED 亮的持續秒數 */
int YellowLightLastingSecond = 3;         /*  黃色 LED 亮的持續秒數 */
                                                               /* 〔注意〕:GreenLightLastingSecond +
                                                                      YellowLightLastingSecond  必須等於
                                                                      RedLightLastingSecond */

void display();                            /* 宣告自定顯示函數 */
void calCountDigit(int count);    /* 宣告找出計數值的十位數與個位數值 */
int scanDigitNum;                      /* 實際需要掃描的位數,可為 1 或 2 */
int countTenthsDigit;                  /* 暫存找出的計數值的十位數值 */
int remainder;                            /* 暫存在找出計數值的十位數與個位數值過程產生的餘數 */
int countUnitsDigit;                    /* 暫存找出的計數值的個位數值 */
int redLEDRemainTime;            /* 紅色 LED 亮的剩餘秒數 */

void setup() {
     /* 規劃輸出控制縱向與橫向紅黃綠 LED 的訊號接腳為輸出 */ 
  pinMode(GreenLight_1, OUTPUT);
  pinMode(YellowLight_1, OUTPUT);
  pinMode(RedLight_1, OUTPUT);
  pinMode(GreenLight_2, OUTPUT);
  pinMode(YellowLight_2, OUTPUT);
  pinMode(RedLight_2, OUTPUT);
  
  for (int i = 0; i < 4; i++){
    pinMode(scanPin[i], OUTPUT);    /* 規劃第 5、6、7、8 的掃瞄接腳為輸出 */
    digitalWrite(scanPin[i], 1);             /* 初始狀態:第 5、6、7、8 腳停止掃描,故分別送出邏輯 1     }                                                            訊號*/

  pinMode(dataPin, OUTPUT);           /* 規劃第 10 腳為輸出 */
  pinMode(clockPin, OUTPUT);          /* 規劃第 12 腳為輸出 */
  pinMode(latchPin, OUTPUT);           /* 規劃第 11 腳為輸出 */

  digitalWrite(RedLight_1, HIGH);      /* 初始狀態縱向紅色 LED 亮 */
  digitalWrite(YellowLight_1, LOW);   /* 初始狀態縱向黃色 LED 熄 */
  digitalWrite(GreenLight_1, LOW);    /* 初始狀態縱向綠色 LED 熄 */

  digitalWrite(RedLight_2, LOW);      /* 初始狀態橫向紅色 LED 熄 */
  digitalWrite(YellowLight_2, LOW);   /* 初始狀態橫向黃色 LED 熄 */
  digitalWrite(GreenLight_2, HIGH);   /* 初始狀態橫向綠色 LED 亮 */
  previousTime = millis();                    /* 取得系統從開始至今歷經的時間 (單位毫秒) */ 
  redLEDRemainTime = RedLightLastingSecond-second; /* 找出紅色 LED 亮的剩餘時間 */
}

void loop() {
  nowTime = millis();                              /* 取得系統從開始至今歷經的時間 (單位毫秒) */ 
  display();                                             /* 呼叫顯示函式 */
  if ((nowTime - previousTime) >= 1000 ){  /* 如果已過 1 秒的時間 */
    previousTime = nowTime;
    second++;                                                  /* 歷經的秒數遞加一 */
    redLEDRemainTime = RedLightLastingSecond-second; /* 找出紅色 LED 亮的剩餘秒數 */
     
    if (second == GreenLightLastingSecond ){     /* 若一次週期中綠燈持續亮的時間到 */
      if (RedOnLongitudinal){                                 /* 若輪到縱向紅色 LED 亮 */
        digitalWrite(YellowLight_2, HIGH);              /* 令橫向黃色 LED 亮 */
        digitalWrite(GreenLight_2, LOW);               /* 令橫向綠色 LED 熄 */
      }
      else{                                                             /* 若輪到橫向紅色 LED 亮 */
        digitalWrite(YellowLight_1, HIGH);            /* 令縱向黃色 LED 亮 */
        digitalWrite(GreenLight_1,LOW);              /* 令縱向綠色 LED 熄*/ 
      }
    }
    else if (second == (GreenLightLastingSecond + YellowLightLastingSecond)){ /* 一次週期中綠燈 +
                                                                                                                           黃燈持續亮的時間到 */
      if (RedOnLongitudinal){
        digitalWrite(YellowLight_2, LOW);    /* 令橫向黃色 LED 熄 */
        digitalWrite(RedLight_2, HIGH);       /* 令橫向紅色 LED 亮 */
        digitalWrite(GreenLight_1, HIGH);   /* 令縱向綠色 LED 亮 */
        digitalWrite(RedLight_1, LOW);       /* 令縱向紅色 LED 熄 */
      }
      else{
        digitalWrite(YellowLight_1, LOW);   /* 令橫向黃色 LED 熄 */
        digitalWrite(RedLight_1, HIGH);       /* 令橫向紅色 LED 亮 */
        digitalWrite(GreenLight_2, HIGH);   /* 令縱向綠色 LED 亮 */
        digitalWrite(RedLight_2, LOW);       /* 令縱向紅色 LED 熄 */
      }
      second = 0;                                            /* 秒數歸 0,新的週期開始 */
      redLEDRemainTime = RedLightLastingSecond-second;
      RedOnLongitudinal = !RedOnLongitudinal;   /* 切換縱橫向紅燈亮 */
    }
  }
}

void display(){
  if(redLEDRemainTime >=100)                /* 若紅燈亮的剩餘秒數大於等於 100 */
    return;                                                   /*  返回 (不顯示) */
  calCountDigit(redLEDRemainTime);      /*  找出紅燈亮的剩餘秒數的十位數與個位數 */

  for(int x = 0; x < 4; x++) 
    digitalWrite(scanPin[x], 1);                  /* 清除掃描信號 */ 

  if (RedOnLongitudinal) {                             /* 縱向紅燈亮 */
    for (int i = 0; i < scanDigitNum ; i++){        /* scanDigitNum 為實際掃描位數 */ 
      digitalWrite(latchPin, 0);                          /* 栓鎖信號解除 */ 
      /* 序列傳輸縱向紅燈倒數秒數的第 i 位數數字的七段顯示碼 */   
      shiftOut(dataPin, clockPin, MSBFIRST, sevenSegCode[dataOut[i]]); 
      digitalWrite(latchPin, 1);
      digitalWrite(scanPin[i], 0);                  /* 送出掃描信號給縱向紅燈倒數秒數的第 i 位數 */ 
      delay(2);                                               /* 延遲 2 毫秒 */ 
      digitalWrite(scanPin[i], 1);                   /* 清除縱向紅燈倒數秒數的第 i 位數的掃描信號 */ 
    }
  }
  else{                                                              /* 橫向紅燈亮 */
    for (int i = 0; i < scanDigitNum ; i++){      /* scanDigitNum 為實際掃描位數 */ 
      digitalWrite(latchPin, 0);                           /* 栓鎖信號解除 */ 
      /* 序列傳輸橫向紅燈倒數秒數的第 i 位數數字的七段顯示碼 */   
      shiftOut(dataPin, clockPin, MSBFIRST, sevenSegCode[dataOut[i+2]]); 
      digitalWrite(latchPin, 1);                          /* 送出栓鎖信號 */ 
      digitalWrite(scanPin[i+2], 0);                  /* 送出掃描信號給橫向紅燈倒數秒數的第 i 位數 */ 
      delay(2);                                                   /* 延遲 2 毫秒 */ 
      digitalWrite(scanPin[i+2], 1);                  /* 清除橫向紅燈倒數秒數的第 i 位數的掃描信號 */ 
    }    
  }         
}

void calCountDigit(int count){  
    countTenthsDigit = count/10;              /* 找出計數值的十位數值存入變數 countTenthsDigit */
    remainder =  count%10;                       /* 找出計數值的個位數值存入變數 remainder */
    countUnitsDigit = remainder;              /* 計數值的個位數值暫存於變數 countUnitsDigit */
    if (RedOnLongitudinal) {
      dataOut[1] = countTenthsDigit;      /* 將縱向紅燈倒數秒數的十位數值存入輸出顯示器緩衝區 */
      dataOut[0] = countUnitsDigit;       /* 將縱向紅燈倒數秒數的個位數值存入輸出顯示器緩衝區 */
    }
    else{
      dataOut[3] = countTenthsDigit;      /* 將橫向紅燈倒數秒數的十位數值存入輸出顯示器緩衝區 */
      dataOut[2] = countUnitsDigit;        /* 將縱向紅燈倒數秒數的個位數值存入輸出顯示器緩衝區 */
    }
    if (countTenthsDigit != 0)
        scanDigitNum = 2;         /* 如果秒數值的十位數值不為 0,則掃描位數為 2 個位數 */
    else
        scanDigitNum = 1;         /* 否則掃描位數為 1 個位數 */
}




留言

這個網誌中的熱門文章

三段式電子開關電路

陣列 (C++)

首數、尾數與位數

分壓偏壓 BJT 放大電路的直流分析及其近似解的條件

MOSFET 共汲極放大電路 (源極隨耦器) 小訊號分析

為什麼理想的 OPA 電壓放大器有虛短路與虛斷路現象

具有倒數計時自動回復功能的行人穿越道號誌控制電路