前一天我們發現只要按住按鈕,燈號就會持續切換,問題在於程式只知道按鈕目前被按下,還分不出是「剛按下」還是「持續按住」,今天要讓程式記住按鈕前一次的狀態,只在按下的瞬間才切換。
在 ledState 下方新增 lastBtn,初始值設為 HIGH。
bool ledState = false;
bool lastBtn = HIGH;
btn 保存最近這一次迴圈讀到的狀態,lastBtn 則保存上一次迴圈的狀態,接著程式同時比較兩者後,便能知道按鈕是維持放開、維持按住、剛被按下,或剛被放開,透過判斷歷史狀態,程式便能辨認狀態變化,這種做法常用於處理按鍵、開關及其他數位輸入事件。
按鈕放開時是 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 才會再切換一次。
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()。後面會處理實體按鈕常見的機械接點彈跳問題,讓每一次按下的結果更加穩定。
下一篇|實體按鈕常見的彈跳問題