8 сентября 2017 г.

Метеостанция на arduino. #4. Уменьшение энергопотребления Arduino

Так как метеостанцию планируется питать от аккумулятора немаловажен вопрос уменьшения ее энергопотребления. Об уменьшении энергопотребления метеостанции в целом - в другой раз, здесь же хочется рассказать о снижении энергопотребления именно платы arduino.
Ресурсы по данной теме, которые были найдены (наиболее интересные):
Материал указанных источников можно обобщить в виде следующих действий по уменьшению энергопотребления плат arduino:
  • убрать с платы все "лишнее": светодиоды (питания, RX, TX), отказаться от использования стабилизатора или заменить его на более эффективный, коммутировать питание контроллера USB (включать только на время прошивки), увеличить номинал подтягивающего резистора на ноге reset (совсем экзотика :); 
  • уменьшить тактовую частоту микроконтроллера;
  • уменьшить напряжение питания;
  • отключать неиспользуемую периферию микроконтроллера (АЦП, SPI, USART, таймеров, TWI/I2C, watchDog), отключить Brown-out Detector (BOD) и выключать все неиспользуемые пины;
  • на время бездействия использовать режим сна;
  • никаких setup() и loop(), только main().
На своей Pro Mini я сделал следующее:
  • убрал светодиод питания, стабилизатор пока трогать не стал;
  • тактовую частоту пробовал уменьшать - здорово уменьшается энергопотребление в активном режиме, на режим сна не влияет (оно и понятно, МК же спит :)). Здесь нужен компромисс - меньше частота -> меньше производительность -> больше необходимо времени для выполнения аналогичных действий (относительно частоты по-умолчанию). Надо будет поэкспериментировать. Вроде особо ничего затратного выполнять не планируется, поэтому можно уменьшать, но надо будет посмотреть как при этом заработают интерфейсы SPI и I2C. Нашел два способа изменения частоты (см. ниже);
  • остальное см. в коде.
Собственно код (вариант 1):

//код++
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <util/delay.h>

void m_sleep();

int main() 
{
  // инициализация (аналог setup())
  // уменьшаем частоту тактирования микроконтроллера в 16 раз
  clock_prescale_set(clock_div_16);
  
  // аналог loop()
  while(true) {
    pinMode(13, OUTPUT);
    // светим светодиодом 10 секунд
    digitalWrite(13, HIGH);

    // Функция  _delay_ms() завязана на тактовой частоте, define-ом частоту переопределить 
    // не получилось (#define F_CPU 1000000), поэтому делаем так
    // Ждем 10 секунд
    _delay_ms(10000/16);
    digitalWrite(13, LOW);  

    // и засыпаем
    m_sleep();
  }
}

void m_sleep() {

  // все пины на выход и в низкий уровень
  for (byte i = 0; i <= A7; i++) {
    pinMode(i, OUTPUT);    
    digitalWrite(i, LOW);
  }
  
  // отключаем АЦП
  ADCSRA = 0;  

  // отключаем всю периферию
  power_all_disable ();

  // устанавливаем режим сна - самый глубокий, здоровый сон :)
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  

  // разрешаем спящий режим
  sleep_enable();

  // ничто не должно прервать отключение BOD-а, поэтому вырубаем прерывания
  cli();

  // отключаем BOD
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  
  sei();            

  // собственно засыпаем
  sleep_cpu();              
}
//код--

Результат - энергопотребление на уровне 7,2 мА в активном режиме (пустой бесконечный цикл; 13,5 мА без снижения частоты) и 10 мкА в режиме сна при 5 В напряжения питания. 
Позже нашел очень интересный ресурс: MiniCore. После установки этой фичи в Arduino IDE меню "Инструменты" становится таким:
Эта надстройка позволяет простым выбором пунктов меню сконфигурировать FUSE-биты. Интересны настройка BOD-а (выключил) и установка частоты (1 МГц). Результаты в принципе аналогичны, только в спящем режиме энергопотребление 8 мкА. Плюсы: BOD постоянно выключен (хотя он и потребляет совсем немного), нет проблемы с пробуждением по watchdog-гу (подробности здесь) - думаю, в будущем понадобиться эта функция. По ссылке вроде все понятно что и как делать, но если возникнут вопросы, пишите, расскажу поподробнее.
Ну и код упростился (вариант 2):

//код++
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <util/delay.h>

void m_sleep();

int main() 
{
  
  // аналог loop()
  while(true) {
    // светим светодиодом 10 секунд
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);
    
    // Теперь функция _delay_ms() работает нормально
    // Ждем 10 секунд
    _delay_ms(10000);
    digitalWrite(13, LOW);  

    // и засыпаем
    m_sleep();
  }
}

void m_sleep() {

  // все пины на выход и в низкий уровень
  for (byte i = 0; i <= A7; i++) {
    pinMode(i, OUTPUT);    
    digitalWrite(i, LOW);
  }
  
  // отключаем АЦП
  ADCSRA = 0;  

  // отключаем всю периферию
  power_all_disable ();

  // устанавливаем режим сна - самый глубокий, здоровый сон :)
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  

  // разрешаем спящий режим
  sleep_enable();

  // собственно засыпаем
  sleep_cpu();              
}
//код--

Исходники здесь. Дальше буду разбираться с выходом из режима сна.


Комментариев нет: