# Arduino 按鈕開關測試 (二) : 硬體中斷法 (Interrupt)
"若好幾支副程式A程式B程式C程式D程式依序被呼叫,然後B程式值行的時間太久所以我想中斷後直接看到C程式"
簡言之就是希望中斷發生後跳到下一個副程式. 因為篇幅有點長, 所以我把回覆內容記在這裡. 已經好久沒寫 Arduino 程式, 有點生疏了. 還好勤於寫測試筆記有案可稽, 要恢復記憶比較容易.
硬體接線很簡單, 我在 D2 接上一個按鈕, 啟用內部上拉電阻讓平時為 HIGH, 而按下按鈕時接地為 LOW 以觸發中斷.
在下列模擬程式中我定義了 a(), b(), c(), d() 四個副程式, 裡面又呼叫一個 doSomething() 副程式來模擬耗時的工作, 這裡使用 delay(1) 跑 10000 次迴圈, 故正常無中斷情況下 10 秒才會跑完. 注意, 這裡不用 delay(1000) 跑 10 圈, 或直用 delay(10000) 的原因是, delay() 被中斷後跑去執行中斷常式再回來時, 似乎不是跳到 delay() 下一個指令, 而是會繼續把 delay() 沒跑完的休息時間跑完, 這樣會造成中斷後還是得等 delay() 休息結束才會去執行下一個副程式.
在 loop() 中依序呼叫此四個副程式, 當中斷發生時主控權會轉移到 int0() 執行, 印出 "Interrupted ..." 同時把預設為 false 的 breakloop 旗標設為 true 以便回到被中斷的 doSomething() 時可以跳出迴圈, 執行下一個副程式 :
const byte intPin=2; //interrupt pin D2
int debounceDelay=200; //debounce delay (ms)
void int0();
void a();
void b();
void c();
void d();
void doSomething();
boolean debounced();
boolean breakloop=false; #跳出迴圈旗標
void setup() {
Serial.begin(9600);
pinMode(intPin, INPUT_PULLUP); //enable pull-up resistor of input pin
attachInterrupt(0, int0, LOW); //enable int0 (D2)
}
void loop() {
a();
b();
c();
d();
}
void a() {
Serial.println("running a ...");
doSomething();
}
void b() {
Serial.println("running b ...");
doSomething();
}
void c() {
Serial.println("running c ...");
doSomething();
}
void d() {
Serial.println("running d ...");
doSomething();
}
void doSomething() {
for (int i=0; i<10000; i++) {
if (breakloop) { #若被中斷服務程式 int0() 設定為 true
breakloop=false; #重置旗標
break; #跳出迴圈
}
delay(1);
}
}
void int0() { //interrupt handler
if (debounced()) {
Serial.println("Interrupted ... ");
breakloop=true;
}
}
boolean debounced() { //check if debounced
static unsigned long lastMillis=0; //record last millis
unsigned long currentMillis=millis(); //get current elapsed time
if ((currentMillis-lastMillis) > debounceDelay) {
lastMillis=currentMillis; //update lastMillis with currentMillis
return true; //debounced
}
else {return false;} //not debounced
}
running a ...
Interrupted ...
running b ...
Interrupted ...
running c ...
running d ...
Interrupted ...
running a ...
Interrupted ...
running b ...
running c ...
running d ...
running a ...
running b ...
Interrupted ...
running c ...
Interrupted ...
running d ...
每按一下按鈕就會馬上跳到下一個副程式執行.
不好意思還讓版主你為了解決我的問題讓你花時間寫這篇文章
回覆刪除看完這文章讓我對這部份更加的了解
提供的測試程式也沒問題
不過我想更加了解今天換成是想重while()中斷出來該怎麼做阿?
Dear 傅勁崴 :
回覆刪除您只要將 for loop 改為 while loop 就行啦! 但這樣就會變成開機後一直執行 a, 中斷後才會執行 b.
你好
回覆刪除問個問題
請問
A副程式B副程式C副程式D副程式
首先A程式先跑再來換B但是B程式裡頭有while的判斷式造成沒辦法進入C程式
這我該怎麼做?
你 while 裡面要設條件讓他跳出來, 例如用 breakloop 旗標使其跳出來 :
回覆刪除void doSomething() {
while (!breakloop) {
delay(1);
}
breakloop=false;
}
你好請問一下~
回覆刪除void int0();
void a();
void b();
void c();
void d();
void doSomething();
這些是必要寫上去的嗎?
我在寫副程式時上面都沒有寫這些,
雖然可以跑但是不是有什麼問題會發生還是要注意的呢?
這是 C 語言的要求, Arduino IDE 編譯器似乎不會有問題 (不同編譯器).
回覆刪除謝謝大大
回覆刪除感謝,很棒的文章。
回覆刪除列入我的收藏。
老師您好,請問有辦法是不靠外部的按鈕,而是靠輸入字串或字元, 然後中斷迴圈的方式嗎?
回覆刪除此處講的都是硬體中斷, 即透過 pin2/3 觸發之中斷, 另外還有定時器中斷等, 參考 :
回覆刪除https://www.arduino.cn/thread-13205-1-1.html
作者已經移除這則留言。
回覆刪除請問是否可讓兩個程式同時不停的循環執行呢?
回覆刪除像是兩個Loop在執行
Hi, 這我沒試過, Arduino 是單核, 事實上沒辦法真正 concurrent, 但透過 TimedAction.h 函式庫可以模擬多個 thread 執行, 參考 :
回覆刪除https://create.arduino.cc/projecthub/reanimationxp/how-to-multithread-an-arduino-protothreading-tutorial-dd2c37
請問一下,怎麼一個按鍵控制4 個燈,每個燈量15分鐘,在切換時要非常順利
回覆刪除按鍵按下去四個燈輪流亮 15 分鐘, 然後不斷輪迴, 直到再次按下按鍵就停止嗎?
回覆刪除大大您好,想請問一下
回覆刪除我在做輸送帶接紅外線感測物件
感測到後發送stp
但因為物體是長的
想要寫出
紅外線感測物體後輸送帶停止,等待inf訊號,inf收到訊號後再發送stp
但使用delay的話又會卡住程式
請問大大 這裡的等待命令有什麼解法嗎?
int Sensor=digitalRead(7);
if(Sensor == 0)
{
Serial.println(Sensor);
digitalWrite(stp, HIGH);
delay(1000);
digitalWrite(stp, LOW);
while(digitalRead(inf) == 0){
delay(2000);
}
else(digitalRead(inf) == 1) {
break;
}
}
}
sleep() 會使程序停住, 這可能要用到多執行緒來做, 但我也沒做過, 可參考這篇 :
回覆刪除https://www.itread01.com/p/171971.html
非常感謝您!
回覆刪除