Працюємо з 09:00 до 19:00 без вихідних
Київ біля ТЦ Квадрат бул.Перова

Зачем нужен watchdog (сторожевой таймер)?

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

Использование сторожевого таймера для предотвращения сбоев в Arduino UNO

Как и любая функция «низкого уровня» микроконтроллера, функция сторожевого таймера Arduino запрашивает регистры битов настройки. Соответствующие регистры и их значения описаны в техническом описании микроконтроллера, встроенного в каждую плату Arduino, которое может быть довольно сложным для чтения новичками. К счастью, в Arduino IDE есть несколько функций и макросов для упрощения процесса, которые можно импортировать с помощью команды #include <avr/wdt.h>. Таким образом, сторожевой таймер можно включить, вызвав функцию wdt_enable (). Аргумент функции wdt_enable () указывает время до перезагрузки платы в случае, когда сторожевой таймер не сбрасывается, и определяет следующие псевдонимы:

время срабатывания watchdog    аргумент функции wdt_enable()
-------------------------------------------------------
15mS                           WDTO_15MS
30mS                           WDTO_30MS
60mS                           WDTO_60MS
120mS                          WDTO_120MS
250mS                          WDTO_250MS
500mS                          WDTO_500MS
1S                             WDTO_1S            
2S                             WDTO_2S
4S                             WDTO_4S
8S                             WDTO_8S

Например, следующий код перезагрузит Arduino UNO после выполнения секции setup и нахождения в секции loop на протяжении 15 мс (вы увидите мигание светодиода на 13-м пине каждый раз, когда контроллер будет пробегать через setup при каждой автоматической перезагрузке контроллера):

#include <avr/wdt.h>
#define led 13 // пин 13 соединен со светодиодом на плате Arduino Uno
void setup()
{
  pinMode(led, OUTPUT);      // назначить тип пина
  digitalWrite(led, HIGH);   // включить светодиод
  delay(100);                // подождать 100 мс
  digitalWrite(led, LOW);    // выключить светодиод
  delay(100);                // подождать 100 мс
}
void loop(){
  wdt_enable(WDTO_15MS);     // разрешить watchdog
                             // функция запустится через 15 мс, если не сбросить таймер
  while(1)
  {
//     wdt_reset();          // разкоментируйте для избежания перезагрузок
  }
}

Для избежания перезагрузок Arduino UNO во время нормальной работы вашей программы, нужно постоянно в каждом цикле loop вызывать функцию wdt_reset(); для обнуления сторожевого таймера.

Как использовать watchdog для экономии энергии

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

Перевод контроллера в самый низкопотребляемый режим сна можно произвести с помощью функции set_sleep_mode(SLEEP_MODE_PWR_DOWN); из библиотеки #include <avr / sleep.h>. Эту команду способен инициировать наш сторожевой таймер. Так же эту команду можно подавать и в функции прерывания по дискретному входу.

Но для стабильного просыпания удобно использовать именно watchdog. Это можно сделать, например, с помощью следующей программы, в которой было предпринято несколько дополнительных мер для экономии энергии, таких как отключение АЦП.

#include <avr/wdt.h>            // библиотека для стандартных watchdog функций
#include <avr/interrupt.h>      // библиотека для работы с прерываниями
#include <avr/sleep.h>          // библиотека для режима сна
#include <avr/power.h>          // библиотека для управления режимами питания

// сколько раз погружаться в сон до просыпа
int nbr_remaining; 

#define led 13

// прерывание запускается сторожевым таймером
// когда watchdog срабатывает во время сна, выполнится следующая функция
// запомните, что прерывания внутри функции ISR запрещены
ISR(WDT_vect)
{
        // перезапустить watchdog
        wdt_reset();
}

// функция для конфигурирования сторожевого таймера: пусть спит 8 секунд перед срабатыванием
void configure_wdt(void)
{
 
  cli();                           // запретить прерывания для изменения регистров

  MCUSR = 0;                       // сбрасывание флагов status register

                                   // перевести таймер в режим interrupt-only:                                       
  WDTCSR |= 0b00011000;            // Выставляем биты WDCE и WDE для входа в режим конфигурирования,
                                   
  WDTCSR =  0b01000000 | 0b100001; // выставляем биты WDIE: разрешение прерывания
                                   // clr WDE: сбрасывание запрещено
                                   // и выставляем интервал задерки 8 секунд. Все битами

  sei();                           // разрешаем прерывания

  // напоминалка для выставления времени задержки перед срабатыванием:
  //  16 мс:  0b000000
  //  500 мс: 0b000101
  //  1 сек:  0b000110
  //  2 сек: 0b000111
  //  4 сек: 0b100000
  //  8 сек: 0b100001
}

