3.27. 看门狗定时器

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

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

一张随时间变化的看门狗计数器图。计数器 从超时值开始,线性下降趋向 零,并在脚本每次调用 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() 放在何处是关键的设计决策;看门狗只能捕获那些在两次喂狗之间运行的代码部分中的 bug。

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

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

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

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

3.27.3. 可用性

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

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