Ватчдог таймер/петля глубокого сна для Atmega 328 Pro Mini 5v 16 mhz
Я изо всех сил пытался заставить свой Adafruit 5v pro mini несколько раз переходить в режим глубокого сна, затем просыпаться, чтобы что-то выполнить, а затем вернуться в цикл глубокого сна. Пока он находится вне режима глубокого сна, я хочу, чтобы сторожевой таймер был активен для защиты от зависаний системы. Хотя я могу достаточно легко входить и выходить из режима глубокого сна, а также использовать цикл, который выполняет один цикл глубокого сна / бодрствования. Я хочу максимизировать экономию энергии, имея длительный период глубокого сна в несколько минут. Я прочитал много постов, связанных с этой темой, но пока не нашел ответа, который сработал бы для меня. Прилагается мой код, который дважды проходит цикл сна, но не в том случае, если ISR(WDT_vect) в строке 85 вызывает мою функцию hardwareReset() . Если используется мой вызов hardwareReset, мой тестовый цикл по строкам увеличивается в 25-30 раз, как и ожидалось, но моя функция waitSleep больше не повторяется дважды. Кто-нибудь может дать совет? Большое спасибо
// спасибо KeyChainino за базовый код. Я добавил отключение АЦП, чтобы еще больше снизить мощность с 0,67 мА до 0,54 мА
//
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
const int ledPin = 13;
const int resetPin = 9;
// функция setup запускается один раз при нажатии кнопки reset или включении питания платы
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(resetPin, OUTPUT);
digitalWrite(resetPin,HIGH);
Serial.begin(9600);
Serial.println("Resetting..");
delay(100);
}
// функция loop работает снова и снова forever
void loop() {
digitalWrite(13, HIGH); // включите светодиод (ВЫСОКИЙ уровень напряжения)
delay(1000); // дождитесь второго
digitalWrite(13, LOW); // выключите светодиод, сделав напряжение НИЗКИМ
for(int i=0; i<9; i++){; // настройте цикл для принудительного таймаута
delay(1000);
Serial.print("Loop ");
Serial.println(i);
delay(100);
}
waitSleep(2); //перейти в спящий режим для x циклов настроек WDT (максимум 8 секунд на цикл)
}
void waitSleep(int sleep_cycles) {
while (sleep_cycles) {
goSleep();
sleep_cycles--;
}
}
void goSleep() {
watchdogSetup(); //включить сторожевой пес
// отключить АЦП (аналого-цифровое преобразование)
byte old_ADCSRA = ADCSRA;
ADCSRA = 0;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
//отключить сторожевой пес после сна
wdt_disable();
ADCSRA = old_ADCSRA;
}
void watchdogSetup() {
//WDP3 - WDP2 - WPD1 - WDP0 - время
// 0 0 0 0 16 мс
// 0 0 0 1 32 мс
// 0 0 1 0 64 мс
// 0 0 1 1 0.125 с
// 0 1 0 0 0.25 с
// 0 1 0 1 0.5 с
// 0 1 1 0 1.0 с
// 0 1 1 1 2.0 с
// 1 0 0 0 4.0 с
// 1 0 0 1 8.0 с
// Сброс флага
bitClear(MCUSR, WDRF);
// Следующий бит должен быть установлен одновременно, иначе система не установит правильное значение
/* Start timed equence */
//Watchdog Change Включить, чтобы очистить WD (установив бит WDCE) и Включить WD (установив бит WDE)
WDTCSR |= (1 << WDCE) | (1 << WDE);
// Установите новое значение тайм-аута сторожевого пса равным 8 секундам (WDP3 и WDP0 равны 1) и включите прерывания вместо сброса (WDIE равен 1).
WDTCSR = (1 << WDIE) | (1 << WDP3) | (1 << WDP0) ;
}
void hardwareReset(){;
digitalWrite(resetPin,LOW);
}
ISR(WDT_vect) {
//hardwareReset();
}
@J.B., 👍1
Обсуждение1 ответ
Я не знаю, лучшее ли это решение, но я нашел обходной путь. В функции ISR(WDT_vect) я добавил оператор while, в котором я добавил логическую переменную, установленную в true при запуске базовой программы и false при входе в циклический режим глубокого сна. Я был бы признателен, если бы кто-то там мог предложить лучшее решение, но пока это работает для меня. Ниже приведен пересмотренный код на случай, если кому-то это интересно.
// спасибо KeyChainino за базовый код. Я добавил отключение АЦП, чтобы еще больше снизить мощность с 0,67 мА до 0,54 мА
//пересмотрено Джоном Бедфордом 01/12/2020
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
const int ledPin = 13;
const int resetPin = 9;
volatile boolean wdtInterrupt; //флаг прерывания сторожевого таймера
// функция setup запускается один раз при нажатии кнопки reset или включении питания платы
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(resetPin, OUTPUT);
digitalWrite(resetPin,HIGH);
Serial.begin(9600);
Serial.println("Resetting..");
delay(100);
watchdogSetup();
}
// функция цикла выполняется снова и снова навсегда
void loop() {
wdtInterrupt= true;
digitalWrite(13, HIGH); // включите светодиод (HIGH - уровень напряжения)
delay(1000); // дождитесь второго
digitalWrite(13, LOW); // выключите светодиод, сделав напряжение НИЗКИМ
for(int i=0; i<9; i++){; // настройте цикл для принудительного таймаута WDT
delay(1500);
Serial.print("Loop ");
Serial.println(i);
delay(100);
}
wdt_reset();
waitSleep(2); //перейти в спящий режим для x циклов настроек WDT (максимум 8 секунд на цикл)
}
void waitSleep(int sleep_cycles) {
wdtInterrupt=false;
while (sleep_cycles) {
goSleep();
sleep_cycles--;
}
}
void goSleep() {
watchdogSetup(); //включить сторожевой пес
// отключить АЦП (аналого-цифровое преобразование)
byte old_ADCSRA = ADCSRA;
ADCSRA = 0;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
//отключить сторожевой пес после сна
wdt_disable();
ADCSRA = old_ADCSRA;
}
void watchdogSetup() {
//WDP3 - WDP2 - WPD1 - WDP0 - время
// 0 0 0 0 16 мс
// 0 0 0 1 32 мс
// 0 0 1 0 64 мс
// 0 0 1 1 0.125 с
// 0 1 0 0 0.25 с
// 0 1 0 1 0.5 с
// 0 1 1 0 1.0 с
// 0 1 1 1 2.0 с
// 1 0 0 0 4.0 с
// 1 0 0 1 8.0 с
// Сброс флага
bitClear(MCUSR, WDRF);
// Следующий бит должен быть установлен одновременно, иначе система не установит правильное значение
/* Start timed equence */
//Watchdog Change Включить, чтобы очистить WD (установив бит WDCE) и Включить WD (установив бит WDE)
WDTCSR |= (1 << WDCE) | (1 << WDE);
// Установите новое значение тайм-аута сторожевого пса равным 8 секундам (WDP3 и WDP0 равны 1) и включите прерывания вместо сброса (WDIE равен 1).
WDTCSR = (1 << WDIE) | (1 << WDP3) | (1 << WDP0) ;
}
void hardwareReset(){;
digitalWrite(resetPin,LOW);
}
ISR(WDT_vect) {
while (wdtInterrupt){
hardwareReset();
}
}
- Помощь с прерыванием режима ожидания Arduino и ватчдог таймера
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Как перевести ATtiny/ATmega в режим глубокого сна (чтобы годами работать от батарей), но при этом обнаруживать нажатие кнопки?
- ESP8266 глубокий сон и MQTT
- ESP.deepSleep() — снова и снова?
- Пользовательская функция сна для Nextion
- WatchDog на Arduino Nano — не восстанавливается
- Вывод ESP8266 из deepSleep с помощью кнопки
Я не совсем понимаю, в чем проблема. Мне кажется, что вы не совсем понимаете, как работает таймер wachdog в сочетании с ISR, но я могу ошибаться. Ваш
цикл принудительного тайм-аута
вызывается до настройки сторожевого пса. Так что это работает только во второй раз. Может быть, вы делаете это для тестирования, но я всегда помещаю это в свою функцию "setup" (то есть минус бит WDIE). Помните, что позже вам нужно вызвать wdt_reset(); если вы ожидаете, что цикл займет более 8 секунд., @GerbenЕсли я правильно помню, бит WDIE очищается каждый раз, когда сторожевой таймер истекает. Поэтому, если ваш код зависает или спит в течение 8 секунд, вызывается ISR, и бит WDIE очищается. Затем, если пройдет еще 8 секунд, чип сбрасывается. Попробуй изменить " я " .<9
до
i<17` и посмотрите, сбросится ли чип сам., @GerbenЖаль только, что я не могу полностью понять, как работает сторожевой таймер. Я уже не помню, почему я убрал watchdog из настройки. Я перепробовал так много разных вещей. Я не хотел, чтобы прерывание срабатывало только один раз, а затем при следующем таймауте выполнял сброс. Это был мой способ разбудить устройство с помощью встроенной кнопки сброса, в то же время позволяя сброс, если программное обеспечение зависает. Может быть, мне стоит попробовать управлять WDIE немного по-другому? Я изменил "я<9" до "i<17". Изменений не было. Спасибо за вклад, Гербен., @J.B.
`Я не хотел, чтобы прерывание срабатывало только один раз"; вы можете. Вам просто нужно установить бит WDIE после каждого вызова прерывания. В вашем случае это будет установка бита WDIE каждый раз, когда MCU переходит в спящий режим. Может быть, вы могли бы сказать, чего именно вы пытаетесь достичь в конце концов, а не того, что не работает так, как вы ожидаете в своем тестовом коде. Поскольку я все еще не совсем понимаю, о чем вы просите / ожидаете., @Gerben
Сброс WDIE был ответом, который я искал. Все просто. Спасибо. Это делается для управления перистальтическим трюмным насосом на паруснике, зимующем в воде. Насос сохраняет трюмную кость сухой, но работает непрерывно. Лед - это проблема, поэтому Arduino чувствует температуру и отключает насос через реле, когда он близок к замерзанию. Для экономии заряда батареи arduino будет проверять температуру каждые 15 минут. Я не могу рисковать зависанием программного обеспечения, следовательно, сбросом сторожевого пса. Внешнее прерывание, необходимое для запуска OLED, если я хочу прочитать высокие и низкие сохраненные показания. Далее я проверю расход воды через датчик барометрического давления., @J.B.