Поделюсь с вами небольшим, но довольно интересным способом работы с Watchdog таймером микроконтроллера.
Всем нам известно — что Watchdog — таймер используется для предотвращения зависания вашей программы в самых неожиданных местах. И, если этот таймер вовремя не сбросить- то произойдет сброс микроконтроллера, в народе называемый ресет.
И по этой причине обычно программисты раскидывают по всей программе и во всех циклах команду сброса таймера wdt_reset (), что не только портит вид всего текста программы но и мешает ее чтению, если вы копаетесь в чужом коде, а так же — самое главное — это лишний десяток байт, который тратится только на простой сброс. Ну и если что-то новое добавляете, то опять же надо постараться не забыть вставить вызов этой функции. В Avr Gcc вызов этой функции заменяется всего одной командой на ASMе, но всей некрасоты этого метода оно не отменяет.
Что же делать?
Мой коллега с первой моей работы подсказал мне довольно элегантное решение этой проблемы, коим я до сих пор и пользуюсь. А именно — сбрасывать watchdog — таймер в обычном хардверном таймере. Да, вы не ослышались, — в обычном таймере, но с небольшой обвязкой. Потому как просто поставив команду сброса в функцию прерывания таймера равносильно самоубийству.
И так — обвязка.
Для начала нам надо объявить одну глобальную переменную размером в байт или бит (в зависимости от архитектуры вашего микроконтроллера). И еще одну прееменную размером побольше. Скажем
- u08 SWdtReset;
- u32 SWdtTimer;
Затем в функцию прерывания таймера вставляем такой вот кусок кода:
- if (SWdtReset)
- {
- SWdtReset = 0;
- SWdtTimer = SWDT_VALUE;
- }
- if (SWdtTimer)
- {
- SWdtTimer--;
- wdt_reset();
- }
Значение макроса SWDT_VALUE вычисляем на практике, равное примерному количеству срабатываний нашего таймера в течение выполнения основного цикла программы, с учетом основных прерываний.
Теперь осталось дело за малым — в основном цикле нашей программы (я обычно ставлю в самое начало его) мы ставим следующую строку:
- SWdtReset = 1;
И, собственно, все!
Теперь разберем все по порядку.
В функции прерывания таймера мы опрашиваем переменную SWdtReset на предмет наличия в ней какого либо значения, отличного от 0. Если таковое есть, то это значит, что кто-то его туда записал, т.е. жизнь еще теплится в жилах нашего микроконтроллера, и можно спокойно записать в переменную SWdtTimer наше SWDT_VALUE — количество циклов таймера на один проход основного цикла программы, и надо не забыть обнулить переменную SWdtReset. Это обязательно!
Далее мы опрашиваем переменную SWdtTimer на предмет наличия в ней ненулевого значения, и, если такое имеется, то мы сбразываем наш watchdog-таймер, и одновременно уменьшаем значение переменной SWdtTimer на 1-цу. Это тоже обязательно!
Таким образом, если никто не будет записывать в переменную SWdtReset значение, отличное от 0, то в скором времени мы перестанем сбрасывать watchdog — таймер, и он перезапустит наш микроконтроллер.
Т.е. этим простым трюком мы мониторим сразу все: — основной цикл программы, — и все прерывания, ведь, зависни одно из прерываний — то watchdog — таймер, просто напросто, не сбросится, а если основной цикл зависнет, то watchdog — таймер будет сбрасываться только определенное количество раз и затем перестанет это делать. Что, собственно, нам и надо было получить в результате.
И этот метод нам дает одно преимущество — а именно — чистоту кода — у нас нет больше раскиданных там и туд команд wdt_reset (), — один раз настроив этот механизм, мы просто забываем о нем.
Есть и небольшой недостаток — если в программе есть циклы, которые требуют много времени — например — чтение сектора флеш-карты, то надо в этот цикл тоже вставить строку
- SWdtReset = 1;
Или можно скорректировать значение SWDT_VALUE таким образом, что бы процесс чтения укладывался в отведенное время.
Приятного кодинга вам сегодня!
2 комментария к “Tips and Tricks: использование Watchdog таймера”
Оставить комментарий или два
Пожалуйста, зарегистрируйтесь для комментирования.
18th February 2009 в 1:47
А смысл? Растянуть выдержку вачдога? Так ее же и так можно увеличить. И также будет в одном месте главного цикла одна строка. Получается меняем WDR на LDI. Или я что то недоглядел?
18th February 2009 в 10:23
Смысл именно в том, чтобы растянуть выдержку и уменьшить раскиданные по всей программе команды сброса вачдога. Например если в проге много мелких циклов — то, при обычном использовании вачдога, в каждый их них надо ставить команду сброса, а так — и не надо вовсе. А сам вачдог аппаратно вроде как не сильно то и увеличивается. Максимум до 1.9 секунды. А так — и на 10 можно поставить. Ну и один из плюсов — один раз сделал и забыл.