# 以Interrupt的方式實作按鈕
### interrupt方式實作按鈕讀取
由於在實作最後作品的過程,需要一個按鈕控制模式且以不同顏色的燈顯示狀態,但arduino程式隨時處於一個無限迴圈的工作模式接收外部訊息然後做出對應動作,大部分時候都處於忙碌狀態,導致即使使用者按下按鈕,程式也不一定會馬上去讀取,等開始讀取時使用者已經釋放按鈕了,因此讀到的結果是開路狀態(未按押)
因此最好的解決方法不是一直去讀取按鈕,而是按鈕被按押電路從開路變成閉合的當下,由系統中斷的方式先通知程式記錄起來,等到需要按鈕狀態時再去讀取並得知剛才這段時間按鈕是否有按下過。
中斷模式必須先設定才會生效,且在Leonardo只能使用0,1,2,3,7 其他pin腳是不支援中斷模式的,因此按鈕開關必須接前述5個pin腳的其中一個,其餘不會動
### 原始interrupt教學文件
https://create.arduino.cc/projecthub/ronbentley1/button-switch-using-an-external-interrupt-7879df
### 參考範例
參考以上原始文件,我將上課中展示的作品簡化成只有一顆按鈕去控制三色燈
> 一開始為全暗
> 按一次為綠燈
> 再按一次為藍燈
> 再按一次又回到全暗
### 以下為接線步驟
根據老師投影片**Arduino3開關** 第七張投影片

開關接在pin D2
再根據老師的投影片 **Arduino1LED** 第36張投影片
RGB三色燈的
R端接在D10
B接在D11
G接在D12
或可個別使用三顆燈,分別為紅燈 藍燈 綠燈
RGB燈參考接法
藍線一邊接到D11 另一邊串接100ohm電阻 再接到RGB LED的**最短**的那根pin腳`(B)`
綠線一邊接到D12 另一邊串接100ohm電阻 再接到RGB LED的**次長**的那根pin腳`(G)`
紅線一邊接到D10 另一邊串接100ohm電阻 再接到RGB LED的**次短**且在最長地線旁邊的那根pin腳`(R)`
最長的那端接到麵包板上的Ground(-)


### 參考程式碼
只要線接對,應不需修改即可做動
以下有一大段標示arduino.cc的代碼是直接拷貝自原始教學文件
其餘我加入的部份為中文註釋
第一段先定義pin腳位置,若照前述串接 可不用修改直接複製以下代碼
再貼到arduino程式編輯器
```c=
#define BUTTON_PIN 2 /* 需注意Leonardo只能使用0,1,2,3,7支援interrupt,
* 根據投影片接在D2 */
#define R_PIN 10
#define B_PIN 11
#define G_PIN 12
/* 根據按鈕被按壓次數改變的變數,0->1->2->0 */
int option_switch=0;
```
接下來這段直接拷貝到程式中間,在ardublock產生的代碼setup()及loop()之前
```c=
////// 以下代碼來自 arduino.cc Interrupt 示範篇
int button_switch = BUTTON_PIN; // external interrupt pin
#define switched true // value if the button switch has been pressed
#define triggered true // controls interrupt handler
#define interrupt_trigger_type RISING // interrupt triggered on a RISING input
#define debounce 10 // time to wait in milli secs
volatile bool interrupt_process_status = {
!triggered // start with no switch press pending, ie false (!triggered)
};
bool initialisation_complete = false; // inhibit any interrupts until initialisation is complete
//
// ISR for handling interrupt triggers arising from associated button switch
//
void button_interrupt_handler()
{
if (initialisation_complete == true)
{ // all variables are initialised so we are okay to continue to process this interrupt
if (interrupt_process_status == !triggered) {
// new interrupt so okay start a new button read process -
// now need to wait for button release plus debounce period to elapse
// this will be done in the button_read function
if (digitalRead(button_switch) == HIGH) {
// button pressed, so we can start the read on/off + debounce cycle wich will
// be completed by the button_read() function.
interrupt_process_status = triggered; // keep this ISR 'quiet' until button read fully completed
}
}
}
} // end of button_interrupt_handler
bool read_button() {
int button_reading;
// static variables because we need to retain old values between function calls
static bool switching_pending = false;
static long int elapse_timer;
if (interrupt_process_status == triggered) {
// interrupt has been raised on this button so now need to complete
// the button read process, ie wait until it has been released
// and debounce time elapsed
button_reading = digitalRead(button_switch);
if (button_reading == HIGH) {
// switch is pressed, so start/restart wait for button relealse, plus end of debounce process
switching_pending = true;
elapse_timer = millis(); // start elapse timing for debounce checking
}
if (switching_pending && button_reading == LOW) {
// switch was pressed, now released, so check if debounce time elapsed
if (millis() - elapse_timer >= debounce) {
// dounce time elapsed, so switch press cycle complete
switching_pending = false; // reset for next button press interrupt cycle
interrupt_process_status = !triggered; // reopen ISR for business now button on/off/debounce cycle complete
return switched; // advise that switch has been pressed
}
}
}
return !switched; // either no press request or debounce period not elapsed
} // end of read_button function
////// 以上代碼來自於arduino.cc interrupt 示範篇
```
以下程式碼
setup(), loop()裡的內容可插入自己的ardublock產生的代碼
注意中文註解標示處 以上/以下(7~9)及(42~44)部分
```c=
void setup() {
attachInterrupt(digitalPinToInterrupt(button_switch),
button_interrupt_handler,
interrupt_trigger_type);
initialisation_complete = true; // open interrupt processing for business
//以下將ardublock產生的setup()中的代碼拷貝過來
//以上將ardublock產生的setup()中的代碼拷貝過來
}
void loop() {
//在迴圈中加入以下這段讀取按紐最新狀態,並根據數值做出對應的動作
if (read_button() == switched) {
option_switch++;
option_switch%=3; //option_switch為按鈕狀態:
// 共三階段,0/1/2循環
// 0為關閉
// 1為綠燈
// 2為藍燈
// 0->1->2->0
//對應動作
//先把燈全點暗
digitalWrite(R_PIN, LOW);
digitalWrite(G_PIN, LOW);
digitalWrite(B_PIN, LOW);
// 根據按鈕狀態做出對應動作
switch (option_switch) {
case 1: //當按鈕狀態為1時, 點綠燈
digitalWrite(G_PIN, HIGH);
break;
case 2: //當按鈕狀態為2時,點藍燈
digitalWrite(B_PIN, HIGH);
break;
}
}
// 以下放本來用ardublock產生的loop()裡的代碼,我這裡只有delay(100)
delay(100);
// 以上放本來用ardublock產生的loop()裡的代碼
}
```