2017年12月4日 星期一

按鈕及彈跳

作者:王一哲
日期:2016/9/7



所需元件



  1. 可插入麵包板的按鈕1個
  2. 220 Ω 電阻1個
  3. 10 kΩ 電阻1個
  4. 3 mm 或 5 mm LED 1個
  5. 麵包板1塊
  6. Arduino Uno 開發板1塊
  7. 麵包板連接線數條

使用外接電阻



將 5 V 接到按鈕,按鈕的另一端串聯1個 10 kΩ 電阻再接地,這樣的接法被稱為下拉電阻(pull-down resistor)。若測量電阻及按鈕間的電壓,當按鈕未按下時電路未接通,測量到低電位;當按鈕按下時測量到高電位。當按鈕被按下時,點亮LED;反之則熄滅。
線路圖

實際的裝置照片


程式碼如下:
/* 實驗5-1:用按鈕控制LED                              *
 *          使用外接10kOhm電阻                         *
 * 參考資料:葉難(2014)。Arduino輕鬆入門              *
 * 日期:Sep. 17, 2016                                 *
 * 作者:王一哲                                        */
#define LEDPIN 7     //定義LED接腳
#define BUTTONPIN 4  //定義按鈕接腳
#define BAUDRATE 9600
int state;

void setup() {
    pinMode(LEDPIN, OUTPUT); //定義LED接腳為輸出
    pinMode(BUTTONPIN, INPUT); //定義按鈕接腳為輸入
    Serial.begin(BAUDRATE);
}

void loop() {
    state = digitalRead(BUTTONPIN); //讀取按鈕狀態,按下為HIGH
    if (state == HIGH) {               //若按下按鈕則點亮LED
        digitalWrite(LEDPIN, HIGH);
    } else {                        //若沒有按下按鈕則熄滅LED
        digitalWrite(LEDPIN, LOW);
    }
}

在這個程式當中我們第一次用到 if ,用法如下
if (條件) {
    條件為true時執行的程式碼......
} else {
    條件為false時執行的程式碼......
}

其中 else 的部分可以不設定。若有更多的條件時可以寫成
if (條件1) {
    條件1為true時執行的程式碼......
} else if (條件2) {
    條件1為false且條件2為true且時執行的程式碼......
} else {
    條件1、2皆為為false時執行的程式碼......
}


使用開發板內建的上拉電阻



開發板內建有上拉電阻(pull-up resistor),若使用內建的電阻可以使接線方式更加簡單,接法如下。
線路圖

實際的裝置照片


若測量電阻及按鈕間的電壓,當按鈕未按下時電路未接通,測量到高電位;當按鈕按下時測量到低電位。當按鈕被按下時,點亮LED;反之則熄滅。程式碼如下:
/* 實驗5-2:用按鈕控制LED                             *
 *          使用開發板內建的上拉電阻                  *
 * 參考資料:葉難(2014)。Arduino輕鬆入門              *
 * 日期:Sep. 17, 2016                                 *
 * 作者:王一哲                                        */
#define LEDPIN 7     //定義LED接腳
#define BUTTONPIN 4  //定義按鈕接腳
#define BAUDRATE 9600
int state;

void setup() {
    pinMode(LEDPIN, OUTPUT); //定義LED接腳為輸出
    pinMode(BUTTONPIN, INPUT_PULLUP); //定義按鈕接腳為輸入並啟用開發板內建的上拉電阻
    Serial.begin(BAUDRATE);
}

void loop() {
    state = digitalRead(BUTTONPIN); //讀取按鈕狀態,按下為LOW
    if (state == LOW) {               //若按下按鈕則點亮LED
        digitalWrite(LEDPIN, HIGH);
    } else {                        //若沒有按下按鈕則熄滅LED
        digitalWrite(LEDPIN, LOW);
    }
}


按鈕的彈跳問題(方法1:時間延遲)



若連續且快速地按按鈕,有時候LED不會在按下時被點亮,這是因為按鈕被按下到彈回來的過程中程式已經讀取了接腳的狀態很多次。為了解決這個問題,我們可以先讀取一次接腳的狀態並儲存起來,隔一小段時間後再測量一次,如果兩次的狀態相同,代表按鈕真的被按下。程式碼如下:
/* 實驗5-3:去除開關彈跳                               *
 *          使用延遲時間(輪詢式)                     *
 *          持續按住會亮暗交替                         *
 * 參考資料:葉難(2014)。Arduino輕鬆入門               *
 * 日期:Sep. 17, 2016                                  *
 * 作者:王一哲                                        */
#define LEDPIN 7      //定義LED接腳
#define BUTTONPIN 4   //定義按鈕接腳
#define DELAYTIME 200 //定義延遲時間
#define BUADRATE 9600 //定義鮑率
boolean ledState;
unsigned long timePrevious, timeCurrent;

void setup() {
    Serial.begin(BUADRATE);
    pinMode(LEDPIN, OUTPUT); //定義LED接腳為輸出
    pinMode(BUTTONPIN, INPUT_PULLUP); //定義按鈕接腳為輸入並啟用開發板內建的上拉電阻
    ledState = LOW;
    digitalWrite(LEDPIN, ledState);
}

