Главная chevron_right Уроки chevron_right Электроника chevron_right Руководство по Low Power Design на Arduino

Руководство по Low Power Design на Arduino

Полное руководство по снижению энергопотребления Arduino: режимы сна, внешние события, управление периферией, выбор источника питания и продвинутые техники.

Цель Low Power — снизить потребление энергии устройством, управляя его поведением так, чтобы как можно дольше продлить срок работы от батареи. Устройства, подключённые напрямую к стационарному источнику питания, как правило, не нуждаются в подобных техниках. Другое дело — автономные гаджеты на батарейках: здесь каждый миллиампер на счету.

Практики из этого руководства применимы к любым платам Arduino. Например, платы на базе процессора Arm Cortex-M0 32-bit SAMD21 поддерживают аппаратные функции низкого энергопотребления. Платы Arduino серии SAMD21 с беспроводным протоколом LoRa®, оснащённые модулем Murata CMWX1ZZABZ от MKR WAN 1310, в сочетании с техниками Low Power способны работать очень долго без подзарядки. Список всех плат Arduino можно найти на странице Arduino Documentation Hardware.

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

Для работы с режимами низкого энергопотребления на платах Arduino установите библиотеку Arduino Low Power через менеджер библиотек Arduino IDE. Она ориентирована прежде всего на платы семейства MKR.

  • Откройте Sketch → Include Library → Manage Libraries, введите в поиске "Arduino Low Power" и установите последнюю версию.
  • Если нужно обновить поддержку конкретной платы, зайдите в Tools → Board → Boards Manager и найдите нужное семейство.

Скачать различные версии Arduino IDE можно по ссылкам ниже:

Дополнительные материалы по работе с Arduino IDE:

Техники снижения энергопотребления

Существует несколько основных способов уменьшить потребление энергии микроконтроллером:

  • Режим сна (Sleep Mode)
  • Внешние события (External Events)
  • ADC
  • Режим ожидания (Stand-by)

Режим сна

Самый эффективный способ сэкономить энергию — отправить процессор спать. Deep Sleep (глубокий сон) отключает большинство внутренних модулей и обеспечивает максимальную экономию. Его можно настроить с таймером: устройство проснётся через заданный промежуток времени. Есть и Light Sleep (лёгкий сон) — часть модулей остаётся включённой, чтобы поддерживать необходимые процессы и отслеживать состояние внешних устройств при пробуждении.

Внутри этих режимов выделяют Stand-By и Idle — они дают разный уровень контроля над переходом в режим низкого энергопотребления.

Stand-By — один из самых экономичных режимов для плат на базе SAMD21. Он останавливает все тактовые источники и переводит регуляторы напряжения в режим низкого потребления. Генераторы могут находиться в трёх состояниях: остановлены, работают постоянно или запускаются по запросу периферии. Устройство уходит в глубокий сон при активном WFI (Wait For Interrupt). Разбудить его может прерывание или WDT (Watchdog Timer).

Idle похож на Stand-By, но периферия продолжает работать при активном WFI — это означает более высокое потребление. Тактовые источники можно оставить включёнными или отключить — решение за разработчиком. Пробуждение также происходит по прерыванию или WDT.

Внешние события для пробуждения из режима сна

Устройство не всегда должно просыпаться по таймеру — иногда нужно реагировать на события во внешней среде. Внешние события — это условия, при выполнении которых микроконтроллер выходит из режима сна. Чаще всего источниками таких событий служат ADC (Analog to Digital Converter), периферийные интерфейсы типа UART и порты I/O.

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

Пробуждение по ADC (Analog to Digital Converter)

Пробуждение по ADC — частный случай внешнего события. Оно срабатывает при изменении напряжения на аналоговом входе платы. Смысл изменения напряжения зависит от задачи: например, падение до нижнего порога может означать, что подключённое устройство выключилось, а рост до верхнего — что оно включилось. Как именно использовать этот сигнал для управления режимами сна — задача разработчика программного обеспечения.

Источники питания и скорость саморазряда

