Зачем нужен 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 сек).
Сменное жало паяльника 900M-T 1шт.
Жало для паяльника 60Вт с регуляторомДиаметр рабочей части 3 ммВнутренний диаметр отверстия под нагр..
32.87грн.
Генератор импульсов на NE555
Генератор прямоугольных импульсов на микросхеме NE555 с плавной подстройкой частоты и заполненияНапр..
40.60грн.
Набор керамических конденсаторов 300шт.
Конденсаторы миниатюрные керамические 30 номиналов по 10 штук каждого.Напряжение до 50 В.2P,3P,5P,10..
116.42грн.
Двигатель с редуктором 10RPM 12В
Двигатель постоянного тока с редуктором. При питании 12 В - скорость вращения оси 10 об./минСко..
390.25грн.
Радиомодуль NRF24L01
Радио передатчик-приемник с рабочей частотой 2,4 ... 2,5 ГГц для работы в связке с платой контроллер..
50.26грн.