close

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)
 
# use its pull up resistor, which will guarantee that it will be in a known state (VCC) when no electrical signal is applied. 
 
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程式

 

 

 

 

 

 

arrow
arrow
    創作者介紹
    創作者 stanley 的頭像
    stanley

    史坦利Stanley程式Maker的部落格

    stanley 發表在 痞客邦 留言(0) 人氣()