Сторожевой таймер (watchdog)
В данном уроке рассматривается устройство и работа со сторожевым таймером, для управления которым предлагается использовать библиотеку GyverWDT (by N1gra), предоставляющую полный контроль за WDT и доступ ко всем его настройкам и режимам работы. Библиотека очень лёгкая и подходит для большинства МК AVR, т.е. в частности для всех плат Arduino на их базе. Сторожевой таймер в МК AVR представляет собой независимый асинхронный блок, тактирующийся от отдельного внутреннего RC генератора, частота которого составляет около 128 кГц (несколько зависит от температуры и напряжения питания). Следует учесть, что изначально данный генератор спроектирован как энергоэффективный источник тактирования, и не предполагает высокой точности, отклонение частоты при производстве составляет около 10%. При желании этот генератор может тактировать и ядро AVR, в случае, если внешний кварцевый резонатор не предусмотрен, а внутренний RC генератор на 8МГц не удовлетворяет требованиям энергопотребления. Сторожевой таймер представляет из себя простой 10-битный счетный регистр, он инкрементируется при каждом фронте тактового сигнала, приходящего из делителя. Как только значение в этом счетном регистре достигнет 1023 - произойдет тайм-аут, а сторожевой таймер, в зависимости от установленного режима, инициирует сброс МК или вызывает прерывание. Чтобы предотвратить тайм-аут, можно сбросить счет сторожевого таймера в 0, воспользовавшись короткой функцией Watchdog.reset() библиотеки GyverWDT, или ассемблерной инструкцией “WDR”. Зачем нужен сторожевой таймер? Он позволяет автоматически перезагрузить контроллер в случае зависания программы, что может быть просто необходимо в автономных проектах. Помимо этого, библиотека GyverWDT предоставляет возможность использовать сторожевой таймер и в других целях, например для генерации прерываний с высоким приоритетом без использования основных аппаратных таймеров, что может очень пригодиться в сложных проектах, например для регулярного опроса датчиков. Не стоит забывать, что сторожевой таймер остается активным во всех, даже самых глубоких режимах сна, и может разбудить контроллер прерыванием. Полезным может оказаться и комбинированный режим сторожевого таймера, в котором он может одновременно опрашивать датчики, выступать сторожевым таймером для внешнего устройства, и следить за работой собственного ядра, чтобы в случае зависания сбросить его. Делители частоты сторожевого таймера
Константа делителя GyverWDT | Приблизительный тайм-аут | Приблизительная частота тайм-аутов |
WDT_PRESCALER_2 |
~ 16ms | ~ 62Hz |
WDT_PRESCALER_4 |
~ 32ms | ~ 32Hz |
WDT_PRESCALER_8 |
~ 64ms | ~ 16Hz |
WDT_PRESCALER_16 |
~ 0.125s | ~ 8Hz |
WDT_PRESCALER_32 |
~ 0.25s | ~ 4Hz |
WDT_PRESCALER_64 |
~ 0.5s | ~ 2Hz |
WDT_PRESCALER_128 |
~ 1s | ~ 1Hz |
WDT_PRESCALER_256 |
~ 2s | ~ 0.5Hz |
WDT_PRESCALER_512 |
~ 4s | ~ 0.25Hz |
WDT_PRESCALER_1024 |
~ 8s | ~ 0.125Hz |
Почему приблизительная? Потому что частота генератора сторожевого таймера уже при производстве имеет приличный разброс, и если на малых значениях делителя это практически не проявляется (16 мс +/- 1.6 мс), то на больших это может стать серьезной проблемой (8 с +/- 1 с). Не стоит забывать, что RC генератор, в отличии от кварцевого резонатора, имеет небольшую зависимость частоты от температуры и напряжения питания.
Библиотека GyverWDT
Для работы с Watchdog можно использовать встроенную avr библиотеку, но у нас есть своя библиотека с более широким набором настроек: GyverWDT. Отдельная страница с документацией и ссылка на загрузку здесь. Функции библиотеки GyverWDT:
Watchdog.reset();
короткая и быстрая команда сброса сторожевого таймера в 0, вызывается внутри программы , для подтверждения нормальной работы.Watchdog.disable();
короткая команда остановки работы сторожевого таймера. Вызывается для полного отключения watchdog.Watchdog.enable(mode, prescaler);
функция для конфигурации и запуска таймера (см. пример)
Режим (константа) | Действие при тайм-ауте |
INTERRUPT_MODE |
Прерывание, вызывающее функцию, указанную в watchdog_enable () |
INTERRUPT_RESET_MODE |
Прерывание, вызывающее функцию, указанную в watchdog_enable () , и автоматическая установка сторожевого таймера в режим reset. |
(комбинированный режим) | Соответственно первый тайм-аут инициирует прерывание, второй тайм-аут – инициирует сброс. |
Зачем нужен комбинированный режим? Он позволяет одновременно использовать как прерывания, так и сброс, что может оказаться очень мощным инструментом. Как это работает:
- Если причина зависания кроется в программной ошибке, например, в бесконечном ожидании поступления данных. Первый тайм-аут вызовет прерывание с высоким приоритетом, если мы смогли в него войти и понять, что это случилось – зависание явно носит программный, а не аппаратный характер. В таком случае мы можем попытать исправить программную ошибку прямо внутри прерывания, или предупредить пользователя, что написанный код имеет проблемные участки. Если удалось оперативно решить проблему до наступления следующего тайм-аута, мы можем не отходя от кассы перенастроить сторожевой таймер функцией
Watchdog.enable(…)
, чтобы следующий тайм- аут отправил нас в прерывание-сигнализатор, вместо hard-reset. Если же не удалось оперативно исправить причину зависания – следующий тайм-аут автоматически сбросит контроллер. - Если необходимо использовать одновременно прерывания (например, для опроса датчиков), и защиту от зависания. В пользовательской функции прерывания, помимо необходимых нам действий, опроса датчиков и т.д. просто дописывается функция перенастройки
Watchdog.enable (…)
, если перенастройка не произошла, значит контроллер не вошел в процедуру прерывания, попросту зависнув. Следующий тайм-аут сбросит контроллер. - Если необходимо аппаратно следить следить как за работоспособностью внешнего устройства, так и самого МК. Например, при совместной работе МК и Bluetooth/Wi-Fi /GSM/GPS модуля. Как это реализовать? Watchdog настраивается на необходимый период. Внешнее устройство обязано периодически посылать на МК подтверждение своей работоспособности, это может быть как импульс, периодически создаваемый с помощью GPIO внешнего устройства, инициирующий внешнее прерывание МК, так и посылка, отправляемая внешним устройством по одному из последовательных интерфейсов, и аналогично инициируя прерывания МК. Внутри процедуры прерывания, вызывается короткая функция
Watchdog.reset ();
, сбрасывающая сторожевой таймер. Если внешнее устройство зависло и перестало посылать данные/импульсы, сбросов watchdog не происходит и происходит прерывание, внутри которого мы можем без труда сбросить внешнее устройство, подав на него низкий уровень (необходимо наличии внешней подтяжки к VCC). В этом же прерывании производится перенастройка watchdog. Если МК зависнет – он не перенастроит watchdog вовремя, и следующий тайм-аут инициирует сброс МК.
Примеры из библиотеки
Watchdog и сон
- Настроить МК на один из режимов сна
- Разрешить сон
- Взвести watchdog на определенный тайм-аут в режиме прерываний
- Уйти в сон
- При пробуждении, оказавшись в процедуре прерывания watchdog, запретить сон
- Продолжить выполнение программы
Проблемы
Основная проблема заключается в том, что режим автоматического сброса не работает на некоторых платах Arduino. Вернее работает то он просто отлично, только приводит к бесконечной перезагрузке или boot loop. Почему так происходит? Дело в том, что контроллеры свежих партий, в случае перезагрузки по watchdog автоматически перенастраивают сторожевой таймер на минимальный период, т.е. 16 мс, не отключая его. Чтобы избежать бутлупа необходимо при старте программы выключить watchdog, но как известно, в большинстве плат мы имеем МК с зашитым загрузчиком, время выполнения которого выше, чем 16 мс. Это приводит к тому, что контроллер даже не успевает приступить к выполнению основной программы, как уже улетает в hard reset , происходит это циклически и бесконечно долго. Единственный способ остановить этот процесс – обесточить плату. Во многих платах выпуска 2018+ года зашит более оптимизированный и продуманный загрузчик optiboot, который автоматически выключает watchdog при старте, что предотвращает бутлуп и позволяет нам использовать сторожевой таймер на 100%.
Проверка работоспособности Watchdog
В примерах к библиотеке идёт скетч тестирования поддержки watchdog на вашей плате. Загрузите его в плату и откройте монитор порта. Спустя 10 секунд паузы, установленной для упрощения перепрошивки при boot loop, в монитор порта начнет выводиться время работы программы в секундах. Если при достижении таймаута программа сбросилась и отсчет начался заново – ваша плата (загрузчик) поддерживает watchdog на все 100%. Если после перезагрузки корректный вывод времени в монитор порта прекратился, а светодиод на плате начал быстро и безостановочно мигать – загрузчик на вашей плате не поддерживает перезагрузку watchdog. Загрузите в плату любой другой скетч, чтобы продолжить использование.
Что делать, если загрузчик на моей плате не поддерживает watchdog? Если вам действительно нужен полный функционал сторожевого таймера – необходимо перепрошить загрузчик, для этого необходим программатор, который поддерживает МК AVR, или другая плата Arduino + скетч Arduino ISP. Подробнее читайте в уроке про работу с программатором. Без перепрошивки загрузчика вам доступны обычные прерывания, не использующие таймеров и имеющие высокий приоритет, и соответственно возможность использования watchdog в режиме будильника МК.
Полезные страницы
- Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])