Предыдущая статья. Следующая статья

Ардуино. Энергосбережение.


Навеяно (вольный и далеко не полный перевод) статьёй Nick Gammon:
This page can be quickly reached from the link: http://gammon.com.au/power

Sketch A

void setup () {}
void loop () {}
В первой части статьи говорится и показывается, что сам по себе микроконтроллер, без обвязки (макетной платы, ардуино) за счет отсутствия светодиодных индикаторов, линейного стабилизатора питания и т.п. "свистелок", потребляет ощутимо меньше. Тут - не поспоришь...

Уровни энергосбережения.


Различные режимы "сна" могут быть включены инструкцией:
 set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
По энергопотреблению режимы "сна" делятся на:

Note: В режиме более глубокого, чем IDLE сна - счетчик millis() не работает, но сохраняет свои значения между промежутками сна и увеличивает значение счетчика во время бодрствования. Таким образом, для приблизительного отсчета промежутков времени все-таки можно использовать привычный millis(), просто уменьшив требуемое значение в соответствии с реальными данными.

Запрет Brown-out.


В режиме SLEEP_MODE_PWR_DOWN с отключенным brown-out, потребление снижается с 360 uA to 335 uA, что позволяет сэкономить 25 uA (микро-ампер).

Brown-out Detector (BOD) - встроенная в ATmega подсистема, следящая за уровнем питания чипа, и выключенная по-умолчанию в Arduino. В момент подачи питания на схему происходит масса переходных процессов: возрастает напряжение питания, разгоняется резонатор. Необходимо дождаться их стабилизации, прежде чем начинать выполнение инструкций, иначе поведение МК может быть непредсказуемым. Для нейтрализации этого эффекта применяют стандартные схемы - т.н. "супервизоры" питания, которые следят за уровнем входного напряжения и удерживают (или подают) сигнал сброса контроллера до тех пор, пока оно не достигнет нормального уровня.
В промышленных разработках, особенно при работе со встроенной в AVR eeprom, Brown-out Detector должен быть включен.

BOD может быть выключен постоянно программированием соответствующих фьюзов в AVRdude:
avrdude -c usbtiny -p m328p -U efuse:w:0x07:m
или временно (до перезагрузки) - из кода программы:

Sketch C

Команда sleep_cpu() должна следовать сразу за вышеприведенными битовыми инструкциями, иначе Brown-out Detector снова включится.
#include  <avr/sleep.h>

void setup () 
 {
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);  // turn on brown-out enable select
  MCUCR = bit (BODS);        // this must be done within 4 clock cycles of above
  sleep_cpu ();              // sleep within 3 clock cycles of above
 }  // end of setup

void loop () { }

Выключение ADC (analog to digital conversion)


Отключение подсистемы аналого-цифрового преобразования (в SLEEP_MODE_PWR_DOWN) приводит к тому, что потребляемая мощность падает с 335 uA до 0.355 uA!
 // disable ADC
 ADCSRA = 0;

Конфигурация портов ввода-вывода


В зависимости от конфигурации портов ввода-вывода потребление в режиме сна тоже может быть разным.

Sketch D


#include <avr/sleep.h>

void setup () 
{

  for (byte i = 0; i <= A5; i++)
      {
       pinMode (i, OUTPUT);    // changed as per below
       digitalWrite (i, LOW);  //     ditto
      }

      // disable ADC
      ADCSRA = 0;  

      set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
      sleep_enable();

      // turn off brown-out enable in software
      MCUCR = bit (BODS) | bit (BODSE);
      MCUCR = bit (BODS); 
      sleep_cpu ();  

}  // end of setup

void loop () { }


Результаты тестирования в режиме SLEEP_MODE_PWR_DOWN:
Note: При этом к портам ничего не должно быть подключено. Если у Вас имеется работающий LED или что-то еще - понятно, что потребление будет выше.


Power Reduction Register (PRR)



Using the internal clock



Cнижение частоты - так же позволяет экономить энергию. Путем изменения фьюзов можно переключить микроконтроллер на внутренний источник частотой 8 MHz. Запуск приведенного выше скетча "A", со значением "lfuse = 0xE2", позволит снизить потребление до 11.05 mA (по сравнению с 15.15 mA при использовании внешнего кварца).
avrdude -c usbtiny -p m328p -U lfuse:w:0xE2:m


Пробуждение из режима "сна" по WatchDog таймеру.


В отличие от техасских MSP430x2xx, в AVR - WatchDog выключен по-умолчанию, и его надо предварительно включать.

Note:Прежде, чем начать эксперименты, полезно проверить: правильно ли обрабатывает bootloader ардуины перезагрузку. Хорошая статья на эту тему: http://habrahabr.ru/post/189744/

Тестирование на watchdog
#include <avr/wdt.h>