// Перевести Arduino UNO в глубокий сон. Только прерывание сможет разбудить его.
void sleep(int ncycles)
{  
  nbr_remaining = ncycles; // сколько циклов будет спать

  //Установить в сон с самым низким потреблением. Разбудить при этом сможет внешнее прерывание или watchdog
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
  //выключить ADC пока спит
  power_adc_disable();
 
  while (nbr_remaining > 0){

  // разрешить сон и погрузиться в режим сна
  sleep_mode();

  // CPU is now asleep and program execution completely halts!
  // Once awake, execution will resume at this point if the
  // watchdog is configured for resume rather than restart
 
  // When awake, disable sleep mode
  sleep_disable();
  
  // we have slept one time more
  nbr_remaining = nbr_remaining - 1;
 
  }
 
  // put everything on again
  power_all_enable();
 
}

void setup(){
  
  // use led 13 and put it in low mode
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  
  delay(1000);
  
  // configure the watchdog
  configure_wdt();

  // blink twice
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 

}

void loop(){

  // sleep for a given number of cycles (here, 5 * 8 seconds) in lowest power mode
  sleep(5);

  // usefull stuff should be done here before next long sleep
  // blink three times
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 

}

ISR (WDT_vec) - это функция прерывания, которая будет вызываться при срабатывании сторожевого таймера. WDT_vec указывает, что это прерывание будет вызываться сторожевым таймером. Другие инициаторы могут вызывать прерывания и будут иметь другие параметры _vec. Есть несколько вещей, которые нужно знать о функциях ISR. Во-первых, прерывания не работают внутри функций ISR, поэтому функции, основанные на прерываниях, не будут работать должным образом (например, Serial.write). Более того, обычные переменные не могут быть изменены в функциях ISR. Если вам нужно изменить переменную в прерывании, вы должны объявить ее как volatile при первом использовании. В общем, функции ISR должны быть как можно короче, чтобы позволить нормальному циклу программы возобновиться как можно быстрее (не забывайте, что прерывания отключаются во время ISR, поэтому прием прерываний по последовательному или контактному принципу не будет работать при выполнении ядра ISR).

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

Использование watchdog для предотвращения зависаний и энергосберегающего режима вместе

Мы научились использовать сторожевой таймер, чтобы избежать зависаний кода и перевести Arduino в режим минимальной мощности. Мы также можем использовать обе функции в одной программе, используя немного больше кода в функции ISR (). Это именно то, что делает следующий пример.

#include <avr/wdt.h>            // library for default watchdog functions
#include <avr/interrupt.h>      // library for interrupts handling
#include <avr/sleep.h>          // library for sleep
#include <avr/power.h>          // library for power control

// how many times remain to sleep before wake up
// volatile to be modified in interrupt function
volatile int nbr_remaining; 

// pin on which a led is attached on the board
#define led 13

// interrupt raised by the watchdog firing
// when the watchdog fires, this function will be executed
// remember that interrupts are disabled in ISR functions
// now we must check if the board is sleeping or if this is a genuine
// interrupt (hanging)
ISR(WDT_vect)
{
    // Check if we are in sleep mode or it is a genuine WDR.
    if(nbr_remaining > 0)
    {
        // not hang out, just waiting
        // decrease the number of remaining avail loops
        nbr_remaining = nbr_remaining - 1;
        wdt_reset();
    }
    else
    {
        // must be rebooted
        // configure
        MCUSR = 0;                          // reset flags
       
                                            // Put timer in reset-only mode:
        WDTCSR |= 0b00011000;               // Enter config mode.
        WDTCSR =  0b00001000 | 0b000000;    // clr WDIE (interrupt enable...7th from left)
                                            // set WDE (reset enable...4th from left), and set delay interval
                                            // reset system in 16 ms...
                                            // unless wdt_disable() in loop() is reached first
                                       
        // reboot
        while(1);
    }
}