Мало сэкономить энергию внутри микроконтроллера — важно ещё понимать, как ведёт себя сам источник питания. Если батарея саморазряжается быстрее, чем устройство потребляет ток в режиме сна, все усилия по оптимизации теряют смысл.

Разные типы батарей имеют разную ёмкость и скорость саморазряда, причём разряд не линейный: в начале он идёт быстрее. Температура окружающей среды тоже существенно влияет на поведение батареи. Всё это нужно учитывать при проектировании.

Несколько ориентиров для сравнения:

  • CR2032: ёмкость ~210 mAH, саморазряд ~1% в месяц.
  • NiMH AAA: ёмкость ~900 mAH, саморазряд ~30% в месяц.
  • NiMH AA: ёмкость ~2400 mAH, саморазряд ~30% в месяц.
  • Li-Ion: ёмкость ~4400 mAH, саморазряд ~10% в месяц.

Omnienergy CR2032 Discharge Characteristics at Different Loads

Характеристики разряда CR2032 при различных нагрузках (ось Y — напряжение в В, ось X — время разряда в ч)

Для расчёта эквивалентного тока саморазряда используется следующая формула:

Self Discharging Rate Formula

Формула расчёта эквивалентного тока саморазряда: Эквивалентный ток = (Ежемесячная скорость саморазряда × Ёмкость батареи) / (100 × 24 × 30)

Эта формула позволяет выразить скорость саморазряда в виде непрерывного тока потребления — так удобнее сравнивать её с реальным потреблением устройства.

Помимо ёмкости и скорости саморазряда, важен и форм-фактор батареи: он определяет габариты устройства и ограничивает выбор источников питания для компактных решений.

Примеры Low-Power приложений

Простейший пример Low Power

  • Необходимое оборудование: любая плата Arduino семейства MKR на базе SAMD21

Самый простой способ задействовать режим сна. Светодиод используется как индикатор: горит — устройство активно, не горит — спит. Устройство уходит в сон на 5 секунд.

#include "ArduinoLowPower.h"

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
  LowPower.sleep(5000);
}

Чтобы включить Deep Sleep, достаточно изменить одну строку:

//LowPower.sleep(5000);
LowPower.deepSleep(5000);

Эта строка переводит устройство в режим глубокого сна сразу после включения. Обратите внимание: в примере вызов помещён внутрь loop(), но его можно перенести и в setup(). Помните, что setup() выполняется первым — именно здесь происходит вся первоначальная настройка платы. Поэтому перед написанием кода рекомендуется сначала продумать архитектуру программы.

Пример Low Power с внешними событиями

  • Необходимое оборудование: любая плата Arduino семейства MKR на базе SAMD21

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

#include "ArduinoLowPower.h"

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin, mode);
  LowPower.attachInterruptWakeup(pin, callback, mode);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000); 
  LowPower.sleep(10000);
}

void callback() {
  // Эта функция вызывается один раз при пробуждении устройства
  // Здесь можно выполнять небольшие операции (например, изменять переменные, используемые в loop)
  // Не вызывайте delay() и долгие функции — этот код выполняется в контексте прерывания
}

Здесь нужно вызвать функцию LowPower.attachInterruptWakeup(pin, callback, mode), указав пин, функцию-обработчик (callback) и режим срабатывания. Режим определяет, какое изменение сигнала считается событием:

  • FALLING — сигнал падает (отрицательный фронт);
  • RISING — сигнал растёт (положительный фронт);
  • CHANGE — любое изменение.

Для pinMode(pin, mode) доступны режимы INPUT, OUTPUT и INPUT_PULLUP. Полный пример с внешними событиями можно найти в Arduino IDE: Examples → Arduino Low Power → ExternalWakeup.

#include "ArduinoLowPower.h"

// Номер последовательности мигания
// Объявить volatile, так как значение изменяется внутри прерывания
volatile int repetitions = 1;

// Пин для запуска пробуждения
const int pin = 8;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  // Установить пин 8 как INPUT_PULLUP, чтобы избежать ложных пробуждений
  pinMode(pin, INPUT_PULLUP);
  // Подключить прерывание пробуждения на пине 8, вызывающее repetitionsIncrease при пробуждении устройства
  LowPower.attachInterruptWakeup(pin, repetitionsIncrease, CHANGE);
}

