4位元七段顯示器應用—由監看視窗自行輸入時間數值之倒數計時器

參考程式碼

//---------------------------------------------------------------------------
const int pb = 34;                   // 按鈕開關接腳
bool lastButtonState = HIGH;         // 按鈕開關前一次的狀態
unsigned long lastDebounceTime = 0;  // 按鈕開關前一次狀態改變時的millis()時間
int debounceDelay = 150;             // 防彈跳預設的延遲時間
unsigned long buttonDownTime = 0;    // 按鈕開關被按下的時間(長按時使用)
unsigned long longPressTime = 1500;  // 長按時間(長按時使用)
//---------------------------------------------------------------------------
bool startCount = 0;     // (0:停止,1:開始)
int count = 9999;        // 計數器,初值為9999
//---------------------------------------------------------------------------
unsigned long previousMillis = 0;    // 前一次的millis()時間
const long interval = 100;           // 預設計時的時間
//---------------------------------------------------------------------------
int seg[8] = {15, 16, 17, 18, 19, 21, 22, 23};
// 對應的接腳 { a,  b,  c,  d,  e,  f,  g, dp}
int data[]= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x27,0x7F,0x67,0x00};
// 顯示的數字 { 0  ,  1 ,  2 ,  3 ,  4 ,  5 ,  6 ,  7 ,  8 ,  9 ,全滅}
int com[4] = {25, 26, 27, 14};
// 對應的接腳 {com1,com2,com3,com4}
int digit[4] = {   8  ,   4  ,   2  ,   1  };
// 位元掃瞄     {"1000","0100","0010","0001"};
int num[4] = {0, 0, 0, 0};
// 各位元數值 {千,百,十,個}
//---------------------------------------------------------------------------
int input;        // 串列埠輸入的數值
//---------------------------------------------------------------------------

void setup() {
  //--------------------------------------------------
  Serial.begin(9600);     // 啟用串列埠監看視窗
  Serial.println("Enter a number(0~9999):");  //輸入提示
  //--------------------------------------------------
  pinMode(pb, INPUT);     // 初始化按鈕開關
  //--------------------------------------------------
  // 初始化七段顯示器a~g、dp接腳
  for(int i=0; i<=7; i++)
  {
    pinMode(seg[i], OUTPUT);
    digitalWrite(seg[i],0);   // 全滅
  }
  //--------------------------------------------------
  // 初始化七段顯示器com1~com4接腳
  for(int i=0; i<=3; i++)
  {
    pinMode(com[i], OUTPUT);
    digitalWrite(com[i],0);   // 全滅    
  }
  //--------------------------------------------------
}