// function to configure the watchdog: let it sleep 8 seconds before firing
// when firing, configure it for resuming program execution by default
// hangs will correspond to watchdog fired when nbr_remaining <= 0 and will
// be determined in the ISR function
void configure_wdt(void)
{
 
  cli();                           // disable interrupts for changing the registers

  MCUSR = 0;                       // reset status register flags

                                   // Put timer in interrupt-only mode:                                       
  WDTCSR |= 0b00011000;            // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
                                   // using bitwise OR assignment (leaves other bits unchanged).
  WDTCSR =  0b01000000 | 0b100001; // set WDIE: interrupt enabled
                                   // clr WDE: reset disabled
                                   // and set delay interval (right side of bar) to 8 seconds

  sei();                           // re-enable interrupts 
}

// Put the Arduino to deep sleep. Only an interrupt can wake it up.
void sleep(int ncycles)
{  
  nbr_remaining = ncycles; // defines how many cycles should sleep

  // Set sleep to full power down.  Only external interrupts or
  // the watchdog timer can wake the CPU!
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
  // Turn off the ADC while asleep.
  power_adc_disable();
 
  while (nbr_remaining > 0){ // while some cycles left, sleep!

  // Enable sleep and enter sleep mode.
  sleep_mode();

  // CPU is now asleep and program execution completely halts!
  // Once awake, execution will resume at this point if the
  // watchdog is configured for resume rather than restart
 
  // When awake, disable sleep mode
  sleep_disable();
 
  }
 
  // put everything on again
  power_all_enable();
 
}

void setup(){
  
  // use led 13 and put it in low mode
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  
  delay(1000);
  
  // configure the watchdog
  configure_wdt();

  // blink twice
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 

}

void loop(){

  // sleep for a given number of cycles (here, 5 * 8 seconds) in lowest power mode
  sleep(5);

  // usefull stuff
  // blink three times
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500);
  digitalWrite(led, HIGH);   
  delay(500);               
  digitalWrite(led, LOW);   
  delay(500); 

  // now a real hang is happening: this will rebood the board
  // after 8 seconds
  // (at the end of the sleep, nrb_remaining = 0)
  while (1);

}

Выводы

Теперь вы можете использовать сторожевой таймер Arduino для предотвращения зависаний и экономии энергии. Поскольку детальное назначение сторожевого таймера максимально доступно показано в программе, вы легко сможете адаптировать код под свои нужды. Например, если вам нужно отправить какой-либо контент в Интернет и некоторые операции требуют некоторого времени, вы можете вызывать следующий код перед каждой трудоемкой операцией.

wdt_reset();
nbr_remaining = 5; 

Это нужно для разрешения операции выполняться на протяжении нескольких полных циклов таймера (здесь, 5 * 8 = 40 сек).

Написать отзыв

Примечание: HTML разметка не поддерживается! Используйте обычный текст.
    Плохо           Хорошо
Модуль датчика расстояния TCRT5000

Модуль датчика расстояния TCRT5000

Модуль для измерения расстояния по интенсивности отражения инфракрасного луча от объекта.Имеет дискр..

17.63грн.

Уроки Arduino для новичков 1.2.3 Компиляция программы Arduino

Уроки Arduino для новичков 1.2.3 Компиляция программы Arduino

Компиляция программы ArduinoНа этом уроке мы поговорим о компиляции программы в Arduino IDE: как..

Гайка М3 нейлоновая

Гайка М3 нейлоновая

Гайка четырехгранная под резьбу М3Подходит к нейлоновым стойкам и винтамОсновное преимущество перед ..

0.70грн.

Контроллер заряда для солнечной батареи 3А 6В, 12В

Контроллер заряда для солнечной батареи 3А 6В, 12В

Это зарядное устройство кислотного аккумулятора для зарядки его от солнечной батареи.Импульсный хара..

173.28грн.

Усилитель звуковой частоты 10 Вт х 2 PAM8610

Усилитель звуковой частоты 10 Вт х 2 PAM8610

Миниатюрный мощный стерео- усилитель звуковой частоты класса DНоминальное напряжение питания однопол..

42.52грн.