2019年7月12日 星期五

MicroPython on ESP32 學習筆記 (六) : 看門狗計時器 WDT

今天重新在之前 Tim 大哥給我的 bpi:bit 開發板上燒錄最新的韌體 (bpi:bit 發佈的, 不是 MicroPython 官方的), 發現此韌體竟然有實作藍芽驅動, 實在太棒了. 仔細研究了此開發板的配備, 有按鈕, 溫度, 光度, 以及九軸感應器, 還有 25 位元全彩 LED 與蜂鳴器, 覺得學習物聯網所需要的常用周邊幾乎都有了, 實在是非常優的開發板, 決定今後的 ESP32 實驗將以 bpi:bit 為主, NodeMCU-32S 為輔, 主要是它的韌體實作了較完整之驅動程式, 參考台中市教育局網路教學平台的這篇文章 :

http://elesson.tc.edu.tw/md221/mod/page/view.php?id=4403

關於 bpi:bit 的簡介與韌體燒錄參考 :

ESP32 開發板 bpi:bit
bpi:bit ESP32 開發板測試 (一) : 燒錄 MicroPython 韌體

晚上去還書回來後心血來潮, 想到以前測試 ESP8266 的看門狗 (Watch Dog Timer, WDT) 功能發現當時 MicroPython on ESP8266 (v1.9) 尚未支援的 WDT 功能, 可以建立 WDT 物件, 但無法指定監視時間. 參考 :

MicroPython on ESP8266 的看門狗 WDT 無法設定時限問題

今天測試居然可以耶! 我在 bpi:bit (v1.10) 與 NodeMCU-32S (v1.11) 上測試都可以, 好奇它到底何時開始完善的, 就去查了 change log (異動日誌), 原來是在 2019-01-26 釋出的 v1.10 版韌體就已經開始完整支援 WDT 了 :

http://micropython.org/resources/micropython-ChangeLog.txt

- let machine.WDT trigger the software WDT if obj is not fed (ESP8266)
- implement machine.WDT() class (ESP32)

關於 WDT 的官方文件參考 :

https://docs.micropython.org/en/latest/library/machine.WDT.html

WDT 是一個放在內建的 machine 模組中的類別 :

>>> import machine   
>>> dir(machine) 
['__class__', '__name__', 'ADC', 'DAC', 'DEEPSLEEP', 'DEEPSLEEP_RESET', 'EXT0_WAKE', 'EXT1_WAKE', 'HARD_RESET', 'I2C', 'PIN_WAKE', 'PWM', 'PWRON_RESET', 'Pin', 'RTC', 'SDCard', 'SLEEP', 'SOFT_RESET', 'SPI', 'Signal', 'TIMER_WAKE', 'TOUCHPAD_WAKE', 'Timer', 'TouchPad', 'UART', 'ULP_WAKE', 'WDT', 'WDT_RESET', 'deepsleep', 'disable_irq', 'enable_irq', 'freq', 'idle', 'lightsleep', 'mem16', 'mem32', 'mem8', 'reset', 'reset_cause', 'sleep', 'time_pulse_us', 'unique_id', 'wake_reason']
>>> machine.WDT
<class 'WDT'>    

不過為了方便通常會用 from~import~ 語法直接匯入 WDT 類別, 在官方的 v1.11 韌體環境下用 dir(WDT) 檢視可知已經實作了 feed() 方法, 可用來設定監視狀態的時間 :

>>> from machine import WDT   
>>> dir(WDT)     
['__class__', '__name__', 'feed']    

但在 bpi:bit 最新的 v1.10 韌體測試, 發現它提供較多方法, 多了 delete() 與 status() 方法 :

MicroPython v1.10-223-g43faa7e85-dirty on 2019-06-23; ESP32 module with ESP32
Type "help()" for more information.
>>> from machine import WDT 
>>> dir(WDT) 
['__class__', '__name__', 'delete', 'feed', 'status']

看門狗是一個用計時器控制是否系統要重開機的機制, ESP8266/ESP32 MPU 內部有一個 WDT 計時器, 建立 WDT 物件並指定 timeout 時間 (單位=毫秒) 即起始了 WDT 計時器, 程式必須在計時器到期前呼叫 feed() 方法來重設計時器, 否則 timeout 時間一到便會觸發系統重開機動作, 相當於自動按下 RESET 鈕的效果, 這樣可以讓系統重新啟動運轉, 而毋須等待維護人員去現場按下 RESET 鍵.

wdt=WDT(timeout=20000)      #起始看門狗計時器 (20 秒) 並設置旗標
wdt.feed()                                   #重設 WDT 計時器
wdt.delete()                                #刪除 WDT 計時器 ()
wdt.status()                                #查詢 WDT 狀態 (0=運作中, 261=已刪除)

