| ESP32 教學 | MicroPython | Timer Class | 203 |

說到微控制器的 Timer,在一般的嵌入式平台中,可以應用到很多的需求,例如定時啟動某項功能、計數觸發事件等,不過如果使用暫存器設定搭配C語言的話,需要知道的知識相當的繁多,而目前在ESP32 MicroPython 平台上,Timer Class 這個類別的用法就是可以做定時或一次觸發的機制,設定就相當簡單,這篇就來瞭解一下 Timer 如何設定~!

1. Timer Class 用法

Timer 的類別一樣放在 machine 模組下,所以一開始需先 import Timer 類別,下面為 Timer 初始化範例:

from machine import Timer
tim1=Timer(1)
tim1.init(period=500, mode=Timer.PERIODIC, callback=None)

第一個步驟就是先建構 Timer 的物件 tim1=Timer(1),Timer()刮號內的 ID 以ESP32 的硬體為例,可以填入 0-3,所以共有 4 組設定可用,這邊要留意的是,目前ESP32是只支援硬體 Timer,所以 ID 不可以填入-1(ESP8266 支援visual timer,所以可以填入 -1)。第 2 個步驟就是初始化 tim1.init,需要填入period、mode、callback 三個參數:

  • period:單位是 ms,也就是你可以設定 Timer 時間週期,網路資料查詢到最大可以設定到 3435973836。(JIMI哥自己沒設過這麼大的數字啦,因為有時 Timer 通常需要考慮內部溢位問題)
  • mode: 可以選擇一次觸發Timer.ONE_SHOT或是週期性觸發Timer.PERIODIC
  • callback: 就是當 Timer 觸發時,要呼叫的函數,如果沒有的話,可以填入None。

要初始化 Timer 就是設定這三個參數即可,記得喔,初始化完後 Timer 就會開始運作,至於如何停止 Timer 的運作,就要利用 tim1.deinit() 方法。

2. LED 閃爍範例

還記得如何實現 Led 閃爍功能嗎?程式上可以使用兩種作法:一是用 Polling 方式,像是JIMI哥之前在Arduino103的分享文所使用的,另一個就是利用 Timer 中斷的方式了,先來複習 Polling 的範例,我們可以利用 Nodemcu-32s 內建的 Gpio2 Led操作:

from machine import Pin
import time
LED=Pin(2,Pin.OUT)

while 1:
    LED.value(1)
    time.sleep(0.5)
    LED.value(0)
    time.sleep(0.5)

這樣的程式會有什麼問題? 功能上沒有問題,只是比較沒有效率,注意到time.sleep(0.5)這行,這就是關鍵的地方,cpu會因閒置等待0.5秒而且不會做任何 process,如果我們知道 Timer 的用法,並利用週期性呼叫的方式就可以把上述程式碼改成下面:

from machine import Pin,Timer
LED=Pin(2,Pin.OUT)

def led_switch(x):
    LED.value(not LED.value())
    
tim1=Timer(1)
tim1.init(period=500, mode=Timer.PERIODIC, callback=led_switch)
try:
    while 1:
        pass
except:
    tim1.deinit()
    print('stopping')

簡單進行程式說明:

  1. 第 4-5 行:定義一個 led_switch 的副程式,功能就是將 led 的值進行反轉,也就是如果是 High 的話,就會變成 Low,反之則亦然。
  2. 第 7-8 行: 初始化 timer1 的物件,並指定為 0.5 秒週期觸發,每隔 0.5 秒便會執行 led_switch 一次。
  3. 第 9-14 行: while 主程式內執行需要的動作,目前為 pass,except 內容則為放入中斷程式後,印出 stopping。

注意到兩段程式最大的不同是什麼嗎? 在 Timer 的寫法中,主程式 while 內是可以放入我們所需要的動作的,例如複雜又耗時的演算法等,因為計時的部份是採用MCU 內部的硬體 Timer 進行,所以不會佔用 CPU 資源,這樣的作法就遠比上面Polling 更有效率。

3. 結語

今天這篇簡單的介紹的 Timer 的用法,並一個簡單的 LED 閃爍案例,說明硬體Timer的優勢與特色,實務上來說,Timer呼叫的副程式內(例如上面的led_switch),建議不要放入執行時間較長的指令(例如 UART print),因為一旦副程式所執行時間過長,會影響主程式的運行,想像一種情境,在需要定時更新參數的即時控制系統中,Time r的週期時間如果為設定為 10ms 更新參數,一旦在副程式內放入 Process 時間較長的指令(例如總時間為 11ms),開啟 Timer 後,CPU資源便會不停的卡在副程式內(因為 timer 的週期不停的觸發),導致主程式所需要的動作異常,這也就是不建議的原因,各位朋友可以先將這個概念放在心上,日後如果碰到相關 Issue 時,可以有方向解決問題。今天就分享到這裡,如果在測試或練習有遇到問題的朋友,歡迎下方留言給我互相交流!

↓↓↓↓↓↓賣場連結↓↓↓↓↓↓

歡迎大家有需要的話,可以多多支持一下我們的蝦皮賣場喔! 😀 

吉米家官方店-創客機器人材料專賣 https://shopee.tw/jimirobot.tw

Follow JIMI哥 Twitter : https://twitter.com/jimirobot  <–得到最新文章通知

This Post Has 2 Comments

  1. 請教大大兩個問題,
    1.為什麼def led_switch(x)要傳一個參數x進去?
    2.把程式改成這樣好像也可以Run,看起來也比較簡單,但不知道會不會有什麼後遺症?
    try:
    tim1=Timer(1)
    tim1.init(period=500, mode=Timer.PERIODIC, callback=led_switch)
    except:
    tim1.deinit()
    print(‘stopping’)

    煩請撥冗解惑,感恩。

    1. Hi Ben:

      1.關於led_switch(x)為什麼要加一個參數的問題,這是因為在micropython官方的說明文件提到如果要使用timer的callback function時,必須帶入一個argument( http://docs.micropython.org/en/latest/library/pyb.Timer.html#pyb.Timer.callback ),而這個argument會是一個timer object,所以我這邊使用的x,其實就是我們所建立的tim1 object。(當然也可以命名為其他名字,只是如果沒有帶入時,會有錯誤發生)

      2.如果把我的範例改成你所修改的程式,執行上是沒問題的,我的寫法是基於『希望LED閃爍於背景執行,主程式可以執行其他重要的工作』的想法所寫的,但因為是簡單範例所以主程序的動作我修改成pass,這樣應該就很好理解了。

      以上內容,希望有回答到你的問題,歡迎持續交流討論~

發佈留言

Close Menu