RSS
people

Tips and Tricks: использование Watchdog таймера

Поделюсь с вами небольшим, но довольно интересным способом работы с Watchdog таймером микроконтроллера.

Всем нам известно — что Watchdog — таймер используется для предотвращения зависания вашей программы в самых неожиданных местах. И, если этот таймер вовремя не сбросить- то произойдет сброс микроконтроллера, в народе называемый ресет.

И по этой причине обычно программисты раскидывают по всей программе и во всех циклах команду сброса таймера wdt_reset (), что не только портит вид всего текста программы но и мешает ее чтению, если вы копаетесь в чужом коде, а так же — самое главное — это лишний десяток байт, который тратится только на простой сброс. Ну и если что-то новое добавляете, то опять же надо постараться не забыть вставить вызов этой функции. В Avr Gcc вызов этой функции заменяется всего одной командой на ASMе, но всей некрасоты этого метода оно не отменяет.

Что же делать?

Мой коллега с первой моей работы подсказал мне довольно элегантное решение этой проблемы, коим я до сих пор и пользуюсь. А именно — сбрасывать watchdog — таймер в обычном хардверном таймере. Да, вы не ослышались, — в обычном таймере, но с небольшой обвязкой. Потому как просто поставив команду сброса в функцию прерывания таймера равносильно самоубийству.

И так — обвязка.

Для начала нам надо объявить одну глобальную переменную размером в байт или бит (в зависимости от архитектуры вашего микроконтроллера).  И еще одну прееменную размером побольше. Скажем

  1. u08 SWdtReset;
  2. u32 SWdtTimer;

Затем в функцию прерывания таймера вставляем такой вот кусок кода:

  1.  if (SWdtReset)
  2.  {
  3.    SWdtReset = 0;
  4.    SWdtTimer = SWDT_VALUE;
  5.  }
  6.  if (SWdtTimer)
  7.  {
  8.   SWdtTimer--;
  9.   wdt_reset();
  10.  }

Значение макроса SWDT_VALUE вычисляем на практике, равное примерному количеству срабатываний нашего таймера в течение выполнения основного цикла программы, с учетом основных прерываний.

Теперь осталось дело за малым — в основном цикле нашей программы (я обычно ставлю в самое начало его)  мы ставим следующую строку:

  1. SWdtReset = 1;

И, собственно, все!

Теперь разберем все по порядку.

В функции прерывания таймера мы опрашиваем переменную SWdtReset на предмет наличия в ней какого либо значения, отличного от 0. Если таковое есть, то это значит, что кто-то его туда записал, т.е. жизнь еще теплится в жилах нашего микроконтроллера, и можно спокойно записать в переменную SWdtTimer наше SWDT_VALUE — количество циклов таймера на один проход основного цикла программы, и надо не забыть обнулить переменную SWdtReset. Это обязательно!

Далее мы опрашиваем переменную SWdtTimer на предмет наличия в ней ненулевого значения, и, если такое имеется, то мы сбразываем наш watchdog-таймер, и одновременно уменьшаем значение переменной  SWdtTimer на 1-цу. Это тоже обязательно!

Таким образом, если никто не будет записывать в переменную SWdtReset значение, отличное от 0, то в скором времени мы перестанем сбрасывать watchdog — таймер, и он перезапустит наш микроконтроллер.

Т.е. этим простым трюком мы мониторим сразу все: — основной цикл программы, — и все прерывания, ведь, зависни одно из прерываний — то watchdog — таймер, просто напросто, не сбросится, а если основной цикл зависнет, то watchdog — таймер будет сбрасываться только определенное количество раз и затем перестанет это делать. Что, собственно, нам и надо было получить в результате.

И этот метод нам дает одно преимущество — а именно — чистоту кода — у нас  нет больше раскиданных там и туд команд wdt_reset (), — один раз настроив этот механизм, мы просто забываем о нем.

Есть и небольшой недостаток — если в программе есть циклы, которые требуют много времени — например — чтение сектора флеш-карты, то надо в этот цикл тоже вставить строку

  1. SWdtReset = 1;

Или можно скорректировать значение  SWDT_VALUE таким образом, что бы процесс чтения укладывался в отведенное время.

Приятного кодинга вам сегодня!

2 комментария к “Tips and Tricks: использование Watchdog таймера”

  1. di-halt.livejournal.com/ пишет:

    А смысл? Растянуть выдержку вачдога? Так ее же и так можно увеличить. И также будет в одном месте главного цикла одна строка. Получается меняем WDR на LDI. Или я что то недоглядел?

  2. MasterAlexei пишет:

    Смысл именно в том, чтобы растянуть выдержку и уменьшить раскиданные по всей программе команды сброса вачдога. Например если в проге много мелких циклов — то, при обычном использовании вачдога, в каждый их них надо ставить команду сброса, а так — и не надо вовсе. А сам вачдог аппаратно вроде как не сильно то и увеличивается. Максимум до 1.9 секунды. А так — и на 10 можно поставить. Ну и один из плюсов — один раз сделал и забыл.

Оставить комментарий или два

Пожалуйста, зарегистрируйтесь для комментирования.