void updateLed() { 
    timeCurrent = millis();
    if ((timeCurrent - timePrevious) > DELAYTIME) {
        timePrevious = timeCurrent;
        ledState = !ledState;
        digitalWrite(LEDPIN, ledState);
//        Serial.println(ledState);
    }
}

void loop() {
    boolean buttonState = digitalRead(BUTTONPIN); //讀取按鈕狀態,按下為LOW
    if (buttonState == LOW) {                     //若按下按鈕則更新LED狀態
        updateLed();
//        Serial.println("Change");
    }
}


在這個程式當中 void updateLed() 是自己設定的函式,開頭的 void 代表函式沒有回傳值。我們會將程式當中會一直使用到的部分寫成一個函式,在程式裡其它的部分不斷地呼叫它,這樣可以減少程式碼的長度。

問題:若一直按著按鈕會發生什麼事?
請先努力地嘗試寫出解決方案,至少堅持10分鐘以後再看,而且這不是唯一的寫法,其它寫法也能做到同樣的效果。
/* 實驗5-5:去除開關彈跳                             *
 *          使用延遲時間(輪詢式)                   *
 *          每按一下亮、暗變換一次                   *
 * 參考資料:葉難(2014)。Arduino輕鬆入門             *
 * 日期:Sep. 17, 2016                                *
 * 作者:王一哲                                       */
#define LEDPIN 7      //定義LED接腳
#define BUTTONPIN 4   //定義按鈕接腳
#define DELAYTIME 50  //定義延遲時間
#define BAUDRATE 9600 //定義鮑率
boolean ledState;
unsigned long timePrevious, timeCurrent;

void setup() {
    Serial.begin(BAUDRATE);
    pinMode(LEDPIN, OUTPUT); //定義LED接腳為輸出
    pinMode(BUTTONPIN, INPUT_PULLUP); //定義按鈕接腳為輸入並啟用開發板內建的上拉電阻
    ledState = LOW;
    digitalWrite(LEDPIN, ledState);
}

void updateLed() { 
    timeCurrent = millis();
    if ((timeCurrent - timePrevious) > DELAYTIME) {
        timePrevious = timeCurrent;
        ledState = !ledState;
        digitalWrite(LEDPIN, ledState);
    }
}

void loop() {
    boolean buttonPrevious, buttonCurrent;
    buttonPrevious = digitalRead(BUTTONPIN);       //讀取按鈕狀態,按下為LOW
    delay(DELAYTIME);
    buttonCurrent = digitalRead(BUTTONPIN);
    if (buttonCurrent == LOW && buttonCurrent != buttonPrevious) {        //若按下按鈕而且不是持續按著則更新LED狀態
        updateLed();
        Serial.println("Change");
    }
}


按鈕的彈跳問題(方法2:Bounce2.h)



已經有熱心人士將去除彈跳的函式庫寫好了,名為 Bounce2.h ,我們可以從Arduino官網上找到( http://playground.arduino.cc/Code/Bounce ),下載的連結為 https://github.com/thomasfredericks/Bounce-Arduino-Wiring/archive/master.zip,將下載的檔案解壓縮後得到的資料夾複製到Arduino開發程式的資料夾內即可。可能的路徑為
  1. Windows環境 C:\\Program Files (x86)\\Arduino\\libraries\\
  2. Linux環境 home//Arduino/libraries 或 home//sketchbook/libraries
接下來在程式碼的開頭只要加上 #include 即可引用。程式碼如下:
/* 實驗5-4:去除開關彈跳                             *
 *          使用Bounce2.h                             *
 *          每按一下亮、暗變換一次                   *
 * 函式庫:http://playground.arduino.cc/Code/Bounce   *
 * 參考資料:葉難(2014)。Arduino輕鬆入門             *
 * 日期:Sep. 17, 2016                                *
 * 作者:王一哲                                       */
#include <Bounce2.h>
#define LEDPIN 7      //定義LED接腳
#define BUTTONPIN 4   //定義按鈕接腳
#define DELAYTIME 50 //定義延遲時間
#define BAUDRATE 9600 //定義鮑率

Bounce bouncer = Bounce(BUTTONPIN, DELAYTIME);
boolean ledState;

void setup() {
    Serial.begin(BAUDRATE);
    pinMode(LEDPIN, OUTPUT); //定義LED接腳為輸出
    pinMode(BUTTONPIN, INPUT_PULLUP); //定義按鈕接腳為輸入並啟用開發板內建的上拉電阻
    ledState = LOW;
    digitalWrite(LEDPIN, ledState);
}

void loop() {
    if (bouncer.update() == true && bouncer.read() == LOW) {
        ledState = !ledState;
        digitalWrite(LEDPIN, ledState);
        Serial.println(ledState);
    }
}


其中
Bounce bouncer = Bounce(buttonPin, delayTime);
先定義了一個名為 bouncer 的物件,需要指定讀取的接腳及延遲時間。在 void loop() 當中
if (bouncer.update() == true && bouncer.read() == LOW){
    ledState = !ledState;
    digitalWrite(ledPin, ledState);
    Serial.println(ledState);
}
bouncer.update() 會讀取接腳的狀態且判斷狀態是否有變化,因此這段程式碼的功能是當狀態發生變化且為低電位時改變 LED 的狀態。


HackMD 版本連結:https://hackmd.io/@yizhewang/S1pGNzKEN

沒有留言:

張貼留言