понедельник, 2 апреля 2018 г.

Библиотека Low-Power

После предыдущих публикаций я не могу не упомянуть о библиотеке Low-Power. Данная библиотека предназначена для управления режимами энергосбережения Ардуино. Чтобы перевести Ардуино в интересующий режим достаточно вызвать соответствующую функцию:


  • idle;
  • adcNoiseReduction;
  • powerDown;
  • powerSave;
  • powerStandby;
  • powerExtStandby;
  • standby.
Каждая из этих функций имеет параметр для указания периода пребывания микроконтроллера в данном режиме (т.е. будет задействован WatchDog таймер для пробуждения) - от 15мс до 8с. Если периодическое пробуждение не требуется, то при вызове функции следует указать значение SLEEP_FOREVER. Остальные параметры функций предназначены для отключения периферии микроконтроллера, я не стану их перечислять и объяснять, вместо этого предлагаю заглянуть в файл LowPower.cpp, там все подробно расписано. Попробуйте найти, например, строку Name: powerDown - с нее начинается описание данной функции, приводятся ее параметры, их назначение и возможные значения.

С библиотекой идут несколько примеров для пробуждения Ардуино по сторожевому таймеру и от внешнего прерывания. Давайте рассмотрим пример powerDownWakeExternalInterrupt.ino, его листинг с моими комментариями приведен ниже.
// **** INCLUDES *****
#include "LowPower.h"

// Используем пин 2 для пробуждения
const int wakeUpPin = 2;

void wakeUp(){
  // Обработчик прерывания от пина 2
}

void setup(){
  // Настраиваем наш пин на ввод и задействуем подтягивающий резистор
  pinMode(wakeUpPin, INPUT_PULLUP);   
}

void loop(){
  // Разрешаем внешнее прерывание на пине 2 при низком уровне
  attachInterrupt(0, wakeUp, LOW);

  // Переводим Ардуино в режим power down с отключением модулей ADC и BOD
  // Остальные модули в этом режиме уже отключены
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 
  // При низком логическом уровне на пине 2 будет сгенерировано прерывание
  // и после вызова обработчика wakeUp() выполнение данной функции продолжится

  // Временно запретим прерывания, они пока не нужны
  detachInterrupt(0); 

  // А здесь должен быть код, который будет выполняться при пробуждении Ардуино
  // Это может быть опрос датчиков, обработка данных, передача или отображение 
  // информации и т.п.
}

Я лишь изменил режим работы пина 2 на INPUT_PULLUP, чтобы по умолчанию на нем всегда был высокий уровень и не требовался внешний подтягивающий резистор. Обработчик прерывания wakeUp не содержит кода, он здесь и не нужен, т.к. единственная задача внешнего прерывания - это разбудить микроконтроллер. При вызове функции powerDown выполняются те же действия, что и в моем скетче MinimizingPowerConsumption из предыдущей публикации. Конечно, здесь это все делается короче и проще. Единственное ограничение данной библиотеки - это тип используемого микроконтроллера.

На момент написания статьи библиотека Low-Power поддерживает следующие микроконтроллеры:

  • ATMega168
  • ATMega328P
  • ATMega32U4
  • ATMega2560
  • ATMega256RFR2
  • ATSAMD21G18A
Поэтому если вы работаете, например, с tiny85, то переводить ее в энергосберегающий режим придется вручную. А в целом библиотека весьма удачная, можете смело использовать ее для создания энергоэффективных устройств на Ардуино.

