2017.07.12
要寫出比較複雜或功能性的程式,總是要多著墨一下在處理器的中斷處理機制,除非你的程式很簡單只是單一程式或只做簡單地LED控制,那可以不用學習中斷處理機制,否則你會發現中斷處理能幫忙你解決很多程式碼。
Interrupt handlers(中斷處理) - 也被做中斷服務常式(ISR)或定義成callback functions。它們的概念是當某一事件發生時去做一些事,這些事件可能是計時器觸發或某個pin脚位電壓改變。這些事件也可能發生在程式執行時的任何點。
中斷處理程式碼撰寫技巧
- (1)中斷服務常式盡可能短及簡單。
- (2)中斷服務常式內不能使用記憶體分配的功能。
- (3)中斷服務常式需要返回多個bytes值時,使用事先分配好的bytearray資料型態傳送,如在主程式及中斷服務常式要分享多個整數值時使用array資料型態傳送。
- (4)主程式及中斷服務常式要存取資料時,優先考慮先在主程式中先暫停中斷功能,存取資料後再恢復中斷(也就是臨界區段的處理方式(Critical section),它是指一個存取共用資源(例如:共用裝置或是共用記憶體)的程式片段,而這些共用資源有無法同時被多個執行緒存取的特性)。
- (5)中斷服務常式要分配一個emergency exception buffer去處理ISR中的錯誤。例如
import micropython micropython.alloc_emergency_exception_buf(100)
中斷處理程式範例
def callback(p): print('pin change', p) 下面設定兩個pin脚為輸入
from machine import Pin
p0 = Pin(0, Pin.IN)
p2 = Pin(2, Pin.IN)
下面設定兩個pin脚何時被觸發
p0.irq(trigger=Pin.IRQ_FALLING, handler=callback)
p2.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=callback)
接脚0被觸發當接脚0的電位從高電位變為低電位時啓動 callback
接脚2被觸發當接脚2的電位從高電位變為低電位時或從低電位變為高電位時啓動 callback
#------------------------
下面為時間器物件的中斷服務常式範例
每一個計時器由記數器組,而計數器以固定的頻率做計數(通常以HZ為單位)。當計數器計數到設定的時間時即觸發一個事件,而我們設定事件使用callback的方式就可以呼叫我們定義的函數。
資料來源: http://docs.micropython.org/en/latest/wipy/wipy/tutorial/timer.html
#----------------------
Example1: 使用固定頻率觸發一個LED
from machine import Timer
from machine import Pin
led = Pin('GP16', mode=Pin.OUT) # enable GP16 as output to drive the LED
tim = Timer(3) # create a timer object using timer 3
tim.init(mode=Timer.PERIODIC) # initialize it in periodic mode
tim_ch = tim.channel(Timer.A, freq=5) # configure channel A at a frequency of 5Hz
tim_ch.irq(handler=lambda t:led.toggle(), trigger=Timer.TIMEOUT) # toggle a LED on every cycle of the timer
Example using named function for the callback:
from machine import Timer
from machine import Pin
tim = Timer(1, mode=Timer.PERIODIC, width=32)
tim_a = tim.channel(Timer.A | Timer.B, freq=1) # 1 Hz frequency requires a 32 bit timer
led = Pin('GP16', mode=Pin.OUT) # enable GP16 as output to drive the LED
def tick(timer): # we will receive the timer object when being called
global led
led.toggle() # toggle the LED
tim_a.irq(handler=tick, trigger=Timer.TIMEOUT) # create the interrupt
Further examples:
from machine import Timer
tim1 = Timer(1, mode=Timer.ONE_SHOT) # initialize it in one shot mode
tim2 = Timer(2, mode=Timer.PWM) # initialize it in PWM mode
tim1_ch = tim1.channel(Timer.A, freq=10, polarity=Timer.POSITIVE) # start the event counter with a frequency of 10Hz and triggered by positive edges
tim2_ch = tim2.channel(Timer.B, freq=10000, duty_cycle=5000) # start the PWM on channel B with a 50% duty cycle
tim2_ch.freq(20) # set the frequency (can also get)
tim2_ch.duty_cycle(3010) # set the duty cycle to 30.1% (can also get)
tim2_ch.duty_cycle(3020, Timer.NEGATIVE) # set the duty cycle to 30.2% and change the polarity to negative
tim2_ch.period(2000000)
#----------------------
Example3: ESP32 MicroPython: External interrupts
import
machine
interruptCounter
=
0
totalInterruptsCounter
=
0
def
callback(pin):
global
interruptCounter
interruptCounter
=
interruptCounter
+
1
p25
=
machine.Pin(
25
, machine.Pin.IN, machine.Pin.PULL_UP)
p25.irq(trigger
=
machine.Pin.IRQ_FALLING, handler
=
callback)
#
interrupt should be triggered when a falling edge is detected in the input signal connected to the pin. while
True
:
if
interruptCounter>
0
:
state
=
machine.disable_irq()
interruptCounter
=
interruptCounter
-
1
machine.enable_irq(state)
totalInterruptsCounter
=
totalInterruptsCounter
+
1
print
("Interrupt has occurred:"
+
str
(totalInterruptsCounter))
註: GPIO 輸入腳最好用 2K~10K 上拉電阻接至 3.3V (使用 2K 對雜訊抑制力較佳), 不過 GPIO 腳內部都有內建上拉電阻, 可以透過軟體設定開啟
#-----偵測RSSI值的MicroPython程式-------
import network
from machine import Timer
wlan = network.WLAN(network.STA_IF)
def connectAP(ssid, pwd):
if not wlan.isconnected():
wlan.active(True)
wlan.connect(ssid, pwd)
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())
connectAP("無線網路AP名稱", "無線網路密碼")
# 每2秒顯示信號強度
tim = Timer(-1)
tim.init(period=2000, mode=Timer.PERIODIC,
callback=lambda t:print('RSSI:', wlan.status('rssi')))
try:
while True:
pass
except:
tim.deinit()
print('bye!')
參考資料: MicroPython: Interrupts with ESP32 and ESP8266
偵測ESP8266的Wi-Fi RSSI(接收信號強度)的Arduino和MicroPython程式
留言列表