3.27. Сторожевой таймер¶
Сторожевой таймер – это оборудование, которое сбрасывает микроконтроллер, если работающий скрипт когда-либо перестаёт периодически его «подкармливать». Скрипт «кормит» сторожевой таймер из места, о котором он знает, что там выполняется исправный код; если ошибка, зависание или непредвиденное исключение когда-либо не дадут камере покормить сторожевой таймер в течение заданного тайм-аута, микросхема сбрасывает себя, и скрипт начинается заново.
На развёрнутом устройстве, рядом с которым нет человека, чтобы перезапустить его по питанию, это разница между кратковременной ошибкой, которая восстанавливается за секунды, и кирпичом, требующим выезда сервиса.
Счётчик сторожевого таймера отсчитывает вниз от своего тайм-аута. Каждый 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(), если что-то выглядит заклинившим. Он менее надёжен, чем аппаратный сторожевой таймер (заклинивший обработчик прерывания может уронить и программный монитор), но он покрывает те же случаи на уровне приложения.