Сторожевой таймер (WDT) — отдельный счётчик со своим генератором, который сбрасывает микроконтроллер или вызывает прерывание, если программа вовремя не выполнила команду WDR (wdt_reset()). У AVR он работает даже при остановленном основном генераторе и годится как для защиты от зависаний, так и для периодического пробуждения из сна.
Время срабатывания
У ATmega328P, ATmega2560, ATmega32U4 и ATtiny85 сторож считает такты собственного генератора 128 кГц, у старого ATmega8 — генератора 1 МГц. Предделитель WDP выбирает число тактов до срабатывания:
| WDP | Тактов (128 кГц) | Типично при 5 В | Константа avr-libc |
|---|---|---|---|
| 0000 | 2K | 16 мс | WDTO_15MS |
| 0001 | 4K | 32 мс | WDTO_30MS |
| 0010 | 8K | 64 мс | WDTO_60MS |
| 0011 | 16K | 0,125 с | WDTO_120MS |
| 0100 | 32K | 0,25 с | WDTO_250MS |
| 0101 | 64K | 0,5 с | WDTO_500MS |
| 0110 | 128K | 1,0 с | WDTO_1S |
| 0111 | 256K | 2,0 с | WDTO_2S |
| 1000 | 512K | 4,0 с | WDTO_4S |
| 1001 | 1024K | 8,0 с | WDTO_8S |
Таблица одинакова у всех четырёх микроконтроллеров (ATmega328P — таблица 11-2, ATmega2560 — 12-2, ATmega32U4 — 8-5, ATtiny85 — 8-3). Даташиты округляют времена: 1024K тактов при ровно 128 кГц — это 8,19 с. У ATmega8 тактов вдвое и больше, а генератор быстрее: от 16K тактов (16,3 мс при 5 В) до 2048K (2,1 с), и при 3 В времена примерно на 5 % длиннее (таблица 17).
Генератор сторожа экономичный, но неточный: ATmega328P нормирует его частоту 128 кГц только при 3 В и 25 °C и прямо пишет, что он не рассчитан на высокую точность (раздел 9.7). Поэтому сторож сбрасывают с большим запасом, а если время важно — измеряют частоту генератора на своём экземпляре и вводят её в калькулятор.
Режимы и настройка
| WDTON | WDE | WDIE | Режим |
|---|---|---|---|
| не запрограммирован | 0 | 0 | выключен |
| не запрограммирован | 0 | 1 | прерывание |
| не запрограммирован | 1 | 0 | сброс |
| не запрограммирован | 1 | 1 | прерывание, затем сброс |
| запрограммирован | x | x | сброс, отключить нельзя |
Таблица — по ATmega328P (таблица 11-1). В режиме «прерывание, затем сброс» первое срабатывание вызывает прерывание и аппаратно сбрасывает WDIE; если до следующего срабатывания программа не установит WDIE снова, произойдёт сброс. Так перед перезагрузкой можно сохранить важные данные. Фьюз WDTON включает сторож навсегда в режиме сброса — его состояние показывает калькулятор фьюзов AVR.
Изменить предделитель или выключить сторож можно только последовательностью из двух записей: сначала одновременно WDCE = 1 и WDE = 1, затем в течение четырёх тактов — новое значение регистра с WDCE = 0 (ATmega328P, раздел 11.8). Прерывания на это время запрещают. Перед выключением сторожа нужно сбросить флаг WDRF в MCUSR: пока он установлен, WDE нельзя сбросить. Функция wdt_enable() из avr-libc выполняет эту последовательность сама.
Сторожевые таймеры STM32 устроены иначе — независимый IWDG и оконный WWDG; их считает калькулятор сторожевых таймеров STM32.
Частые вопросы
Почему после срабатывания сторожа Arduino бесконечно перезагружается?
У ATmega328P и других AVR с прерыванием от сторожа таймер после сброса остаётся включённым с самым коротким временем, около 16 мс (avr-libc, avr/wdt.h). Если программа или загрузчик не успевают его выключить, микроконтроллер сбрасывается снова и снова. Даташит требует в начале программы сбрасывать флаг WDRF в MCUSR и бит WDE — даже если сторож не используется.
Какое наибольшее время сторожевого таймера ATmega328P?
1024K тактов генератора 128 кГц — около 8 с (WDP3:0 = 1001, константа WDTO_8S). У ATmega8 наибольшее время — 2048K тактов генератора 1 МГц, около 2,1 с при 5 В.
Можно ли разбудить AVR сторожевым таймером?
Да, в режиме прерывания (WDIE = 1, WDE = 0) сторож вызывает прерывание WDT_vect, которое выводит микроконтроллер из сна, в том числе из самого экономного режима Power-down (ATmega328P, раздел 10.5). Так делают периодическое пробуждение батарейных устройств с потреблением в микроамперы. У ATmega8 прерывания от сторожа нет — только сброс.
Источники
- Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — 9.7 — генератор 128 кГц номинален при 3 В и 25 °C и не рассчитан на точность; 11.8 — режимы, WDTON, последовательность WDCE, сброс WDRF при инициализации; 10.5 — прерывание сторожа будит из Power-down; таблица 11-1 — режимы; таблица 11-2 — 2K…1024K тактов, 16 мс…8 с при 5 В (эталон тестов)
- Microchip (Atmel). ATmega640/1280/1281/2560/2561 datasheet (2549Q) — таблица 12-2 — те же 2K…1024K тактов и времена (второй источник тестов)
- Microchip (Atmel). ATmega16U4/32U4 datasheet (Atmel-7766J) и ATtiny25/45/85 datasheet (2586Q) — ATmega32U4: таблица 8-5; ATtiny85: таблица 8-3 и регистр WDTCR — в строке 32K напечатано 32764 вместо 32768
- Microchip (Atmel). ATmega8(L) datasheet (2486AA) — генератор 1 МГц, таблица 17 — 16K…2048K тактов, 16,3 мс…2,1 с при 5 В и 17,1 мс…2,2 с при 3 В; только сброс
- avr-libc. avr/wdt.h (репозиторий avrdudes/avr-libc) — wdt_enable(), wdt_reset(), константы WDTO_15MS…WDTO_8S (4S и 8S — при наличии WDP3); сторож остаётся включённым после сброса, кроме сброса по питанию
Где пригодится
Встроить калькулятор на свой сайт
Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах
<iframe src="https://gaw.ru/embed/avr-watchdog/" title="Сторожевой таймер AVR: время срабатывания и настройка" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/avr-watchdog/">Сторожевой таймер AVR: время срабатывания и настройка</a> — GAW.ru</p>Автоподстройка высоты рамки
Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.
<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>Обновлено .
Нашли ошибку в расчёте?