常見的微控制器系列,例如Arduino的ATmega系列(像是ATmega328P)、STM32系列、PIC系列,或是ESP32,通常都支援多種不同的中斷類型。ESP32可以用硬體中斷來處理外部硬體或模組引起的中斷,像是GPIO或是觸碰(TOUGH)中斷;用軟體中斷處理定時器中斷,本篇主要說明的是硬體中斷。
什麼是中斷?
中斷(Interrupt)是一種在微控制器在執行主要工作時,仍然能夠立即回應外部事件的一種機制。一個設計得當的中斷,不僅可以簡化程式邏輯,還能提高處理器的執行效率。我們可以用中斷來偵測開關、旋轉編碼器,或是光遮斷器等的變化。要理解中斷,讓我們先用一個例子來說明。
假如要偵測一個按鈕是否被按下,我們可能會這樣寫:
int buttonPin = 25; // 將按鈕連接到 GPIO 25
bool buttonState = 0;
void setup() {
pinMode(buttonPin, INPUT);
}
void loop() {
// 反覆檢查按鈕狀態
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
// 執行按下按鈕的相應處理
delay(30); // 避免開關彈跳
}
// 執行主程式的其他任務
// ...
}
像這樣依照程式流程定時且反覆檢查數值的方法就稱做輪詢。在不使用中斷的情況下,通常會使用輪詢來檢查外部事件,優點是程式寫起來直覺、好理解,但是這其實會讓ESP32的CPU在很低的效率下運作,因為系統需要在主程式迴圈中不斷的檢查GPIO的狀態。如果主程式區塊還有很多需要處理的任務,檢查的速度也會變慢。
而使用中斷的好處是,只要特定的接腳偵測到狀態發生變化,就能立即「中斷」CPU現在正在做的事情,優先處理目前的事件下要完成的任務。
換句話說,中斷機制允許系統只在事件發生時才進行處理,這麼一來就能節省不必要的處理器運算和電力消耗,而我們也能確保特定事件發生時可以被即時的處理,是不是一舉多得呢?
ISR(Interrupt Survice Routine)
從上面的例子中,我們可以知道中斷的核心概念是「事件驅動」。當有更優先、更需要即時處理的事件發生時,CPU就能夠在最短的時間內反應並且處理完畢,不需要經過輪詢的等待時間。
當中斷發生時,CPU會暫時停止執行主程式,轉而執行相應的處理程序。用中斷的術語來說,這個處理程序叫做「中斷服務程序 (Interrupt Survice Routine, ISR)」。 ISR是專門用來處理中斷事件的一段特殊函式,負責處理中斷發生後要執行的任務或是操作,它不能傳入任何參數,也不會回傳任何內容。當ISR完成任務後,CPU會返回主程式繼續執行原本的程式。
ISR具有以下特點:
- CPU一次只能處理一個ISR;
- 發生多個中斷時,會依照ISR的優先順序執行;
- 每個ISR都專注於處理特定的事件;
- ISR越簡單越好,要盡可能保持很短的執行時間,才能減少對系統的干擾;
- 處理中斷時,通常會使用全域變數在ISR和主程式之間傳遞資料,為了確保ISR跟主程式共享的變數可以正常更新,會將變數宣告成volatile型態(讓變數可以被多個執行緒訪問)。
ESP32的GPIO中斷
ESP32的內部使用中斷矩陣來處理許多不同的中斷來源。
所有ESP32能使用的GPIO都可以拿來當作GPIO中斷接腳,其中10隻接腳支援觸碰感測器(touch sensor)中斷(請參考進階功能章節)。
接下來說明中斷常用的語法:
設定中斷
在Arduino IDE中,我們透過將GPIO接腳與對應的ISR函式綁定來啟用中斷,設定中斷時可以使用 attachInterrupt
attachInterrupt(GPIO_pin, ISR, mode);
參數說明:
- GPIO_pin: 將其設定為中斷接腳
- ISR: 當此中斷發生後要執行的ISR函式
- mode: 定義中斷觸發的條件。有八種模式可以選擇。
//Interrupt Modes
#define DISABLED 0x00
#define RISING 0x01
#define FALLING 0x02
#define CHANGE 0x03
#define ONLOW 0x04
#define ONHIGH 0x05
#define ONLOW_WE 0x0C
#define ONHIGH_WE 0x0D
Tips:
在Arduino-esp32核心函式庫的esp32-hal-gpio.h檔案中可以找到這些模式的定義。
說明:
- DISABLED: 禁用GPIO中斷
- RISING: 上升邊沿觸發(rising edge trigger)
- FALLING: 下降邊沿觸發(falling edge trigger)
- CHANGE: 任意邊沿觸發 (any edge trigger)
- ONLOW: 低電位觸發(low level trigger)
- ONHIGH: 高電位觸發(high level trigger)
- ONLOW_WE和ONHIGH_WE是當ESP32在 Light-sleep模式下用來喚醒CPU的中斷,一般比較少使用到。”WE”是wakeup enable的縮寫。
關閉中斷
detachInterrupt語法可以用來關閉中斷,取消ESP32對特定接腳的監聽。
detachInterrupt(GPIO_pin);
當我們關閉特定接腳的中斷時,需要再次呼叫attachInterrupt 或是重新啟動系統才能重新啟用中斷。
中斷服務程序(ISR)
ISR函式會在中斷被呼叫時執行,它的語法結構看起來會像這樣:
void IRAM_ATTR ISR() {
// 中斷函式內容
}
其中ISR是自訂的函式名稱,而IRAM_ATTR的意思是將ISR函式存放在IRAM(Instruction RAM),而不是flash。從IRAM讀取ISR 函式可以加快載入速度。
接下來,讓我們用實際的範例來了解如何使用GPIO中斷。
範例1:使用按鈕改變LED狀態
這個範例使用按鈕觸發ESP32的GPIO中斷,當按鈕狀態有變化(上升或下降)時,LED的狀態也會跟著改變。
只需不到短短一分鐘...
輸入您的信箱與ID註冊即可享有一切福利!
會員福利
免費電子報
會員搶先看
主題訂閱
好文收藏