Ардуино, энергосбережение

Arduino
Автор темы, Сержант
Сержант
Аватара
Arduino
Автор темы, Сержант
Сержант
Сообщения: 64
Зарегистрирован: 19 апреля 2017
С нами: 6 лет 11 месяцев

#1 Arduino » 27 декабря 2020, 23:02

Большинство плат Arduino поддерживают входное напряжение до 12В, при этом сам микроконтроллер питается напряжением 5В. Таким образом, учитывая, что в большинстве плат Arduino установлены линейные регуляторы напряжения, получается, что при питании платы напряжением больше 5В, значительная часть мощности будет рассеиваться в тепло.

Использование режимов энергосбережения.
Все микроконтроллеры AVR на которых основаны большинство плат Arduino поддерживают различные режимы энергосбережения. Рассмотрим такие режимы для микроконтроллера ATmega328P, на котором основаны платы Arduino UNO, Arduino Nano, Arduino Pro Mini и некоторые другие:

IDLE mode (режим ожидания)

В данном режиме приостанавливается только работа процессора, в то время как остальная периферия (интерфейсы ввода-вывода, таймеры, счетчики, компараторы, система прерываний) продолжает работать. Данный режим обеспечивает самое низкое снижение потребления энергии, но его преимущество в очень быстрой реакции на события, приводящие к пробуждению микроконтроллера. Выход из режима IDLE возможен как по внешнему, так и по внутреннему прерыванию.

Power-Down mode (режим глубокого сна)

Этот режим обеспечивает максимальное энергосбережение за счет отключения тактирования всех узлов микроконтроллера, работающих в синхронном режиме. В рабочем состоянии остаются только сторожевой таймер, система обработки внешних прерываний и блок сравнения адреса модуля TWI. Пробуждение из данного режима возможно в результате возникновения следующих прерываний: от сторожевого таймера, по совпадению адреса от интерфейса TWI, прерывание изменения уровня, или внешнего прерывания INT0 или INT1.

Power Save mode (режим энергосбережения)

Отличается от режима Power-Down тем, что таймер/счетчик 2 продолжает свою работу как в синхронном, так и в асинхронном режиме. Пробуждение из этого режима возможно теми же прерываниями что и из режима Power-Down, а также прерыванием от таймера/счетчика 2.

Standby mode (режим ожидания)

Этот режим идентичен режиму работы Power-Down, за исключением того, что продолжает работать тактовый генератор. За счет этого пробуждение микроконтроллера происходит гораздо быстрее.

Для того чтобы начать использовать данные режимы энергосбережения, необходимо подключить библиотеку sleep.h:

Код: Выделить всё

#include <avr/sleep.h>   

После этого нам станут доступны две простые функции – set_sleep_mode(); и sleep_mode();.

С помощью функции set_sleep_mode(); происходит выбор необходимого режима энергосбережения. Соответственно есть 4 интересующих нас аргумента этой функции для каждого из рассмотренных режимов работы:

Код: Выделить всё

set_sleep_mode(SLEEP_MODE_IDLE);

set_sleep_mode(SLEEP_MODE_PWR_DOWN);

set_sleep_mode(SLEEP_MODE_PWR_SAVE);

set_sleep_mode(SLEEP_MODE_STANDBY); 

После того как был задан необходимый режим энергосбережения, мы можем воспользоваться функцией sleep_mode(); для перевода микроконтроллера в этот режим.

Как видно, ввести микроконтроллер в режим энергосбережения совсем несложно, но помимо этого его необходимо еще и выводить из этого режима для совершения полезной работы. Рассмотрим вариант использования прерывания от сторожевого таймера для этих целей. Для работы со сторожевым таймером необходимо подключить соответствующую библиотеку wdt.h:

Код: Выделить всё

#include <avr/wdt.h>   

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

Код: Выделить всё