void loop() {
  for (int i = 0; i < repetitions; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }
  // Запускает бесконечный сон (устройство пробудится только от зарегистрированных источников пробуждения)
  // Потребление тока микросхемы значительно снизится
  LowPower.sleep();
}

void repetitionsIncrease() {
  // Эта функция вызывается один раз при пробуждении устройства
  // Здесь можно выполнять небольшие операции (например, изменять переменные, используемые в loop)
  // Не вызывайте delay() и долгие функции — этот код выполняется в контексте прерывания
  repetitions ++;
}

Пример Low Power с пробуждением по ADC

  • Необходимое оборудование: любая плата Arduino семейства MKR на базе SAMD21

Небольшая модификация предыдущего примера позволяет настроить ADC как источник пробуждения — по заданному диапазону напряжения на аналоговом входе. Пример находится в Examples → Arduino Low Power → AdcWakeup.

#include "ArduinoLowPower.h"

// Номер последовательности мигания
// Объявить volatile, так как значение изменяется внутри прерывания
volatile int repetitions = 1;

// Пин для запуска пробуждения
const int pin = A0;
// Чувствительность к изменениям напряжения
const int margin = 10;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin, INPUT);
}

void loop() {
  for (int i = 0; i < repetitions; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }

  // Считать напряжение на пине ADC
  int value = analogRead(pin);

  // Задать диапазон вокруг этого значения
  uint16_t lo = max(value - margin, 0);
  uint16_t hi = min(value + margin, UINT16_MAX); 

  // Подключить прерывание ADC на пине A0, вызывающее repetitionsIncrease, когда напряжение выходит за заданный диапазон
  // Вызывать непосредственно перед LowPower.sleep(), так как функция перенастраивает ADC внутри
  LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi);

  // Запускает бесконечный сон (устройство пробудится только от зарегистрированных источников пробуждения)
  // Потребление тока микросхемы значительно снизится
  LowPower.sleep();

  // Отключить прерывание ADC. Вызывать сразу после LowPower.sleep(), так как функция восстанавливает конфигурацию ADC после пробуждения
  LowPower.detachAdcInterrupt();
}

void repetitionsIncrease() {
  // Эта функция вызывается один раз при пробуждении устройства
  // Здесь можно выполнять небольшие операции (например, изменять переменные, используемые в loop)
  // Не вызывайте delay() и долгие функции — этот код выполняется в контексте прерывания
  repetitions ++;
}

Здесь задаётся окно напряжений, в котором аналоговый пин A0 будет вызывать прерывание:

uint16_t lo = max(value - margin, 0);
uint16_t hi = min(value + margin, UINT16_MAX);

Настройка прерывания ADC выполняется внутри loop(), чтобы можно было пересконфигурировать его прямо перед уходом в сон:

LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi);

После пробуждения по прерыванию ADC нужно отсоединить прерывание ADC, чтобы вернуть пину конфигурацию ADC:

LowPower.detachAdcInterrupt();

Функции-обработчики (callback) вызываются при пробуждении из сна. Хорошая практика — избегать внутри них delay() и долгих операций. Это позволяет строить неблокирующую (Non-Blocking) архитектуру, которая одновременно экономит энергию и остаётся отзывчивой.

Пример Low Power с передатчиком LoRa®

  • Необходимое оборудование: MKR WAN 1300/1310 (встроенный модуль Murata)

Подробный пример работы с LoRa® на MKR WAN 1310 и модулем Murata — this documentation

Этот пример демонстрирует MKR WAN1300/1310 в роли удалённого передатчика, который периодически отправляет сигнал о своей работоспособности (beacon). Задача — симулировать устройство, которое транслирует данные через определённые интервалы и при этом должно работать как можно дольше. Приёмник остаётся стационарным. Передатчик переводит в режим сна как SAMD21, так и встроенный модуль Murata — чтобы не тратить энергию впустую.

