3.27. 看門狗計時器

看門狗計時器是一種硬體,當執行中的指令碼停止定期「戳」它時,便會重置微控制器。指令碼會從它確知正在執行健康程式碼的地方「餵食」看門狗;如果有任何錯誤、當機或意外的例外狀況,導致相機未能在設定的逾時時間內餵食看門狗,晶片就會自行重置,指令碼則重新開始。

對於身邊沒有人能進行電源循環的已部署裝置而言,這正是「在數秒內自行恢復的暫時性錯誤」與「需要派人到場處理的當機磚塊」之間的差別。

一張看門狗計數器隨時間變化的圖。計數器 從逾時值開始,線性下降趨向 零,並在指令碼每次呼叫 feed() 時重新載入 為逾時值。在三次成功的 feed() 呼叫之後,第四個區間沒有 feed(),計數器 歸零,觸發 MCU 重置。

看門狗計數器從其逾時值往下遞減。每次 feed() 都會將它重新載入;若它歸零,晶片便會重置。

3.27.1. machine.WDT 類別

machine.WDT 會啟用看門狗,並提供單一方法 feed()。一旦啟動,看門狗便無法停止 -- 唯一的出路就是按時餵食它,或讓它重置晶片:

from machine import WDT

wdt = WDT(timeout=2000)    # reset if not fed within 2 seconds

while True:
    do_work()
    wdt.feed()

timeout 以毫秒為單位。正確的值取決於主迴圈最長一次合理迭代需要多久,再加上充裕的餘裕 -- 一個 100 毫秒的迴圈搭配 2 秒的逾時,對於一次較慢的迭代有充足的餘量,不會發生干擾性的重置。

3.27.2. 在何處呼叫 feed()

feed() 放在何處是關鍵的設計決策;看門狗只能捕捉到那些在兩次餵食之間不會執行的程式碼中的錯誤。

  • 從主迴圈呼叫,放在開頭或結尾。 這是最常見的模式。看門狗會捕捉任何讓主迴圈當住的情況 -- 死結、無窮 while、永不回傳的周邊裝置 -- 並將晶片重置回到迴圈中。

  • 不要從中斷處理常式呼叫。 看門狗的重點在於捕捉正常程式碼路徑中的當機。一個無論主迴圈是否卡住都會觸發的 ISR,會持續餵食一個本應被觸發的看門狗。

  • 不要在長時間的阻塞操作內部呼叫。 一個耗時十秒的網路請求或感測器讀取,正是看門狗應該捕捉的那種當機。把 feed() 放在它裡面會破壞這層保護。

適用於大多數程式的準則:每次主迴圈迭代餵食一次,並將逾時設定為預期迴圈時長的數倍。如果單次迭代確實合理地需要超過逾時的時間 -- 例如刻意安排的校正階段 -- 請將該階段拆解為一連串較小的區塊,並在它們之間穿插 feed(),或在進入該階段之前用 timeout_ms()(在支援的情況下)變更逾時。

3.27.3. 可用性

看門狗在大多數 OpenMV 相機上都有提供,但並非全部 -- 硬體在每個零件上都存在,但 Python API 尚未在所有地方接好。請查閱 OpenMV 開發板,或嘗試建構一個 WDT,並在不支援時捕捉 AttributeError

即使在未提供 WDT 的相機上,現場部署的裝置也可以使用軟體等效方案 -- 一個獨立的任務或主迴圈步驟,用來監控進度,並在發現任何卡住跡象時觸發 machine.reset()。它不如硬體看門狗來得穩固(卡住的中斷處理常式也可能拖垮軟體監控),但它能在應用程式層級涵蓋相同的情況。