ISR (WDT_vect) {


Для работы со сторожевым таймером понадобятся две функции – wdt_enable(); и wdt_disable();.

Функция wdt_enable(); имеет один аргумент, устанавливающий интервал срабатывания сторожевого таймера. Для этого доступны 10 констант:

Код: Выделить всё

wdt_enable(WDTO_15MS);

wdt_enable(WDTO_30MS);

wdt_enable(WDTO_60MS);

wdt_enable(WDTO_120MS);

wdt_enable(WDTO_250MS);

wdt_enable(WDTO_500MS);

wdt_enable(WDTO_1S);

wdt_enable(WDTO_2S);

wdt_enable(WDTO_4S);

wdt_enable(WDTO_8S); 

Кроме этого, необходимо разрешить прерывание от сторожевого таймера. Это можно сделать с помощью установки бита WDIE регистра WDTCSR: WDTCSR |= (1 << WDIE);.

Рассмотрим пример программы, в котором будем просто моргать встроенным светодиодом с высокой энергоэффективностью:

Код: Выделить всё

#include <avr/sleep.h>

#include <avr/wdt.h>



void setup() {

     pinMode(LED_BUILTIN, OUTPUT);

set_sleep_mode(SLEEP_MODE_PWR_DOWN); //выбираем в качестве режима энергосбережения Power-Down mode

}



void loop() {

     digitalWrite(LED_BUILTIN, LOW); //гасим светодиод

     wdt_enable(WDTO_1S); //устанавливаем таймер на 1 секунду

WDTCSR |= (<< WDIE); //разрешаем прерывание

sleep_mode(); //переходим в режим сна, через секунду попадаем в  функцию обработчика прерывания ISR (WDT_vect)

digitalWrite(LED_BUILTIN, HIGH); //зажигаем светодиод

wdt_enable(WDTO_120MS); //устанавливаем таймер на 120 мс

WDTCSR |= (<< WDIE); //разрешаем прерывание

sleep_mode(); //переходим в режим сна, через 120 мс попадаем в функцию обработчика прерывания ISR (WDT_vect)

     }



     ISR (WDT_vect) {

wdt_disable(); //прерывание сработало, отключаем таймер, после чего продолжается выполнение основной программы

     

Как видно, при каждом входе в режим сна, необходимо выполнять по 4 действия – включать таймер и устанавливать время его срабатывания, разрешать прерывание, входить в режим сна, и после выхода из него – отключать таймер. Кроме того, нет возможности устанавливать свои интервалы срабатывания таймера. Оба этих недостатка можно устранить если использовать библиотеку Narcoleptic.
Использование библиотеки Narcoleptic

Данную библиотеку создал Питер Кнайт, скачать ее можно по адресу https://code.google.com/p/narcoleptic/.

Эта библиотека позволяет вводить микроконтроллер в режим сна на определенное время с помощью одной функции – Narcoleptic.delay();. Аргументом данной функции является время в миллисекундах – используется точно так же как и стандартная функция delay();.

Рассмотрим ту же программу что и ранее, но с использованием данной библиотеки:

Код: Выделить всё

#include <Narcoleptic.h>



void setup() {

          pinMode(LED_BUILTIN, OUTPUT);

}



void loop() {

          digitalWrite(LED_BUILTIN, LOW);

          Narcoleptic.delay(1000);

          digitalWrite(LED_BUILTIN, HIGH);

          Narcoleptic.delay(120);


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

Выключение компонентов микроконтроллера

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

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

Для того чтобы воспользоваться данным методом необходимо подключить библиотеку power.h:

Код: Выделить всё

#include <avr/power.h>   

После этого нам будет доступен ряд функций для включения и отключения отдельных модулей периферии микроконтроллера:

Код: Выделить всё


Функция выключения    Функция включения    Описание модуля
power_aca_disable
()    power_aca_enable()    Аналоговый компаратор порта А.
power_adc_disable()    power_adc_enable()    АЦП.
power_adca_disable()    power_adca_enable()    АЦП порта А.
power_evsys_disable()    power_evsys_enable()    Модуль EVSYS
power_hiresc_disable
()    power_hiresc_enable()    Модуль HIRES порта C.
power_lcd_disable()    power_lcd_enable()    Модуль LCD.
power_pga_disable()    power_pga_enable()    Усилитель с программируемым коэффициентом усиления.
power_pscr_disable()    power_pscr_enable()    Контроллер пониженной мощности.
power_psc0_disable()    power_psc0_enable()    0 Контроллер уровня мощности.
power_psc1_disable()    power_psc1_enable()    1 Контроллер уровня мощности.
power_psc2_disable()    power_psc2_enable()    2 Контроллер уровня мощности.
power_ram0_disable()    power_ram0_enable()    SRAM блок 0.
power_ram1_disable()    power_ram1_enable()    SRAM блок 1.
power_ram2_disable()    power_ram2_enable()    SRAM блок 2.
power_ram3_disable()    power_ram3_enable()    SRAM блок 3.
power_rtc_disable()    power_rtc_enable()    Модуль часов реального времени.
power_spi_disable()    power_spi_enable()    Интерфейс SPI
power_spic_disable
()    power_spic_enable()    Интерфейс SPI порта C
power_spid_disable
()    power_spid_enable()    Интерфейс SPI порта D
power_tc0c_disable
()    power_tc0c_enable()    Таймер/счетчик 0 порта C
power_tc0d_disable
()    power_tc0d_enable()    Таймер/счетчик 0 порта D
power_tc0e_disable
()    power_tc0e_enable()    Таймер/счетчик 0 порта E
power_tc0f_disable
()    power_tc0f_enable()    Таймер/счетчик 0 порта F
power_tc1c_disable
()    power_tc1c_enable()    Таймер/счетчик 1 порта C
power_twic_disable
()    power_twic_enable()    Интерфейс I2C порта C
power_twie_disable
()    power_twie_enable()    Интерфейс I2C порта E
power_timer0_disable
()    power_timer0_enable()    Таймер 0
power_timer1_disable
()    power_timer1_enable()    Таймер 1
power_timer2_disable
()    power_timer2_enable()    Таймер 2
power_timer3_disable
()    power_timer3_enable()    Таймер 3
power_timer4_disable
()    power_timer4_enable()    Таймер 4
power_timer5_disable
()    power_timer5_enable()    Таймер 5
power_twi_disable
()    power_twi_enable()    Интерфейс I2C
power_usart_disable
()    power_usart_enable()    Интерфейс USART
power_usart0_disable
()    power_usart0_enable()    Интерфейс USART 0
power_usart1_disable
()    power_usart1_enable()    Интерфейс USART 1
power_usart2_disable
()    power_usart2_enable()    Интерфейс USART 2
power_usart3_disable
()    power_usart3_enable()    Интерфейс USART 3
power_usartc0_disable
()    power_usartc0_enable()    Интерфейс USART 0 порта C
power_usartd0_disable
()    power_usartd0_enable()    Интерфейс USART 0 порта D
power_usarte0_disable
()    power_usarte0_enable()    Интерфейс USART 0 порта E
power_usartf0_disable
()    power_usartf0_enable()    Интерфейс USART 0 порта F
power_usb_disable
()    power_usb_enable()    Интерфейс USB
power_usi_disable
()    power_usi_enable()    Интерфейс USI
power_vadc_disable
()    power_vadc_enable()    Модуль напряжения АЦП
power_all_disable
()    power_all_enable()    Все модули

Доступность данных функций будет определяться типом используемого микроконтроллера, и тем какая периферия в нем присутствует. Для того чтобы не изучать документацию на каждый конкретный контроллер, можно отключать при запуске всю периферию контроллера с помощью функции power_all_disable(), а затем отдельно включать необходимые модули.

Для примера давайте добавим в нашу первую программу отправку данных через Serial порт, а всю остальную периферию микроконтроллера отключим:

Код: Выделить всё

#include <avr/sleep.h>

#include <avr/wdt.h>

#include <avr/power.h>



long int i 0;



void setup() {

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

power_usart0_enable(); //включаем USART0 для Arduino Mega

power_timer0_enable(); //включаем таймер 0 (он необходим для нормальной работы USART)

Serial.begin(9600); //устанавливаем скорость Serial в 9600 бод

pinMode(LED_BUILTINOUTPUT);

set_sleep_mode(SLEEP_MODE_PWR_DOWN);

}



void loop() {

i++;

Serial.println(i); //Отправляем данные в Serial порт

delay(10); //Ждем пока завершится отправка данных через USART, иначе контроллер перейдет в режим сна до того как данные успеют отправиться полностью

digitalWrite(LED_BUILTINLOW);

wdt_enable(WDTO_1S);

WDTCSR |= (<< WDIE);

sleep_mode();

digitalWrite(LED_BUILTINHIGH);

wdt_enable(WDTO_120MS);

WDTCSR |= (<< WDIE);

sleep_mode();

}



ISR (WDT_vect) {

wdt_disable();


Снижение тактовой частоты.

Потребление любого микроконтроллера сильно зависит от частоты его тактирования, и снижая ее, мы можем добиться значительного снижения энергопотребления. В микроконтроллерах AVR имеется возможность программного изменения предделителя частоты тактирования. А для простоты работы с ним мы будем использовать специальную библиотеку Prescaler.h, скачать которую можно по адресу https://github.com/fschaefer/Prescaler:

Код: Выделить всё

#include “Prescaler.h” 

Изменение предделителя тактирования производится с помощью функции setClockPrescaler(); имеющей один аргумент, отвечающий за величину предделителя. Существует 9 констант в качестве аргументов для данной функции:

Код: Выделить всё

setClockPrescaler(CLOCK_PRESCALER_1);

setClockPrescaler(CLOCK_PRESCALER_2);

setClockPrescaler(CLOCK_PRESCALER_4);

setClockPrescaler(CLOCK_PRESCALER_8);

setClockPrescaler(CLOCK_PRESCALER_16);

setClockPrescaler(CLOCK_PRESCALER_32);

setClockPrescaler(CLOCK_PRESCALER_64);

setClockPrescaler(CLOCK_PRESCALER_128);

setClockPrescaler(CLOCK_PRESCALER_256); 

В зависимости от аргумента, данная функция снижает тактовую частоту в несколько раз (CLOCK_PRESCALER_16 означает что базовая тактовая частота микроконтроллера будет снижена в 16 раз).

Перед использованием данной функции, необходимо отметить, что правильность работы всей периферии сильно зависит от тактовой частоты, и при ее снижении – большинство функций микроконтроллера, завязанные на времени, будут работать неправильно (таймеры, PWM, USART, I2C и т.д.). Кроме того неправильно будут работать стандартные функции millis(); и delay();. Но данная библиотека предоставляет замену этим функциям в виде функций trueMillis(); и trueDelay();.

Рассмотрим вариант применения снижения тактовой частоты на примере работы с Serial интерфейсом:

Код: Выделить всё

#include "prescaler.h"



int i 0;



void setup() {

pinMode(LED_BUILTINOUTPUT);

Serial.begin(9600);

setClockPrescaler(CLOCK_PRESCALER_256); //Понижаем тактовую частоту в 256 раз

}



void loop() {

digitalWrite(LED_BUILTINHIGH); //включаем светодиод

trueDelay(200); //ждем 200 мс с учетом пониженной частоты

digitalWrite(LED_BUILTINLOW); //выключаем светодиод

trueDelay(1000); //ждем 1 секунду с учетом пониженной частоты

i++;

if (
== 5) { //каждый 5 цикл отправляем данные в Serial порт

setClockPrescaler(CLOCK_PRESCALER_1); //Повышаем частоту тактирования до стандартной

Serial.println("I'm alive!"); //Отправляем данные

delay(15); //Ждем пока данные отправятся

setClockPrescaler(CLOCK_PRESCALER_256); //Обратно снижаем частоту тактирования

0;

}


Как видно, перед тем как использовать Serial порт, необходимо сначала повысить частоту тактирования до стандартной, и только потом отправлять данные. То же самое касается и приема данных – необходимо позаботиться о возвращении стандартной частоты тактирования еще до того, как данные будут отправлены на наш микроконтроллер, иначе они будут приняты неверно. То же касается и остальной периферии, завязанной на временных промежутках.


Arduino
Автор темы, Сержант
Сержант
Аватара
Arduino
Автор темы, Сержант
Сержант
Сообщения: 64
Зарегистрирован: 19 апреля 2017
С нами: 6 лет 11 месяцев

#2 Arduino » 27 декабря 2020, 23:08

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

Меж тем, потребление тока микроконтроллерами в режимах ожидания может быть настолько мало, что обесточивать схему нет никакой необходимости: если правильно ввести микроконтроллер в спящий режим, то он сможет отслеживать нажатие кнопок, почти не потребляя тока.
Например, микроконтроллеры AVR в таком режиме кушают меньше микроампера. При работе от батарейки CR2032, которая часто используется в фонариках, настольных часах и компьютерах, и ёмкость которой составляет около 225 мА*ч, заряда хватит более чем на 25 лет ожидания.

Ввести микроконтроллер в режим ожидания не сложно. Если программа пишется на C с использованием AVR toolchain (например, в Atmel Studio), то выглядеть это может так:


Код: Выделить всё

#include <avr/sleep.h>

sleep_enable
(); // Разрешаем использование спящего режима
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Выбираем тип спящего режима
sleep_cpu(); // Входим в спящий режим 


Доступные режимы сна различаются для каждой из моделей микроконтроллеров, но почти в каждом есть режим IDLE, когда работает вся периферия, просто останавливается ЦПУ, и режим POWER DOWN, отключающий большинство периферийных модулей.

Разумеется, помимо того, чтобы войти в спящий режим, нужно ещё суметь из него выйти. Для этого должны быть настроены прерывания, на которые МК будет продолжать реагировать. В частности, во многих глубоких режимах сна, осцилляторы таймеров останавливаются, соответственно использовать их для пробуждения не получится. А вот прерывание по изменению логического уровня на входе работает почти всегда, где оно доступно. Соответственно, если мы хотим проснуться по нажатию кнопки, то хорошей идеей будет отслеживать изменение пина, на котором висит кнопка.

Однако, просто отправить процессор в спящий режим — мало для экономии батарейки. Работающая периферия может потреблять сотни микроампер. Рационально будет отключить всё что было включено ранее (таймеры, АЦП, USART, TWI, SPI и т.д.). В некоторых моделях МК присутствует Power Reduction Register (PRR) запись в него определённых бит отключает питание соответствующей периферии.

Ниже рассмотрим кратенько наиболее частые источники утечек:
1) Аналоговый компаратор. Он присутствует на многих МК. И он изначально включен. Рекомендую всегда сразу его выключать, если он не требуется. Для этого в регистре ACSR нужно установить бит ACD:
ACSR |= (1 << ACD)
2) Неподключенные пины. Если вход не используется, то различные помехи могут провоцировать на нём смену логических уровней, а смена состояний полевых транзисторов, как известно, требует энергии. Такой шум может приводить к потреблению нескольких сотен микроампер. Самый простой способ этого избежать: включить pull-up резистор на соответствующем выводе порта (т.е. записать в соответствующий бит регистра PORTx единицу, пока DDRx – ноль).
3) Подключенные пины. Если подтягивающий резистор включен на выводе порта, который внешней периферией подтягивается к низкому уровню, то через него будет уходить до 125 микроампер.
4) Brown-Out Detector (BOD). Очень полезная схема, которая сбрасывает микроконтроллер, если напряжение питания падает ниже заданного уровня. Тем не менее, сама она жрёт, хоть и не много, но существенно для долголетия нашей маленькой батареечки (15-25мкА). К сожалению, на многих моделях AVR, если BOD включен, то избавиться от него так просто не получится. Однако, МК с функцией PicoPower (они содержат в своём названии букву P, например ATmega88PA) позволяют отключить BOD на время сна. Эта процедура представляет некоторую сложность, поскольку требует записи определённых данных в регистры и перехода в режим сна в течение всего нескольких тактов процессора. Поэтому подготовить записываемые данные следует заранее. На C это может выглядеть вот так:

Код: Выделить всё

cli(); // отключение прерываний
uint8_t x = (MCUCR & ~(<< BODSE)) | (<< BODS); // подготовка бит
MCUCR | (<< BODSE); // процедура отключения BOD
MCUCR x;
sei(); // включение прерываний
sleep_cpu(); // вход в режим сна 


Контроль за возможными источниками утечек тока, а также активное и разумное использование спящих режимов позволят эффективно реализовывать схемы, работающие от батарей.


  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Программирование»

Кто сейчас на форуме (по активности за 5 минут)

Сейчас этот раздел просматривают: 3 гостя