Подробнее о библиотеке LoRa — в репозитории this library на GitHub.

// Библиотека Low Power
#include "ArduinoLowPower.h"

// Библиотека LoRa
#include <SPI.h>
#include <LoRa.h>

// Содержимое пакета LoRa 
char* message = "Hello LoRa!";

void setup() {
  Serial.begin(9600);
  while (!Serial);

  // Настройка LoRa
  Serial.println(F("LoRa Sender"));
  if (!LoRa.begin(868E6)) {
    Serial.println(F("Starting LoRa failed!"));
    while (1);
  } else {
    Serial.println(F("Starting LoRa Successful!"));
  }
}

void loop() {
  LoRa_Packet_Sender();
  GoToSleep();
}

// Задача LoRa
void LoRa_Packet_Sender() {
  Serial.print(F("Sending packet: "));
  Serial.println(message);

  // Отправить пакет
  LoRa.beginPacket();
  LoRa.print(message);
  LoRa.endPacket();

  // Перевод модуля LoRa в режим сна 
  Serial.println(F("LoRa Going in Sleep"));
  LoRa.sleep();
}

// Задача сна 
void GoToSleep(){
  Serial.println(F("MKR WAN 1310 - Going in Sleep"));
  LowPower.deepSleep(20000);
}

Важно понимать: библиотека Low Power управляет только микроконтроллером. Внешние модули, такие как Murata на MKR WAN 1310, нужно переводить в сон отдельно — через собственный API модуля.

Если используются внешние модули (например, Murata/LPWAN) или датчики — сначала переведите их в режим сна, и только потом отправляйте MCU. Иначе устройство не перейдёт в полноценный режим сна и максимальная экономия энергии будет недостижима. Это касается и периферийных интерфейсов — TWI и SPI.

Простой пример обнаружения низкого напряжения

  • Необходимое оборудование: любая плата Arduino

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

/*
Low Power — обнаружение низкого напряжения — пример конфигурации для SAMD21
*/

// Ручное управление питанием 
#include "ArduinoLowPower.h"

float voltValue, battery_volt, battery_percentage;
float minimal_voltage = 1800;
float battery_voltage_ref = 3.3;

void setup() {
  Serial.begin(57600);
  delay(100);

  // Установка индикатора низкого питания 
  pinMode(LED_BUILTIN, OUTPUT);

  // Стандартное аналоговое опорное напряжение 3.3V
  analogReference(AR_DEFAULT);

  // Настройка разрешения 12 бит
  analogReadResolution(12);
}

void loop() { 
  // Чтение с пина батареи
  voltValue = analogRead(A0);

  // Вычислить текущий уровень напряжения
  battery_volt = ((voltValue*battery_voltage_ref)/4095)*1000;
  
  // Уровень заряда батареи в процентах
  battery_percentage = 100*abs((battery_volt - minimal_voltage)/((battery_voltage_ref*1000) - minimal_voltage));

  Serial.print(F("Battery: "));
  Serial.print(battery_percentage);
  Serial.println(F(" % "));
  
  if (battery_volt <= minimal_voltage){
    //Световое уведомление об обнаружении низкого напряжения
    lowBatteryWarning();
  }

  delay(2000);
  
  // Переход в режим Low Power на 20 секунд 
  LowPower.deepSleep(20000);
}

// Индикатор низкого заряда батареи
void lowBatteryWarning(){
  digitalWrite(LED_BUILTIN, HIGH);  
  delay (1);             
  digitalWrite(LED_BUILTIN, LOW);    
  delay (999);             
}

Четыре ключевых параметра конфигурации:

  • analogReference() — задаёт опорное напряжение для аналогового входа.
  • analogReadResolution() — задаёт разрядность значения, возвращаемого функцией analogRead().
  • analogRead() — выбирает аналоговый пин для считывания.
  • Делитель разрядности: 4095 для 12-битного разрешения (MKR WAN 1310) или 1023 для 10-битного.

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

Продвинутый пример обнаружения низкого напряжения описан в последнем разделе этого руководства.

Продвинутые техники Low Power на Arduino

