В самом фреймворке Arduino уход в сон реализован с помощью различных библиотек. Я же здесь рассмотрю "низкоуровневый" вариант, который может быть применен к абсолютно любому МК AVR.
МК AVR поддерживает различные режимы сна. Режимы сна реализуются с помощью отключения тактирования различных модулей входящих в состав МК. Самый легкий режим сна это:
IDLE
в этом режиме остановлено тактирование CPU и памяти FLASH. Вся остальная периферия тактируется.
Следующий режим сна связан с АЦП и называется:
ADC_NOISE_REDUCTION
в этом режиме остановлено всё, кроме тактирования АЦП и таймеров. Это сделано для того, чтобы минимизировать шумы, которые могут ухудшить результат оцифровки.
Режим, который останавливает абсолютно всё:
POWER_DOWN
Остановлено всё, кроме таймеров с внешним тактированием. К таким относятся RTC и WDT.
В различных моделях МК добавлены ещё режимы, но они являются вариациями вышеприведенных.
Как выводить МК из режима сна? По различным событиям, которые генерируют прерывания. Из абсолютно всех режимов сна можно выйти:
по таймерам WDT и RTC
по изменению логического уровня на любом пине, обозначенном PCINT. Сюда же входят пины INT0 и INT1 (в режиме POWER_DOWN rising и falling на этих пинах не работает, т.к. остановлено тактирование!!!).
по получению байта с адресом МК по шине I2C.
Из режима ADC_NOISE_REDUCTION дополнительно можно выйти по готовности результат оцифровки сигнала АЦП, по прерыванию от таймера, по флагу готовности EEPROM.
Из режима IDLE выйти можно по любому прерыванию.
Ниже я покажу, как уйти в сон и проснуться от WDT. Можно пожонглировать регистрами МК, но в каждом МК уход в сон реализован по-своему, поэтому проще воспользоваться библиотекой от Atmel. Подключим ее:
#include <avr/sleep.h>
Для ухода в сон воспользуемся следующими макросами:
set_sleep_mode(Sleep_mode); // Выбираем режим сна
sleep_cpu(); // Уходим в сон здесь
Первый макрос определяет режим сна, второй макрос разрешает сон. Если режим сна меняться не будет, то первый макрос достаточно написать один раз, второй макрос пишем в том участке кода, где необходимо уйти в сон.
Просыпаться мы будем по вачдогу. Но прежде нам надо его подготовить. В ATMega328 вачдог работает в одном из двух режимов - либо он отслеживает исполнение программы и ресетит МК, если очередной сброс счетчика вачдога пропущен (что приводит к его переполнению), либо он работает как обычный таймер и его переполнение может сгенерировать прерывание. Есть ещё комбинированный режим, который есть и в ATTiny: первое переполнение счетчика поднимает флаг переполнения и, если разрешено, генерирует прерывание. Последующее переполнение счетчика (если не последовало сброса) генерирует нулевое прерывание - сброс МК.
Итак, первое - выбираем режим работы вачдога - обычный таймер. Для этого я запишу такой макрос:
#define WDT_int_on() WDTCSR = bit (WDCE) | bit (WDE);
Установки данных битов - табличные значения.
Затем сразу необходимо "взвести" таймер. У меня это будут максимальные 8 секунд:
WDT_int_on();
WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);
бит WDIE разрешает прерывания от вачдога, иначе не проснемся никогда. Ну и теперь осталось оформить обработчик прерывания (он должен быть обязателен хотя бы пустой):
ISR (WDT_vect)
{
wdt_disable();
}
Здесь я выключаю таймер вачдога, но можно его и сбросить:
wdt_reset();
Также счетчик таймера вачдога сбрасывает любое изменение периода. Например, 64 мс:
WDTCSR = bit (WDIE) | bit (WDP1) | bit (WDP0);
Если таймер не выключается, а только лишь сбрасывается или меняется уставка периода, то макрос
WDT_int_on();
необходимо вызвать только лишь один раз.
Перед глубоким сном обязательно все "лапки" МК необходимо сконфигурировать либо на выход, либо на вход с подтяжкой ( в зависимости от подключенной периферии). Но ни в коем случае не оставлять их как не подключенными входами - любая помеха на входе заставит переключаться входной триггер Шмитта, что отразится повышенным энергопотреблением. Если всё сделали правильно, то типовое энергопотребление AVR в режиме сна bc включенным вачдогом будет составлять порядка 3-4 мкА, без вачдога - 200-300 нА. Также обязательно выключать модуль BOD с помощью фьюзов - он отъест свои законные микроамперы.