Урок 16. Повышение надежности программ для Ардуино. Сторожевой таймер.

Arduino UNO R3

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

Предыдущий урок     Список уроков     Следующий урок

Как и любое электронное устройство на микроконтроллерах, платы Ардуино могут зависать.  Причины могут быть разные:

  • неисправность контроллера;
  • не нормальная система питания контроллера, броски питающего напряжения, помехи через цепи питания;
  • наводки, электромагнитные помехи на плату и компоненты контроллера;
  • импульсные помехи через общие цепи внешних компонентов системы (компьютер, другие контроллеры, длинные линии связи и т.п.);
  • паразитные связи на плате, особенно в тяжелых условиях эксплуатации (высокая влажность, температура, вибрации);
  • неправильные (скорее не предусмотренные программистом) действия пользователя или реакция внешних компонентов;
  • ошибки в программе.

 

Конечно, это не нормальное явление. Допускать этого нельзя, но что-то не предусмотрели, что-то случилось не зависящее от нас. К тому же мы программируем на языке высокого уровня, а это означает, что есть программная среда, которую мы не контролируем. Компилятор создает какие-то стеки, области собственных данных, и мы не знаем, насколько надежно они реализованы.

Поэтому необходимо контролировать ход выполнения программы и принимать меры, если она работает не нормально.

Сторожевой таймер (watchdog) в системе Ардуино.

Одним из способов повышения надежности является использование сторожевого таймера (watchdog) для контроля выполнения программы. Он представляет собой аппаратный таймер, который должен периодически сбрасываться программой. Если сброс  сторожевого таймера не произойдет в течение заданного времени, то он вызовет перезагрузку всей системы, т.е. выработает сигнал сброс микроконтроллера. Таким образом, если программа зависнет и перестанет сбрасывать сторожевой таймер, то микроконтроллер будет перезагружен, как будто нажали кнопку сброс.

В системах Ардуино есть сторожевой таймер, который является внутренним узлом микроконтроллера ATmega.

 

Библиотека для работы со сторожевым таймером Ардуино.

Для управления сторожевым таймером необходимо подключить к проекту библиотеку avr/wdt.h.

Эту библиотеку не надо искать в интернете, скачивать. Ее не надо устанавливать. Это стандартная библиотека, она находится в  каталоге Arduino. У меня в D:\Arduino\hardware\tools\avr\avr\include\avr\wdt.h

Просто добавьте в проект строку:

#include <avr/wdt.h>

Библиотека имеет три функции.

void wdt_enable(timeout)

Функция разрешает работу сторожевого таймера, задает время тайм-аута. Аргумент timeout (время тайм-аута) может принимать следующие значения.

WDTO_15MS      // 15 мс
WDTO_30MS      // 30 мс
WDTO_60MS      // 60 мс
WDTO_120MS    // 120 мс
WDTO_250MS    // 250 мс
WDTO_500MS    // 500 мс
WDTO_1S            // 1 сек
WDTO_2S            // 2 сек
WDTO_4S           // 4 сек
WDTO_8S          // 8 сек

Пример:

wdt_enable(WDTO_120MS);  // разрешение работы сторожевого таймера с тайм-аутом 120 мс

void wdt_reset(void)

Сброс сторожевого таймера. Для нормальной работы необходимо вызывать эту функцию не реже периода сторожевого таймера. При задержке превышающей тайм-аут произойдет аппаратный сброс контроллера.

wdt_reset();  // сброс сторожевого таймера

void wdt_disable(void)

Отключение сторожевого таймера.

wdt_disable();  // запрет работы сторожевого таймера

 

Применение сторожевого таймера в системе Ардуино.

Сторожевой таймер позволяет контролировать выполнение отдельных циклов программы. Функцию его сброса не надо ставить, где попало.  Я обычно контролирую цикл прерывания по таймеру, от которого отсчитываются остальные циклы программы. В предыдущих уроках я поставил бы сброс watchdog в цикле обработки прерывания по таймеру 2 мс.

Давайте проверим работу сторожевого таймера на реальной программе. В программе:

  • Организован цикл прерывания по таймеру 2мс.
  • В основном асинхронном цикле реализовано управление светодиодом платы (мигает с периодом 1 сек).
  • В основном цикле проверяются данные с последовательного порта, и при появлении любого данного запрещается работа прерывания по таймеру.  Этим имитируется сбой установок таймера.

// проверка работы сторожевого таймера
#include <MsTimer2.h>
#include <avr/wdt.h>

#define LED_PIN 13 // светодиод подключен к выводу 13
int ledCount;             // счетчик времени мигания светодиода

void setup() {
  pinMode(LED_PIN, OUTPUT);      // определяем вывод светодиода как выход
  Serial.begin(9600);     // инициализируем последовательный порт
  MsTimer2::set(2, timerInterupt); // задаем период прерывания от таймера 2 мс
  MsTimer2::start();              // разрешаем прерывание от таймеру
  // wdt_enable(WDTO_15MS); // разрешение работу сторожевого таймера с тайм-аутом 15 мс 
}

