iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
自我挑戰組

一塊板子,兩個腦袋:Arduino UNO Q 30 天實戰系列 第 7

Day 7|按一下再切換,別讓燈一直跳

  • 分享至 

  • xImage
  •  

前一天我們發現只要按住按鈕,燈號就會持續切換,問題在於程式只知道按鈕目前被按下,還分不出是「剛按下」還是「持續按住」,今天要讓程式記住按鈕前一次的狀態,只在按下的瞬間才切換。

記住前一次按鈕狀態

ledState 下方新增 lastBtn,初始值設為 HIGH。

bool ledState = false;
bool lastBtn = HIGH;

btn 保存最近這一次迴圈讀到的狀態,lastBtn 則保存上一次迴圈的狀態,接著程式同時比較兩者後,便能知道按鈕是維持放開、維持按住、剛被按下,或剛被放開,透過判斷歷史狀態,程式便能辨認狀態變化,這種做法常用於處理按鍵、開關及其他數位輸入事件。

判斷由 HIGH 變成 LOW

按鈕放開時是 HIGH,按下時是 LOW,因此「剛被按下」代表前一次為 HIGH,目前變成 LOW,程式使用 && 要求兩個條件同時成立。

if (lastBtn == HIGH && btn == LOW) {
  toggle_led();
}

第一次按下時,lastBtn 仍保存放開時的 HIGH,而 btn 已讀到 LOW,條件成立並切換 LED,這種由 HIGH 轉為 LOW 的變化稱為下降緣,而當按鈕繼續被按住時,兩個值都會變成 LOW,條件不再成立,所以不會重複呼叫 toggle_led()

兩次讀值共有四種組合,HIGH 接 HIGH 代表持續放開,LOW 接 LOW 代表持續按住,LOW 轉 HIGH 代表放開,只有 HIGH 轉 LOW 代表新的按下動作,因此條件只選擇最後一種組合,其他狀態都不改變 LED。

更新下一次比較基準

完成判斷後,要在 loop() 結尾執行 lastBtn = btn;,將目前讀值保存成下一次迴圈的前次狀態。

lastBtn = btn;

我們不妨可以把每次 loop() 視為一張按鈕快照,程式先拿新快照和上一張比較,確認是否出現按下動作,再把新快照保存起來,供下一次迴圈使用,這個順序讓判斷、執行與記錄各自有清楚位置。

按鈕放開後,btn 會回到 HIGH,迴圈末端也會把 lastBtn 更新為 HIGH,下一次按下時,程式再次取得 HIGH 到 LOW 的變化,LED 才會再切換一次。

完整 MCU 程式

const int LED_PIN = 2;
const int BTN_PIN = 9;

bool ledState = false;
bool lastBtn = HIGH;

void toggle_led() {
  ledState = !ledState;
  digitalWrite(LED_PIN, ledState ? HIGH : LOW);
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(BTN_PIN, INPUT_PULLUP);
  digitalWrite(LED_PIN, LOW);
}

void loop() {
  bool btn = digitalRead(BTN_PIN);

  if (lastBtn == HIGH && btn == LOW) {
    toggle_led();
  }

  lastBtn = btn;
}

執行與驗證

按下 App Lab 的 Run 後,快速按下並持續按住 D9,LED 應只在按下瞬間切換一次,維持按住不會繼續變化,放開按鈕也不會切換,再按一次才會產生下一次動作,可連續測試五次,每次完整執行按下與放開,LED 應依序在亮與滅之間切換,測試時也要刻意長按兩秒,確認持續 LOW 不會再次觸發,若偶爾一次按下仍出現兩次變化,原因可能是實體接點在短時間內產生多次 HIGH 與 LOW,下一篇會加入去彈跳處理。

本篇完成內容

  • 新增 lastBtn 保存前一次按鈕狀態。
  • 使用 && 判斷 HIGH 到 LOW 的下降緣。
  • 只在按下瞬間呼叫 toggle_led()
  • 按住按鈕時不再連續切換 LED。

後面會處理實體按鈕常見的機械接點彈跳問題,讓每一次按下的結果更加穩定。

下一篇|實體按鈕常見的彈跳問題


上一篇
Day 6|為 Edge Box 加上實體按鈕
下一篇
Day 8|處理實體按鈕的彈跳問題
系列文
一塊板子,兩個腦袋:Arduino UNO Q 30 天實戰11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言