Следующие методы применимы к любым платам Arduino и позволяют выжать максимум из аппаратных возможностей. Они дают гибкость при проектировании энергоэффективных систем.

Низкая частота и низкое напряжение

Два мощных инструмента дополнительной экономии — снижение тактовой частоты и рабочего напряжения процессора.

Частоту важно подбирать с учётом задач: некоторые модули, например WiFi, требуют минимального уровня тактирования. Слишком низкая частота приведёт к некорректной работе или полному отказу этих модулей. В ряде случаев для работы на очень низких частотах потребуется внешний кварцевый генератор. Поэтому оптимальная стратегия — не фиксировать частоту на минимуме, а подбирать её под конкретную архитектуру ПО.

Напряжение питания: большинство плат работают на 5 В или 3,3 В. Если внешние компоненты позволяют, переход на 3,3 В заметно снизит общее потребление. Уже только эти два параметра — частота и напряжение — могут вдвое сократить энергопотребление системы.

Выбрать нужный вариант можно в Arduino IDE через меню Tools:

Voltage and Frequency Configuration of the Microcontroller

Выбор напряжения и частоты микроконтроллера в меню Tools Arduino IDE

Управление энергопотреблением на уровне микроконтроллера

В большинстве случаев класса library достаточно для достижения низкого потребления. Но если нужно отключать отдельные компоненты микроконтроллера независимо — на помощь приходят регистры управления питанием.

Микроконтроллер SAMD21 — Power Reduction Mode

Для SAMD21 microcontrollers, используемого в платах MKR Family, предусмотрен Power Reduction Mode.

Режим снижения энергопотребления связан с контроллером энергонезависимой памяти (NVM). Регистр CTRLB управляет этим режимом. Когда NVM-контроллер переходит в режим сниженного потребления, соответствующий бит устанавливается в регистре статуса STATUS.PRM. Для входа и выхода из режима используются команды Set и Clear Power Reduction Mode.

CTRLB Register of SAMD21 Microcontroller

Регистр CTRLB микроконтроллера SAMD21

SLEEPPRM Bit from CTRLB Register of SAMD21 Microcontroller

Биты SLEEPPRM регистра CTRLB: управляют режимом снижения энергопотребления NVM во время сна

Микроконтроллер Atmega328P — Power Reduction Register

Платы Classic Family и Arduino Nano на микроконтроллере Atmega328P используют Power Reduction Register (PRR).

Этот регистр позволяет индивидуально отключать: TWI, SPI, USART0, таймеры/счётчики и аналого-цифровой преобразователь. Важный нюанс: перед отключением бита ADC в PRR необходимо сбросить ADCSRA в ноль — иначе ADC зависнет в активном состоянии.

Power Reduction Register

Регистр снижения энергопотребления (PRR) микроконтроллера ATmega328P

Периферия и внутренние модули

Отключение неиспользуемых внутренних модулей — ещё один эффективный способ снизить потребление. Кандидаты на отключение: SPI, I2C, Serial, ADC (ADC), Brown-out Detection и Watchdog Timer.

Периферийные интерфейсы SPI, I2C, Serial часто используются для работы с датчиками. Если какой-то интерфейс не задействован в конкретном приложении — его лучше отключить. Перед отключением интерфейса рекомендуется отправить датчик в режим сна через этот же интерфейс: сначала команда датчику, затем отключение периферии.

ADC (ADC) может служить источником пробуждения, но если эта функция не нужна — ADC стоит отключить: он потребляет заметный ток.

Brown-out Detection сравнивает напряжение питания с порогом и защищает процессор от работы при слишком низком напряжении. Watchdog Timer следит за тем, чтобы микроконтроллер не завис: при зависании он инициирует сброс. Оба модуля можно отключить вручную для экономии энергии — но только после того, как программа тщательно проверена на стабильность.

Внешние устройства

Если к плате подключены SD-карты, датчики и другие внешние устройства, их питание можно коммутировать через MOSFET-транзисторы. Это позволяет полностью обесточить периферию во время глубокого сна и максимально сократить потребление. Особенно эффективно при периодическом цикле «работа → сон → работа».