38 комментариев:

  1. Ардуино нано, в сон в ходит, но по нажатию не выходит. 10 из 10

    ОтветитьУдалить
    Ответы
    1. Добрый день!
      Этого не может быть. Скетч используете приведенный выше? Вы обратили внимание на комментарий после detachInterrupt(0)? Данный пример - это лишь заготовка для скетча и без его доработки Ардуино после пробуждения сразу уснет. Скорее всего дело в этом.

      Удалить
  2. Добрый день!
    Действительно, при компиляции LowPower для ATMega168 возникает ошибка, связанная с тем, что данный микроконтроллер не поддерживает режим Extended Standby Mode. Попробуйте добавить в LowPower.cpp (хоть с самого начала файла) следующий код, мне это помогло избежать ошибки при компиляции.

    #if defined __AVR_ATmega168__
    #ifndef SLEEP_MODE_EXT_STANDBY
    #define SLEEP_MODE_EXT_STANDBY SLEEP_MODE_STANDBY
    #endif
    #endif

    ОтветитьУдалить
  3. Не за что.
    А то уж собрался тестить на версии IDE 1.8.5, потому что до этого проверял на другой. Ну хорошо, что разобрались.

    ОтветитьУдалить
  4. а можно атмега328р разбудить по радиосигналу? всмысле, есть передатчик и приемник(привод камеры наблюдения). приемник спит, пока по передатчику не начинаешь посылать для него радиосигналы

    ОтветитьУдалить
    Ответы
    1. Радиомодуль какой планируется использовать?

      Удалить
  5. в приемнике я на пин 3 подсоединил вывод rx модуля, думал будет просыпаться обрабатывать команду и выполнять какие то действия, но нет(

    ОтветитьУдалить
    Ответы
    1. Я попробовал, у меня получилось. Без библиотеки LowPower. Если использовать режим Power-Down, то при пробуждении первый символ переданного сообщения теряется. Думаю, из-за того, что тактовому генератору нужно время, чтобы войти в рабочий режим. Выход - использовать режим Standby, он идентичен режиму Power Down, за исключением того, что тактовый генератор продолжает функционировать. Благодаря этому пробуждение микроконтроллера и переход в рабочий режим требуют всего 6 тактов. Попробуйте такой код:
      #include "avr/sleep.h"
      #include "SoftwareSerial.h"
      #define pin_rx 2
      #define pin_tx 3
      SoftwareSerial mySerial(pin_rx, pin_tx);

      void setup()
      {
      Serial.begin(9600);
      mySerial.begin(9600);
      }

      void loop()
      {
      Serial.println("go to sleep");
      delay(100);
      attachInterrupt(digitalPinToInterrupt(pin_rx), myISR, LOW);
      set_sleep_mode(SLEEP_MODE_STANDBY);
      sleep_mode();
      Serial.println("wake up");
      while (mySerial.available()){
      Serial.write(mySerial.read());
      delay(1);
      }
      }

      void myISR() {
      detachInterrupt(digitalPinToInterrupt(pin_rx));
      }

      Удалить
    2. а режим SLEEP_MODE_STANDBY поддерживает прерывания типа CHANGE?

      Удалить
    3. а на сколько этот режим экономнее, чем pwr_down и как еще можно в данном коде снизить энергопотребление?

      Удалить
    4. Разница, конечно, есть: в одном случае микроконтроллер потребляет единицы микроампер, в другом десятки. Но эта разница несущественна на фоне потребления HC-12: согласно документации, с завода модуль поставляется сконфигурированным на режим FU3, при котором ток в режиме ожидания составляет 16мА; в режиме FU2 это значение снижается до 3.6мА; наименьший ток - 80мкА в режиме FU1, но при этом снижается и скорость обмена до 4800 бит/c

      Учтите, что Standby mode используется при работе микроконтроллера от внешнего источника тактирования.

      Можно попробовать все-таки использовать режим Power Down, но передатчик должен будет сначала разбудить микроконтроллер фиктивным сообщением, дать ему время на пробуждение и уже после этого передавать данные.

      Для минимизации энергопотребления уменьшайте напряжения питания, снижайте частоту МК. И используйте отдельный микроконтроллер, а не Ардуино.

      Удалить
    5. я вообще радиомодуль настроил на режим fu4, где макс. дальность и минимальная скорость передачи 1200бод. какая частота мк для таких настроек подойдет?

      Удалить
    6. кстати, полез я в ттх радиомодуля, так там 16мА это рабочий ток (для фю4), а в режиме спячки он потребляет всего 22мкА. одна загвоздка в том, что он не засыпает вместе с ардуино, а при помощи ат команды AT+SLEEP и пробуждается по любой другой ат команде. так что, в моем коде, который я ниже привел, модуль находится в активном режиме

      Удалить
    7. Я бы настроил МК на работу от внутреннего RC(8мгц) и понизил до 1мгц фьюзом ckdiv8.

      А режим sleep радиомодуля, я так понимаю, не подойдет. Потому что в нем он сам спит и не получает сообщения

      Удалить
    8. я даже не могу ат команды устанавливать. с замкнутой на землю ногой сет радиомодуля скетч почему то не работает

      Удалить
    9. а так да, ардуино должна просыпаться по радиосигналу от радиомодуля, который сам спит. т.е. замкнутый круг какой то(

      Удалить
    10. а просто если понизить частоту мк до 1-2МГц?

      Удалить
    11. и какой эффект даст отключение ацп?

      Удалить
    12. Можно и так. Только при изменении частоты "на ходу", тайминги, рассчитанные на 16мгц, не соответствуют реальной частоте. Из-за этого меняется длительность задержек по delay. Или вместо 9600, указанных в Serial.begin(), получаеся другая скорость обмена.

      Я как-нибудь соберусь, напишу публикацию про использование atmega328 без ардуино. Чтобы при этом тайминги не ломались. Сейчас пока другим занят...

      Удалить
    13. а какой эффект будет при таком коде?
      void loop() {
      deepSleep();
      if(mySerial.available() > 1){
      int input = mySerial.parseInt(); // read serial input and convert to integer
      if(input == 1111){
      flag=true;
      }
      if(input == 0000){
      flag=false;
      }
      mySerial.flush();//clear the buffer for unwanted inputs
      }
      }

      void deepSleep(){
      ADCSRA &= ~(1 << ADEN); //отключаем ацп
      set_sleep_mode(SLEEP_MODE_STANDBY);
      attachInterrupt(1, myItrpt, CHANGE); // контакт D3
      digitalWrite(13,flag);
      sleep_mode(); // включить энергосберегающий режим
      // Теперь микроконтроллер простаивает

      }

      void myItrpt(){
      detachInterrupt(0);
      ADCSRA |= 1 << ADEN; // Включаем АЦП
      }

      Удалить
    14. отключение ацп даст экономию?

      Удалить
    15. Даст. Конкретное значение зависит от напряжения питания. Навскидку - 2-3 сотни микроампер.

      Удалить
    16. спасибо) залил скетч и все работает. жалко сейчас нет мультиметра, чтоб замерить энергопотребление. а вот с уменьшением частоты возникают проблемы. если добавлю команду clock_prescale_set(clock_div_8); то скетч перестает работать

      Удалить
    17. Если уменьшаете частоту вызовом clock_prescale_set(clock_div_8) то, соответственно, в mySerial ее нужно увеличить в 8 раз. Чтобы фактическая скорость передачи осталась прежней. Это то, о чем я писал выше про тайминги. Или уменьшите скорость обмена у передатчика

      Удалить
    18. я уменьшил до 1200бод. меньше нету

      Удалить
  6. tx:
    #include
    #include

    SoftwareSerial mySerial(4,3);//tx,rx

    const int inputPin = 2;
    volatile boolean flag = true;
    int button=0;

    void setup() {
    mySerial.begin(1200);

    pinMode(inputPin,INPUT_PULLUP);
    }

    void loop() {
    goToSleep();
    if(button == 1){
    mySerial.println(1111);
    flag=true;
    }
    if(button == 0 && flag == true){
    mySerial.println(0000);
    flag=false;
    }
    }

    void goToSleep(){
    set_sleep_mode(SLEEP_MODE_IDLE);
    attachInterrupt(0, ReadButton, CHANGE); // контакт D2
    sleep_mode(); // включить энергосберегающий режим

    }

    void ReadButton(){
    detachInterrupt(0);
    button = !digitalRead(inputPin);
    }

    rx:
    #include
    #include

    SoftwareSerial mySerial(4,3); //tx, rx
    volatile boolean flag = false;

    void setup() {
    mySerial.begin(1200);

    pinMode(13,OUTPUT);
    }

    void loop() {
    deepSleep();
    if(mySerial.available() > 1){
    int input = mySerial.parseInt(); // read serial input and convert to integer
    if(input == 1111){
    flag=true;
    }
    if(input == 0000){
    flag=false;
    }
    mySerial.flush();//clear the buffer for unwanted inputs
    }
    }

    void deepSleep(){
    set_sleep_mode(SLEEP_MODE_STANDBY);
    attachInterrupt(1, myItrpt, CHANGE); // контакт D3
    digitalWrite(13,flag);
    sleep_mode(); // включить энергосберегающий режим
    // Теперь микроконтроллер простаивает

    }

    void myItrpt(){
    detachInterrupt(0);
    }

    вот решил начать постепенно, с малого. тут я кнопкой на передатчике просто включаю и выключаю светодиод на приемнике. вроде работает, но с задержкой

    ОтветитьУдалить
  7. LowPower версии 1.81 работает с ATMega168 без проблем, компилируется из Arduino IDE без ошибок. Но потребление по сравнению с ATMega328P в режимах сна в несколько раз выше.

    ОтветитьУдалить
    Ответы
    1. Ошибка при компиляции для atmega168 была исправлена в версии библиотеки 1.8 от 4 октября 2018.

      Удалить
    2. Добавлю к моему предыдущему сообщению.
      Оказывается, не все так однозначно с ATMega168. Есть две платы Pro Mini ATmega168 16MHz с отпаянными стабилизатором и индикатором. Энергопотребление почему-то значительно отличается. Например, с внутренним генератором на 1 МГц в режиме полного сна потребление соотв. 1,5 и 0,09 мкА (питание 3,3 В).
      Платы покупались вместе, но партии контроллеров разные: 168PA MU1235 и 168PA MU1028 (эта которая лучше).
      0,09 мкА - очень хороший результат, хотя и не исключительный, у меня есть несколько плат ATMega328P с примерно таким же током.

      Удалить
  8. эта библиотека не работает с 88 мегой...
    не компилирует, пишет ошибку

    ОтветитьУдалить
  9. вот:
    C:\Users\Alexandr\Documents\Arduino\libraries\LowPower-master/LowPower.h:148:6: error: #error "Please ensure chosen MCU is either 88, 168, 168P, 328P, 32U4, 2560 or 256RFR2."
    #error "Please ensure chosen MCU is either 88, 168, 168P, 328P, 32U4, 2560 or 256RFR2."
    А должна =работать с 88..
    168 и 328 компилирует.
    с 88 достаточно указать библиотеку и все - выскакивает ошибка.

    ОтветитьУдалить
  10. 1.8.15 ide
    скажите, у вас компилируется что нибудь с данной библиотекой и на 88 мегу?
    а, да, использую https://mcudude.github.io/MiniCore/package_MCUdude_MiniCore_index.json
    указал в нстройках, появились платы, выбрал 88 мегу, прошил загрузчик, пробовал много разных скетчей - работает, светодиод мигает ит.д. И вот нужно теперь запусктить в экономном режиме, но засада.. сам разобраться не смогу.

    ОтветитьУдалить
    Ответы
    1. не подскажите в чем дело? Как можно решить мою проблему?

      Удалить