Ватчдог таймер/петля глубокого сна для 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();
}

, 👍1

Обсуждение

Я не совсем понимаю, в чем проблема. Мне кажется, что вы не совсем понимаете, как работает таймер 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.


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();
  }
}
,