3.27. 看门狗定时器

看门狗定时器是一种硬件,如果正在运行的脚本停止定期触碰它,它就会复位微控制器。脚本从它确信正在运行健康代码的某处"喂"看门狗;如果某个 bug、卡死或意外异常致使摄像头未能在配置的超时时间内喂狗,芯片就会自行复位,脚本随之重新开始。

在一台附近无人能够断电重启的已部署设备上,这就是一个能在数秒内自行恢复的瞬时 bug 与一台需要现场维修才能解决的变砖设备之间的区别。

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() 放在何处是关键的设计决策;看门狗只能捕获那些在两次喂狗之间运行的代码部分中的 bug。

  • 从主循环调用,放在顶部或底部。 最常见的模式。看门狗会捕获任何使主循环卡死的情况——死锁、无限 while、永不返回的外设——并将芯片复位回循环中。

  • 不要从中断处理程序中调用。 看门狗的意义在于捕获正常代码路径中的卡死。无论主循环是否卡住都会触发的 ISR,会持续喂一个本该触发的看门狗。

  • 不要在长时间阻塞操作内部调用。 一个耗时十秒的网络请求或传感器读取,正是看门狗应当捕获的那种卡死。把 feed() 放在其中会使这种保护失效。

对大多数程序适用的一条准则:每次主循环迭代喂一次,并将超时设置为预期循环时长的数倍。如果单次迭代确实需要超过超时的时间——比如一个有意的校准阶段——则将该阶段拆分为一系列较小的块,块与块之间穿插 feed(),或在进入该阶段之前用 timeout_ms()(在受支持的情况下)更改超时。

3.27.3. 可用性

看门狗在大多数 OpenMV 摄像头上都有提供,但并非全部——硬件在每个部件上都存在,但 Python API 尚未在所有地方接通。请查看 OpenMV 开发板,或尝试构造一个 WDT 并在不受支持时捕获 AttributeError

即使在未提供 WDT 的摄像头上,现场部署的设备也可以使用软件等效方案——一个独立的任务或主循环步骤,用于监控进度并在出现异常卡滞时触发 machine.reset()。它不如硬件看门狗稳健(一个卡住的中断处理程序也可能拖垮软件监控器),但它在应用层面覆盖了相同的情况。