注意, delete() 與 status() 僅見於 bpi:bit 韌體, 官方韌體只有 feed() 一個方法. 如果需要 delete() 功能建議燒錄 bpi:bit 提供的 MicroPython 韌體.

Arduinno 也有看門狗功能 (include wdt.h 即可), 呼叫 wdt_enable(timeout) 即啟動看門狗計時器, 呼叫 wdt_disable() 是關閉計時器, 而 wdt_reset() 則是重設計時器 (但 WDT 功能還繼續運作). 不過 Arduino 看門狗的 timeout 只能設定最小 15ms 最大 8s 共 10 種定時時間, 不能隨意設定, 而 MicroPython 可以隨意設, 但不可小於 1 秒 (即 timeout 至少要 1000 以上), 參考 :

Arduino - 看门狗定时器(WDT:Watch Dog Timer)
Arduino運作守護者:看門狗計時器簡介
AVR&Arduino|看門狗定時器(Watch Dog Timer)的使用
看門狗計時器

以下是是在 bpi:bit 開發板上做的測試 :

MicroPython v1.10-223-g43faa7e85-dirty on 2019-06-23; ESP32 module with ESP32
Type "help()" for more information.
>>> from machine import WDT 
>>> wdt=WDT(20000)                  #沒指定 timeout 參數會錯誤
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError:
>>> wdt=WDT(timeout=20000)  #須指定 timeout 參數
>>> wdt.feed()   
>>> wdt.status()              #傳回 0表示 WDT 計時器運作中
0
>>> wdt.status() 
0
>>> wdt.feed()   
>>> E (193719) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (193719) task_wdt:  - mp_task (CPU 1)
E (193719) task_wdt: Tasks currently running:
E (193719) task_wdt: CPU 0: IDLE0
E (193719) task_wdt: CPU 1: IDLE1
E (193719) task_wdt: Aborting.
abort() was called at PC 0x40108c18 on core 0

ELF file SHA256: 0000000000000000000000000000000000000000000000000000000000000000

Backtrace: 0x4008fa47:0x3ffbe670 0x4008fd65:0x3ffbe690 0x40108c18:0x3ffbe6b0 0x400846a1:0x3ffbe6d0 0x401e646b:0x3ffbbc70 0x40109b0b:0x3ffbbc90 0x40097025:0x3ffbbcb0 0x400958e1:0x3ffbbce0

Rebooting...
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:4880
ho 0 tail 12 room 4
load:0x40078000,len:9404
load:0x40080400,len:6228
entry 0x400806ec
I (652) cpu_start: Pro cpu up.
I (652) cpu_start: Application information:
I (652) cpu_start: Compile time:     Jun 22 2019 23:54:14
I (656) cpu_start: ELF file SHA256:  0000000000000000...
I (662) cpu_start: ESP-IDF:          v3.3-beta1-694-g6b3da6b18
I (668) cpu_start: Starting app cpu, entry point is 0x40082de8
I (659) cpu_start: App cpu up.
I (679) heap_init: Initializing. RAM available for dynamic allocation:
I (686) heap_init: At 3FFAFF10 len 000000F0 (0 KiB): DRAM
I (692) heap_init: At 3FFB6388 len 00001C78 (7 KiB): DRAM
I (698) heap_init: At 3FFB9A20 len 00004108 (16 KiB): DRAM
I (704) heap_init: At 3FFBDB5C len 00000004 (0 KiB): DRAM
I (710) heap_init: At 3FFCFDD0 len 00010230 (64 KiB): DRAM
I (716) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (722) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (729) heap_init: At 40098C08 len 000073F8 (28 KiB): IRAM
I (735) cpu_start: Pro cpu start user code
I (82) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (279) phy: phy_version: 4100, 2a5dd04, Jan 23 2019, 21:00:07, 0, 0
Press "A" to enter the smartconfig mode while the led is rolling
Started wifi in normal mode
MicroPython v1.10-223-g43faa7e85-dirty on 2019-06-23; ESP32 module with ESP32
Type "help()" for more information.

可見只要沒有持續在設定的 timeout 時間內呼叫 feed() 方法就會觸發系統重開機, 因此 feed() 必須放在事件迴圈中以確保在 timeout 期限內都會被呼叫至少一次.

其次要注意的是, WDT 的 timeout 參數最小值是 1000 (即 1 秒), 低於 1000 設不進去, 會顯示 "WDT timeout too short" 錯誤, 例如 :

>>> wdt=WDT(timeout=999) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: WDT timeout too short   
>>> wdt=WDT(timeout=1000) 
>>> E (12804139) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (12804139) task_wdt:  - mp_task (CPU 1)
E (12804139) task_wdt: Tasks currently running:
...... (reset)

參考 :

MicroPython ESP32教程
玩 Micro python ?

沒有留言 :