Low-Dropout регуляторы напряжения

Если схема требует регулятора напряжения, выбирайте Low-Dropout (LDO) регуляторы с минимальным током покоя (Quiescent Current). Это существенно снизит потребление системы в режиме сна. Нужные характеристики всегда указаны в даташите производителя.

Бюджет энергопотребления

Полезная практика — вести таблицу бюджета мощности всей системы. Это даёт наглядное представление о том, как распределяется энергия между компонентами, и помогает найти точки оптимизации — например, увеличить интервал сна или отключить ещё один модуль.

Измерение потребления тока

После всех оптимизаций необходимо измерить реальное потребление системы. Воспользуйтесь Multimeter Basics (раздел Reading Current), чтобы точно определить ток и убедиться в корректной работе всех режимов сна.

Обнаружение низкого напряжения (продвинутый вариант)

Даже самое оптимизированное устройство когда-нибудь разрядится. Важно, чтобы оно успело «почувствовать» критически низкий уровень заряда и корректно завершить работу — например, сохранить данные или отправить сигнал тревоги.

Для оценки времени автономной работы используйте калькулятор: https://www.omnicalculator.com/other/battery-life

Для этого применяется метод управления питанием микроконтроллеров avr, на которых основаны Atmega328P и которые встречаются, например, в Arduino Nano и Classic Family. Метод обнаружения низкого напряжения, разработанный Nick Gammon, Retrolefty и Coding Badly, совмещается с возможностью перехода в режим сна.

// Nick Gammon
// Код предоставлен «Coding Badly» и «Retrolefty» с форума Arduino

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

const long InternalReferenceVoltage = 1062;  // Скорректируйте это значение под внутреннее опорное напряжение BG вашей платы

void setup() {
  Serial.begin(57600);
  delay(100);

  // Установка индикатора низкого питания 
  pinMode(LED_BUILTIN, OUTPUT);
  
  // Предварительный режим Low Power 
  resetWatchdog(); // На случай срабатывания WDT
}

void loop() { 
  // Включение периферии, таймеров и ADC
  manual_periph_ctrl(1);
  
  // Обнаружение уровня напряжения
  Serial.println(getBandgap());
  // Обнаружение низкого напряжения на уровне 3V (300)
  if (getBandgap() < 300){
    //Световое уведомление об обнаружении низкого напряжения
    lowBatteryWarning();
    
    // Отключение периферии, таймеров и ADC 
    manual_periph_ctrl(0);
  }

  // Обработчик низкого уровня
  i2c_switch_off();
  Manual_LowPower_Mode(1);    
}

// Фиксированный вариант на 8 секунд — отключает всё
void Manual_LowPower_Mode(uint8_t multiplier){
  delay(70);                                              // Требуется не менее 68 мс запаса для времени загрузки модуля
  for(int i = 0; i <= multiplier; i++){                   // Множитель для тика Power Down
    Deep_Sleep_Manual();
  }
}

/*
Задача обнаружения низкого напряжения
*/
// Nick Gammon
// Код предоставлен «Coding Badly» и «Retrolefty» с форума Arduino
// Результаты — Vcc * 100
// Например, 5V соответствует 500
int getBandgap(){
  // REFS0: выбирает внешнее опорное напряжение AVcc
  // MUX3 MUX2 MUX1: выбирает 1.1V (VBG)  
   ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
   ADCSRA |= bit( ADSC );  // Запустить преобразование
   while (ADCSRA & bit (ADSC)){ 
   }  // Ждать завершения преобразования
   int results = (((InternalReferenceVoltage * 1024) / ADC) + 5) / 10; 
   return results;
} // Конец getBandgap