void loop() {
  // ---------------------------------------------------------------------------------------------------------------
  if (Serial.available()) //若串列埠有輸入資料,則執行下列程式區塊
  {
    input = readNumber();
    if(input >=0 && input <=9999)
    {
      count = input;
    }
    else
    {
      Serial.println("Error, wrong number.");
      Serial.println("Enter a number(0~9999):");  //輸入提示
    }
  }

  
  // ---------------------------------------------------------------------------------------------------------------
  unsigned long currentTime = millis();  // 當前的millis()時間
  bool buttonState = digitalRead(pb);    // 當前的按鈕開關狀態:未按下(HIGH),按下(LOW)

  // 若按鈕開關狀態發生變化,且超過防彈跳預設的延遲時間
  if ((buttonState != lastButtonState) && (currentTime - lastDebounceTime) > debounceDelay)
  {
    // --------------------------------------------------------------------------------------
    if (lastButtonState == HIGH && buttonState == LOW)        // 若按鈕從未按下(HIGH)到按下(LOW)
    {
      // --------------------------------------------------
      // 按鈕按下時,想做的事
      // --------------------------------------------------
      buttonDownTime = currentTime; //記錄按鈕按下的時間
      // --------------------------------------------------

    }
    else if (lastButtonState == LOW && buttonState == HIGH)   // 若按鈕從按下(LOW)到未按下(HIGH)
    {      
      // --------------------------------------------------
      // 按鈕放開時,想做的事
      // --------------------------------------------------
      startCount = !startCount;           // 取反相
      if(startCount==0)
      {
        Serial.println("Stop.");          // 印出停止狀態  
      }
      else
      {
        Serial.println("Start count..."); // 印出開始計數狀態  
      }
      // --------------------------------------------------
      if((currentTime - buttonDownTime) >= longPressTime)
      {
        Serial.println("Long press detected, count reset.");
        count=9999;             // 重置計數器
        startCount=0;           // 停止計數
        Serial.print("Button pressed count: ");   // 印出計數器內容
        Serial.println(count);                    // 印出計數器內容
      }        
      // --------------------------------------------------            
    }
    // --------------------------------------------------------------------------------------
    lastButtonState = buttonState;    // 更新按鈕狀態
    lastDebounceTime = currentTime;   // 更新前一次狀態改變時的millis()時間
  }
  // ---------------------------------------------------------------------------------------------------------------


  // ----------------------------------------------------------------------
  // 每經過一個interval的時間,要做的事
  // ----------------------------------------------------------------------
  unsigned long currentMillis = millis();          // 當前的millis()時間

  if (currentMillis - previousMillis >= interval)  // 若達到預設計時的時間
  {
    //---------------------------------------------------------------------
    // 若設定為開始計數,則執行下列程式
    //---------------------------------------------------------------------
    if(startCount == 1)
    {
      count = count - 1;      // 計數器減1
      Serial.println(count);      
    }
    //---------------------------------------------------------------------

    previousMillis = currentMillis;  // 更新前一次的millis()時間
  }
  // ----------------------------------------------------------------------


  //-------------------------------------
  // 顯示數字
  //-------------------------------------
  num[0] = count / 1000 % 10;   // 千位
  num[1] = count / 100 % 10;    // 百位
  num[2] = count / 10 % 10;     // 十位
  num[3] = count % 10;          // 個位
  showNum();
  //-------------------------------------

}

void showdata(int x)
{
  for(int i=0; i<7; i++)
  digitalWrite(seg[i],bitRead(data[x],i));
}

void scan(int x){
  for(int i=0; i<4; i++)    
  digitalWrite(com[i],bitRead(digit[x],3-i));
}

void showNum()
{
  for(int i=0; i<4; i++)
  {
    showdata(num[i]); 
    scan(i);
    delay(1);  
  }
}

// 讀取一個整數
int readNumber()
{
  while (!Serial.available());  // 等待串列埠輸入
  String input = Serial.readStringUntil('\n');  //讀取一行的輸入資料,直到換行符號為止(按下enter鍵)
  input.trim();                 //去除輸入字串前後的空白字元
  Serial.println(input);        //印出輸入字串
  return input.toInt();         //轉換為整數
}

作業練習:

  1. 開機後,4位元七段顯示器顯示欲倒數計時的初始時間(例:1200代表12:00,時間12:00→11:59→11:58→……→11:01→11:00→10:59→……→00:00)。
  2. 每按一下按鈕開關,可開始或停止倒數計數(每秒減1,直到0000停止)。
  3. 長按按鈕開關,計數器重置為初始時間。
  4. 任何過程中,可以透過監看視窗改變倒數計時的數值。(輸入範圍0000~5959)
  5. 七段顯示器的狀態必須同步顯示在監看視窗上。
  6. 請完成實體接線。

重點提示:

  1. 不可直接對 4 位數字做 count–,否則會變成 12:00 → 11:99 → 11:98… 造成顯示錯誤。
  2. 正確觀念:輸入格式 (MMSS)內部格式(秒)輸出格式 (MMSS)
  3. 輸入數值範圍在 0~5959 之間,兩位為分、兩位為秒,分數、秒數必須小於 60。
  4. 將輸入值轉換為「總秒數」進行倒數計時處理。
  5. 顯示時,再將「總秒數」轉換為「 MM:SS 」輸出到七段顯示器。
[輸入數值 1200] (代表 12分00秒)
      ↓
(拆解與驗證) MM=12, SS=00
      ↓
(轉為總秒數) 12*60 + 0 = [720 秒]  <--- 計時器的核心
      ↓
(每一秒鐘)   720 - 1 = 719, 718...
      ↓
(顯示運算)   719 秒 是幾分幾秒?
             719 / 60 = 11 (分)
             719 % 60 = 59 (秒)
      ↓
[組合成顯示] 11*100 + 59 = [1159]
      ↓
[七段顯示器] 11:59