3.27. 看門狗計時器

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

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

A graph of a watchdog counter over time. The counter starts at the timeout value, drops linearly toward zero, and is reloaded to the timeout each time the script calls feed(). After three successful feed() calls, a fourth interval has no feed() and the counter reaches zero, triggering an MCU reset.

看門狗計數器從其逾時值往下遞減。每次 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()。它不如硬體看門狗來得穩固(卡住的中斷處理常式也可能拖垮軟體監控),但它能在應用程式層級涵蓋相同的情況。