Працюємо з 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

// interrupt raised by the watchdog firing
// when the watchdog fires during sleep, this function will be executed
// remember that interrupts are disabled in ISR functions
ISR(WDT_vect)
{
        // not hanging, just waiting
        // reset the watchdog
        wdt_reset();
}

// function to configure the watchdog: let it sleep 8 seconds before firing
// when firing, configure it for resuming program execution
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

  // reminder of the definitions for the time before firing
  // delay interval patterns:
  //  16 ms:     0b000000
  //  500 ms:    0b000101
  //  1 second:  0b000110
  //  2 seconds: 0b000111
  //  4 seconds: 0b100000
  //  8 seconds: 0b100001
 
}

// 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();
  
  // 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 размітка не підтримується! Використовуйте звичайтий текст.
    Погано           Добре
MOC3023 оптосимістор

MOC3023 оптосимістор

Оптопара з симісторним виходом для імпульсного управління потужним симістором від логічних сигналів ..

9.65грн.

Модуль PN532 NFC RFID

Модуль PN532 NFC RFID

NFC модуль на основі мікросхеми NXP PN532, контролера для безконтактного зв'язку на частоті 13,56 МГ..

171.05грн.

Роз'ємний конектор JST

Роз'ємний конектор JST

Слугує для швидкороз'эмного з'єднання. Наприклад, для монтажу датчиків, виконавчих механізмів, підве..

7.13грн.

Модуль CAN Bus в SPI

Модуль CAN Bus в SPI

Модуль для включення любительського контролеру в мережу CAN або для об'єднання любительських контрол..

70.31грн.

USB Паяльник 8 Вт

USB Паяльник 8 Вт

Напруга живлення 5 ВМожна живити від комп'ютера, ноутбука, павербанка ... Потужність споживання 8 В..

206.07грн.