中斷

想要知道某個裝置目前的狀態,例如按鈕是否按下,有兩種作法:一種為輪詢(polling),也就是透過一個迴圈不斷的去檢查按鈕所在的輸入腳電位高低;另外一種是中斷(interrupt),意思是當按鈕電位有變化時,再打斷目前 CPU 正進行的工作來處理中斷要做的事情。輪詢程式比較好寫,但執行上比較沒有效率,也耗能。中斷需要硬體支援,程式架構比較複雜一點,但執行上比較有效率。

使用中斷必須先寫一個中斷服務函數,其實就是一個一般的函數,但參數固定,這個函數目的是當中斷發生時,CPU 會暫停目前手上的工作改執行該函數中的程式碼。函數寫法如下,函數名稱與參數名稱都可以任意,但只能接受一個參數,當中斷發生時,會傳進產生中斷的那個裝置。

def buttonClickHandle(sender):
    # code here

這個函數需要「註冊」,這樣當中斷發生時,系統才會知道要呼叫哪個函數,範例如下:

from machine import *

def buttonClickHandle(sender):
    print('hello')

button = Pin(4, Pin.IN, Pin.PULL_DOWN)
button.irq(trigger=Pin.IRQ_RISING, handler=buttonClickHandle)

上面這個程式會在按鈕按下並且鬆手後,也就是電位從低電位變成高電位時產生中斷,這時候程式會跳到 buttonClickHandle() 函數中去執行。參數 trigger 除了 IRQ_RISING 這個值外,還可以使用 IRQ_FALLING,這是從高電位掉到低電位時產生中斷,或是 IRQ_RISING | IRQ_FALLING ,表示只要電位有變化就要產生中斷。

彈跳現象

當使用中斷來偵測按鈕訊號時,由於非常快速的電位變化都會被偵測到(輪詢會 lose 一些),因此按鈕的彈跳現象會變的很明顯。彈跳是按鈕必然發生的物理現象,當按鈕中的金屬接點在接觸的瞬間會產生電位不穩定狀況,也就是高低電位會來回變化幾次,這時就會看到「我明明只按了一次按鈕,怎麼卻變成按了三次或五次」。除非按鈕本身已經處理了彈跳,不然一個單純的按鈕就需要我們自己來解彈跳(debounce)。有兩種作法,一種是在按鈕上面加上一顆電容來吸收彈跳時的電位變化;另外一種是透過程式碼來處理。由於物聯網系統上的按鈕通常不會快速地連點連按,所以透過程式來處理就可以了。作法是當第一個中斷發生時就取消中斷服務函數,等一段時間後再註冊一次,這樣就可以將後續的彈跳濾掉了。範例如下,延遲的 0.5 秒可自行拿捏,秒數越大濾掉的效果越好,但按鈕反應越慢;反之按鈕反應快,但彈跳現象可能無法完全濾除。

from machine import *
from utime import *

def buttonClickHandle(sender):
    sender.irq(None)
    print('hello')
    sleep(0.5)
    sender.irq(trigger=Pin.IRQ_RISING, handler=buttonClickHandle)

button = Pin(4, Pin.IN, Pin.PULL_DOWN)
button.irq(trigger=Pin.IRQ_RISING, handler=buttonClickHandle)

★本系列收錄於「第一次就上手」選單

發表迴響