Зачем нужен 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 сек).
Драйвер двигателя постоянного тока 15А, 5.5–24 В — H-Bridge на A3941 для Arduino, ESP32, STM32
Драйвер для управления двигателями постоянного тока до 15А, построенный на мощной H-мостовой микросх..
387.47грн.
Циклы 5 — Вложенные циклы
Циклы 5 — Вложенные циклы Введение Вложенные циклы — это конструкция, при которой один ц..
Модуль 8 дискретных входов I2C на DIN-рейку без корпуса
Плата продаётся без коробкиМодуль восьми дискретных входов I2C для построения домашней или промышлен..
640.59грн.
Arduino SIM800L выбор оператора AT+COPS
Привет друзья! Пора продолжить тему SIM800. В этом видео мы научимся выбирать GSM-оператора вручн..
Простейший способ проверки GSM-модуля
Бывают моменты, когда нас выводит из себя такая штуковина как GSM модуль к любительской плате контро..