void setup() {
  wdt_disable(); // бесполезная строка до которой не доходит выполнение при bootloop
  Serial.begin(9600);
  Serial.println("Setup..");

  Serial.println("Wait 5 sec..");

  delay(5000); // Задержка, чтобы было время перепрошить устройство в случае bootloop

  wdt_enable (WDTO_8S); // Для тестов не рекомендуется устанавливать значение менее 8 сек.
  Serial.println("Watchdog enabled.");
 }

int timer = 0;

void loop(){
  // Каждую секунду мигаем светодиодом и значение счетчика пишем в Serial
  if(!(millis()%1000)){
  timer++;
  Serial.println(timer);
  digitalWrite(13, digitalRead(13)==1?0:1); delay(1);
  }
  //  wdt_reset();
}
/*
Если после перезагрузки светодиод на 13-м пине начинает бесконечно мигать, то значит 
загрузчик не поддерживает watchdog. Здесь даже кнопка сброса не поможет. Для последующей 
прошивки нужно плату отключать от питания и после включения успеть прошить до первой перезагрузки.
*/


Судя по даташиту, у процессора есть два состояния WDT:
System Reset - ресет при первом же удобном случае (по-умолчанию),
Interrupt and System Reset - при первом срабатывании WatchDog таймер формирует и отрабатывает прерывание, после чего бит WDIE сбрасывается в ноль, и (если бит WDIE не был повторно взведен скетчем) на второй раз - ресет.

Для того, чтобы при срабатывании watchdog не происходило ресета - взводится бит "WDTCSR = bit (WDCE)", или, если компилятор ругается, вместо WDTCSR, в соответствии с avr/wdt.h следует использовать более универсальное "_WD_CONTROL_REG = bit (WDCE)".
Ну и следует помнить, что после такого прерывания, работа программы возобновляется с того же самого места: т.е., если мы были, как это приведено во множестве интернет-примеров, в безусловном цикле while(1) - то мы в нем и останемся. В реальной жизни, для периодического засыпания - цикла не требуется, но иногда требуются delay(100) перед погружением в сон...

С точки зрения безопасности - не рекомендуется (повторно) взводить бит WDIE в процедуре обработки прерывания.

Sketch H


#include  <avr/sleep.h>
#include  <avr/wdt.h>

const byte LED = 9;

void flash ()
  {
  pinMode (LED, OUTPUT);
  for (byte i = 0; i < 10; i++)
    {
    digitalWrite (LED, HIGH);
    delay (50);
    digitalWrite (LED, LOW);
    delay (50);
    }
    
  pinMode (LED, INPUT);
    
  }  // end of flash
  
// watchdog interrupt
ISR (WDT_vect) 
{
   wdt_disable();  // disable watchdog
}  // end of WDT_vect
 
void setup () { }

void loop () 
{
 
  flash ();
  
  // disable ADC
  ADCSRA = 0;  

  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset
  WDTCSR = bit (WDCE) | bit (WDE);  // or use _WD_CONTROL_REG = bit (WDCE)
  // set interrupt mode and an interval 
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // or use _WD_CONTROL_REG = bit (WDCE) - set WDIE, and 8 seconds delay
  wdt_reset();  // pat the dog
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();
 
  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  sleep_cpu ();  
  
  // cancel sleep as a precaution
  sleep_disable();
  
  } // end of loop

Немного модифицированный пример показывает, как Вы можете проснуться, мигнуть на секунду светодиодом (approximately), после чего погрузиться в сон до следующего раза. Здесь, как уже упоминалось выше, используется свойство "WDTCSR Bit 6 - WDIE: Watchdog Interrupt Enable", т.е. при установке этого бита в "единицу" первое срабатывание WatchDog будет просто вызывать прерывание а не ресетить процессор (разница в точке, с которой продолжается работа скетча): в первом случае - будет просто продолжение работы, в втором - полноценный ресет с исполнением setup() и только потом loop().
Исходя из вышесказанного, вероятно, будет правильнее взводить бит WDIE непосредственно перед погружением в сон, чтобы срабатывание WDT по задержке где-то в другом месте программы - вызывало полноценный ресет.

Sketch I


#include  <avr/sleep.h>
#include  <avr/wdt.h>

const byte LED = 9;
  
// watchdog interrupt
ISR (WDT_vect) 
{
   wdt_disable();  // disable watchdog
}  // end of WDT_vect
 
void setup () { }

void loop () 
{
 
  pinMode (LED, OUTPUT);
  digitalWrite (LED, HIGH);
  delay (50);
  digitalWrite (LED, LOW);
  pinMode (LED, INPUT);
  
  // disable ADC
  ADCSRA = 0;  

  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset 
  WDTCSR = bit (WDCE) | bit (WDE);  // or use _WD_CONTROL_REG = bit (WDCE)...
  // set interrupt mode and an interval 
  WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);    // set WDIE, and 1 second delay or use _WD_CONTROL_REG = bit (WDCE)
  wdt_reset();  // pat the dog
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();
 
  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  sleep_cpu ();  
  
  // cancel sleep as a precaution
  sleep_disable();
  
  } // end of loop

PR-CY.ru
Еще статьи.