/*
Задачи, связанные с Low Power 
*/
// Включение Watchdog Timer 
void WatchdogEnable() {
  // Сбросить различные флаги «сброса»
  MCUSR = 0;     
  // Разрешить изменения, отключить сброс
  WDTCSR = bit (WDCE) | bit (WDE);
  // Установить режим прерывания и интервал 
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // Установить WDIE и задержку 8 секунд
  wdt_reset();  // Сбросить watchdog

  // Отключить ADC
  ADCSRA = 0;  

  // Готов к переходу в сон
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  // В лучшем случае даёт ~0.4 мА 
  noInterrupts();
  sleep_enable();

  // Программно отключить brown-out detection
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  interrupts();       // Гарантирует выполнение следующей инструкции
  sleep_cpu ();  

  // Отменить сон в качестве меры предосторожности
  sleep_disable();
} 

void resetWatchdog (){
  // Сбросить различные флаги «сброса»
  MCUSR = 0;     
  // Разрешить изменения, отключить сброс, сбросить существующее прерывание
  WDTCSR = bit (WDCE) | bit (WDE) | bit (WDIF);
  // Установить режим прерывания и интервал (WDE должен быть изменён с 1 на 0)
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // Установить WDIE и задержку 8 секунд
  // Сбросить watchdog
  wdt_reset();  
}  // Конец resetWatchdog

void i2c_switch_off(){
  // Отключить I2C
  TWCR &= ~(bit(TWEN) | bit(TWIE) | bit(TWEA));

  // Отключить подтягивающие резисторы I2C
  digitalWrite (A4, LOW);
  digitalWrite (A5, LOW);
}

// Выполняется примерно 8 секунд, считая как один цикл
void Deep_Sleep_Manual(){
  // Состояние CORE
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // Отключить ADC
  power_all_disable ();  // Отключить питание ADC, Timer 0 и 1, последовательного интерфейса

  // Прерывания не учитываются, как требует ADXL
  noInterrupts ();       // Начинается временна́я последовательность
  resetWatchdog ();      // Подготовить watchdog
  sleep_enable ();       // Готов к переходу в сон
  interrupts ();         // Прерывания теперь необходимы
  sleep_cpu ();          // Сон                
  sleep_disable ();      // Мера предосторожности
  power_all_enable ();   // Включить всё обратно
  
}  // Конец goToSleep 

// Ручной контроллер периферии
void manual_periph_ctrl(uint8_t selector){
  byte old_ADCSRA = ADCSRA;
  // Отключить ADC
  ADCSRA = 0; 
   
  if (selector == 0){
    power_adc_disable();
    power_spi_disable();
    power_timer0_disable();
    power_timer1_disable();
    power_timer2_disable();
    power_twi_disable();

    UCSR0B &= ~bit (RXEN0);  // Отключить приёмник
    UCSR0B &= ~bit (TXEN0);  // Отключить передатчик
  } 
  if (selector >= 1){
    power_adc_enable();
    power_spi_enable();
    power_timer0_enable();
    power_timer1_enable();
    power_timer2_enable();
    power_twi_enable();
    
    UCSR0B |= bit (RXEN0);  // Включить приёмник
    UCSR0B |= bit (TXEN0);  // Включить передатчик
  } 

  ADCSRA = old_ADCSRA;
}

// Индикатор низкого заряда батареи
void lowBatteryWarning(){
  digitalWrite(LED_BUILTIN, HIGH);  
  delay (1);             
  digitalWrite(LED_BUILTIN, LOW);    
  delay (999);             
}

Чтобы найти внутреннее опорное напряжение, можно воспользоваться одним из двух скриптов. После запуска измерьте напряжение на пине AREF мультиметром и умножьте результат на 1000 — это и будет значение Internal Reference Voltage для подстановки в код.

  • Через регистр ADMUX:
// Найти внутреннее опорное напряжение 1.1 на пине AREF
void setup ()
{
  ADMUX = bit (REFS0) | bit (REFS1);
}

void loop () { }
  • Через функции analogReference и analogRead:
// Найти внутреннее опорное напряжение 1.1 на пине AREF
void setup ()
{
  analogReference (INTERNAL);
  analogRead (A0);  // Принудительно включить опорное напряжение
}

void loop () { }

Подробнее о системах Low Power: https://www.gammon.com.au/power

Торговые марки

  • LoRa® — зарегистрированная торговая марка Semtech Corporation.