В любом микроконтроллере есть сторожевой таймер, называемым 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 на микроконтроллер:
Если не включить 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);
}
Результат вышеописанного кода:
Как видно при попадании в зависание (заранее прописанный цикл без выхода) у нас происходит перезагрузка микроконтроллера, во всех остальных случаях микроконтроллер работает без перезагрузки.