void loop() {
  // мигание светодиода
  if ( ledCount > 250 ) {
     ledCount= 0; 
     digitalWrite(LED_PIN, ! digitalRead(LED_PIN));  // инверсия состояния светодиода
  }   

  // проверка данных в буфере последовательного порта (имитация сбоя)
  if ( Serial.available() != 0 ) MsTimer2::stop(); // запрет прерывания от таймера   
}

// обработчик прерывания
void  timerInterupt() {
  ledCount++; // счетчик светодиода
  // wdt_reset();  // сброс сторожевого таймера 
}

Загрузим программу в плату. Светодиод мигает раз в секунду. Откроем монитор порта и пошлем какой-нибудь символ. Светодиод перестанет мигать. Мы имитировали сбой установок таймера и программа зависла. Если нажать кнопку сброс на плате, то программа снова начнет работать, светодиод замигает.

Теперь освободим от комментариев две строчки разрешения и сброса сторожевого таймера.

wdt_enable(WDTO_15MS); // разрешение работы сторожевого таймера с тайм-аутом 15 мс  
wdt_reset();  // сброс сторожевого таймера

Загрузим программу в плату, запустим. Теперь при имитации сбоя сторожевой таймер сбрасывает микроконтроллер, и программа продолжает работать.

 

Способы повышения надежности работы программы.

Сторожевой таймер не панацея от неправильной работы программы. Представьте, что в предыдущем примере установки таймера прерывания собьются так, что он будет работать с периодом не 2 мс, а 5 мс. И как тут поможет сторожевой таймер? Если собьются параметры последовательного порта? Программа не будет выполнять своих функций.

Только комплекс мер позволит написать надежную программу. Я разрабатываю программы для PIC-контроллеров фирмы Microchip. Программы работают в устройствах, зависание или неправильная работа контроллеров в которых приведет к фатальным последствиям. Это мощные специализированные источники питания, станции катодной защиты, системы контроля технологических процессов, GSM телеметрия и т.п. Надежность программ это очень обширная, сложная тема. Я коротко расскажу об основных принципах создания надежных программ.

Что может случиться с программой? После чего она перестает правильно работать?

  • Могут испортиться данные (переменные) в оперативной памяти.
  • Возможно ошибочное изменение состояния регистров микроконтроллера.
  • Программа может бесконечно ожидать какого-то события, которое не наступает, а разработчик полагал, что оно будет обязательно.

 

Контроль данных, переменных, регистров микроконтроллера.

Переменные, которые используются для промежуточных результатов вычислений мы контролировать не в состоянии. Откуда мы знаем, что в них должно быть в данный момент. А переменные, в которых хранятся параметры, режимы, технологические установки и т.п. вполне можно контролировать. А можно циклически переустанавливать.

В моих программах на PIC-контроллерах всегда есть программный блок с названием ”циклические установки”. В нем с определенным периодом переустанавливаются все переменные и регистры микроконтроллера. Конечно те, которые не меняются в цикле.

Например, в программе происходит циклическое прерывание по таймеру с определенным временем периода. В циклических установках переустанавливаются все режимы таймера и контроллера прерываний. При таком подходе время периода прерывания по таймеру не может измениться, сбиться, испортиться.  Хотя, надо признать, что при разработке программ на языке высокого уровня все переменные и регистры переустанавливать не просто.

Большие блоки данных я защищаю контрольными суммами и периодически проверяю. О контроле целостности данных написано в уроке 14.

В случае ошибочных данных лучше перезагрузить всю программу, сформировав программный сброс. Неизвестно, что в ней еще испортилось.

Но лучше использовать аппаратный сброс, чтобы переустановить все регистры микроконтроллера. Сделать это можно, используя сторожевой таймер:

wdt_enable(WDTO_15MS);
while (1) { }

Контроль хода выполнения программы.

Одной из распространенной ошибкой, приводящей к зависанию программы, является ожидание события в бесконечном цикле.

Допустим, Вы принимаете данные с компьютера по последовательному порту. Ждете 10 байтов, а пришло 9. И программа бесконечно ждет 10го байта. Я видел много программ, которые зависали при нарушении приема данных по последовательному интерфейсу.

В подобных случаях необходимо контролировать время выполнения операции. Если байт не пришел в течение 1 сек, то он не придет никогда. Надо считать время ожидания события и при отсутствии его принимать меры. У каждого ожидаемого события должен быть свой тайм-аут – время ожидания. А использовать для этого сторожевой таймер или программные счетчики – решать программисту.

 

В следующем уроке разработаем на базе платы Ардуино полностью рабочее устройство – охранную сигнализацию. Знаний для этого теперь достаточно.

Предыдущий урок     Список уроков     Следующий урок

3 комментария на «Урок 16. Повышение надежности программ для Ардуино. Сторожевой таймер.»

  1. Добрый день.
    А не могли бы вы отдельно объяснить по watchdog в части bootloop (crazy led) и перепрошивки платы optiboot — основы, почему так происходит, как проверить поддерживает ли плата watchdog, как перепрошить и т.д.?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *