Настройка WatchDog на микроконтроллере Atmega8 в среде программирования Atmel Studio.

В любом микроконтроллере есть сторожевой таймер, называемым WatchDog.  Предназначение данного таймера сбрасывать контроллер, если он по какой то причине завис.

Алгоритм следующий: включается таймер WatchDog и начинает отсчет времени, в программе должна быть команда сброса данного таймера в ноль, и таймер начинает отчет заново. Если по какой то причине микроконтроллер завис и таймер не сбросился — он срабатывает и сбрасывает контроллер, аналогично команде RESET. Таким образом микроконтроллер начнет выполнят свою программу заново.

Для запуска WatchDog  есть готовая библиотека :

#include <avr/wdt.h>

Данная библиотека имеет следующие настройки и функции:

wdt_enable(time) — настраивает время работы таймера WatchDog.

wdt_reset() — сбрасывает таймер в ноль.

wdt_disable() запрещает работу WatchDog таймера.

Значение time может быть выбрана из списка ниже:

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 сек

Внизу программный код который демонстрирует работу сторожевого таймера. Программа взята с предыдущего урока по работе с UART. Схема подключения микроконтроллера осталась без изменения:

********************************************

В программе специально внесена ошибка в виде бесконечной зацикленности, когда мы отправляем число 3 на микроконтроллер:

if (b==’3′){
    HL1_off();
    HL2_off();
    while(1){;}
}

Если не включить WatchDog, микроконтроллер зависнет на вечно и программа выполнятся не будет. Такая ситуация может произойти по разным причинам, по этому желательно в программном коде, где это возможно (ожидание сигнала, передача или прием данных ) включать WatchDog таймер.
Программный код (как и в прошлом уроке, при отправке цифры 0,1,2 — у нас будут зажигаться светодиоды, а при отправке 3 мы специально сделаем зависание микроконтроллера):

#include <avr/io.h>
#define F_CPU 4000000
#define BAUD 9600L
#define UBRRL_value (F_CPU/(BAUD*16))-1

#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

void init_pin(void);
#define HL1_on() PORTB|=(1<<PB0)
#define HL1_off() PORTB&=~(1<<PB0)
#define HL2_on() PORTB=(1<<PB1)
#define HL2_off() PORTB&=~(1<<PB1)

int b;

ISR(USART_RXC_vect)
{
b = UDR;
}

void init_USART() {
UBRRL = UBRRL_value;
UBRRH = UBRRL_value >> 8;
UCSRB |=(1<<TXEN)|(1<<RXEN);
UCSRC |=(1<< URSEL)|(1<< UCSZ0)|(1<< UCSZ1);
UCSRB |= (1<<RXCIE);
sei();
}

void send_UART(char value) {
while(!( UCSRA & (1 << UDRE)));
UDR = value;
}

int main(void){
init_pin();
init_USART();
send_UART('O');
send_UART('K');
send_UART('!');
wdt_enable(WDTO_2S);// включаем watchdog  на 2 секунды

while(1){
  if(b=='0'){
     HL1_on(); HL2_off();
     wdt_reset();
  }
if(b=='1'){
   HL2_on();HL1_off();
   _delay_ms(500);
  }
if(b=='2'){
   HL2_on();HL1_on();
   _delay_ms(500);
  }
if (b=='3'){
   HL1_off();
   HL2_off();
   while(1){;}
  }
wdt_reset(); // сбрасываем таймер watchdoq
}
}

void init_pin(void){
 PORTB=0b00000000;// PB1
 DDRB= 0b00000011;// PB1 output
}

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

Мы рассмотрели обычный режим использования WatchDog, но теперь рассмотрим немного другой подход к использованию WatchDog. Суть данного подхода заключается в том, что мы помещаем в прерывание по таймеру сброс WatchDog, при этом в прерывании проверяем флаг который мы устанавливаем заранее в цикле выполнения программы. Если флаг поднят, мы сбрасываем WatchDog и флаги. Таким образом нам нужно только заботиться, что бы в нужном месте программы поднимать флаг. Если микроконтроллер зависнет либо при выполнении программы (тогда у нас флаг не подымиться), либо в принципе зависнет (мы не сможем попасть в прерывание — мы не сможем сбросить WatchDog и микроконтроллер перезагрузиться.

Улучшенный программный код:

#include <avr/io.h>
#define F_CPU 4000000
#define BAUD 9600L
#define UBRRL_value (F_CPU/(BAUD*16))-1

#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

void init_pin(void);
#define HL1_on() PORTB|=(1<<PB0)
#define HL1_off() PORTB&=~(1<<PB0)
#define HL2_on() PORTB=(1<<PB1)
#define HL2_off() PORTB&=~(1<<PB1)

int b;
int metka1 = 0;
int metka2 = 0;

ISR (TIMER0_OVF_vect){// прерывание по таймеру TIMER0
   if ((metka1 == 1)||(metka2 == 1)){
         wdt_reset(); // сбрасываем таймер watchdoq
        metka1 = 0;
        metka2 = 0;
    }
}

ISR(USART_RXC_vect){
    b = UDR;
}

void init_USART() {
   UBRRL = UBRRL_value;
   UBRRH = UBRRL_value >> 8;
   UCSRB |=(1<<TXEN)|(1<<RXEN);
  UCSRC |=(1<< URSEL)|(1<< UCSZ0)|(1<< UCSZ1);
   UCSRB |= (1<<RXCIE);
   sei();
}

void send_UART(char value) {
   while(!( UCSRA & (1 << UDRE)));
  UDR = value;
}

int main(void){
    init_pin();
    init_USART();
    send_UART('O');
    send_UART('K');
    send_UART('!');
    send_UART(0x0D); //переход в начало строки
    send_UART(0x0A);//переход на новую строку
    wdt_enable(WDTO_2S);// включаем watchdog на 2 секунды

while(1){
   metka1 = 1;
   if(b=='0'){
 metka2 = 1;
 HL1_on(); HL2_off();
 send_UART('0');
 send_UART(0x0D); //переход в начало строки
 send_UART(0x0A);//переход на новую строку
 _delay_ms(500);
  }
 if(b=='1'){
 metka2 = 1;
 HL2_on();HL1_off();
   send_UART('1');
   send_UART(0x0D); //переход в начало строки
   send_UART(0x0A);//переход на новую строку
  _delay_ms(500);
 }
 if(b=='2'){
 metka2 = 1;
 HL2_on();HL1_on();
   send_UART('2');
   send_UART(0x0D); //переход в начало строки
   send_UART(0x0A);//переход на новую строку
   _delay_ms(500);
 }
 if (b=='3'){
 metka2 = 1;
 HL1_off();
 HL2_off();
 send_UART('3');
 send_UART(0x0D); //переход в начало строки
   send_UART(0x0A);//переход на новую строку
   while(1){;}
 }
}
}

void init_pin(void){
   PORTB=0b00000000;// PB1
   DDRB= 0b00000011;// PB1 output
   // настройка таймера 0
   TIMSK |= (1<<TOIE0);
   TCCR0 |= (1<<CS00);
}

 

Результат вышеописанного кода:

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