Навіщо потрібний 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 сек).
Датчик вологості та температури DHT21
Це точний та готовий до вивішування прямо на вулиці датчик відносної вологості та температури зі ста..
250.82грн.
Стійка латунна М3 висотою 10мм шестикутник мама-папа
Латунна стійка мама-папа з різьбою М3 та шестикутним зовнішнім перетиномЗастосовується при конструюв..
6.70грн.
Модуль годинника реального часу DS3231SN
Модуль годинника реального часу для різних плат мікроконтролерівХарактеристики:- інтерфейс: I2C , ма..
144.54грн.
Радіатори для Orange PI або Raspberry PI
Набір з трьох радіаторів охолодження для процесора мінікомп'ютера та його мікросхем пам'ятіМатеріал ..
37.03грн.
Модуль датчика відстані TCRT5000
Модуль для вимірювання відстані за інтенсивністю відбиття інфрачервоного променя від об'єкту.Має дис..
27.17грн.