Спящий режим

Микросхема стала кушать слишком много энергии?
Перестало хватать мелочи на батарейки?
Тогда спящий режим идёт к Вам!
Проблема ненасытной микросхемы возникла, когда маленькое устройство – генератор одноразовых паролей – сначала даже не смогло включиться от батарейки cr2032, а потом и вовсе покалечило программатор – сгорел светодиодик, не выдержав большого тока. Тогда проблема отложилась – кое-как, с понижением частоты работы, с выключением подсветки у дисплея и прочих плясок, а то сдавать надо было в тот же день, разбираться времени не было – но работала кроха считанные минуты…
Но вот теперь пора все-таки разобраться!

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

Если же оставить все как есть, то сам чип на 8 МГц потребляет примерно 10,2 мА. В щелочной же пальчиковой батарейке - 1700-3000 мА * ч. Получается, что микросхема проработает от 17 часов до 12 суток - а правильную "большую" батарейку нужно поискать! (а в плоской литиевой, которую мы использовали - вообще 210 мА * ч, горе-печаль-беда Т_Т)

В общем, вариант "оставить все как есть" совершенно не прокатывает для автономных устройств, таких как пульты от телевизора, дистанционные выключатели, беспроводные звонки, устройства охраны, часы и метеостанции с ЖКИ, и примочки для LaserTag ˆˆ

Есть достаточно способов снизить потребление микросхемы:

  1. уменьшить частоту осциллятора
  2. вообще выключить осциллятор
  3. уменьшить количество переключающихся вентелей, отключив ядро и/или дополнительные генераторы и таймеры
  4. отключить аналоговую периферию, которая требует кучу энергии
  5. перевести какие только можно ножки в Z-состояние (здесь когда-нибудь будет ссылка на статейку "как устроена микросхема")

Часть этих способов автоматически выполняется при включении спящего режима. Рассмотрим их подробнее:

  • Idle – в этом режиме отключается CPU – процессор, наш обработчик команд – и, соответственно, flash-память. При этом остаются включенными последовательный интерфейс SPI, USART, аналоговый компаратор, аналого-цифровой преобразователь (ADC), интерфейс TWI – он же I2C, все таймеры, в том числе и сторжевой, ну и система прерываний. Если нам что-нибудь не нужно – компаратор, АЦП или сторжевой таймер, то до засыпания можно отключить это ручками, и сэкономим ещё больше.
    Этот режим буквально создан для работы с периферией, когда нужно быстро проснуться по команде от внешних интерфейсов и не тормозить. Получается даже не сон, а так... легкая дрема
  • Power-Down - тут останавливается практически всё, кроме обработки внешних прерываний, интерфейса TWI и сторжевого таймера.
    Вызывать в таком состоянии прерывания могут только внешний сброс-reset, сброс по сторжевому таймеру, при провале напряжения. Также возможны прерывания при работе TWI и внешние прерывания.
    Большинство счётчиков и таймеров тут тоже останавливаются – таким образом, в таком режиме с микросхемой могут работать только асинхронные интерфейсы.
    В общем, тут чип засыпает намертво, проснуться самому ему достаточно проблематично, а уж если разбудили - просыпаться он будет мучительно долго, пропуская мимо ножек всю информацию от прерываний, например(
Idle и Power-Down встречаются наиболее часто.
  • Power-save - этот режим похож на Power-down, но если таймер2 установлен в асинхронном режиме - бит ACCP.AS2 = 1 - то он будет работать; если таймер2 так не установлен, то даташит советует использовать Power-down - при пробуждении регистр этого таймера будет не определён.
    Здесь микросхема погружена в глубокий сон , но где-то глубоко в подсознании чип про себя отмечает, сколько времени он проспал. Этот режим хорош для устройств, требующих знания о частоте выхода из спящего режима, например, для часов. Но, к сожалению, он не подходит для точного измерения временных отрезков между событиями.
  • Standby - он тоже похож на Power-down, но используется при работе от внешнего источника тактирования и оставляет включённым осциллятор, а включенный осцилятор означает меньше времени на раскачку - быстрее старт. Этот режим используется, если нужно поймать какое-либо событие - например, в клавиатуре чипу надо проснуться до того, как пользователь отпустит клавишу. Ещё один красивый пример - автоспуск пленочного фотоаппарата на молнию.
  • Extended Standby - похож на Power-Save, но оставляет включённым осциллятор. Вот этот режим как раз для устройств, которым нужно точно знать, сколько времени прошло после входа в режим сна - например, для измерителя скорости вращения колеса велосипеда.

Наконец, режим, стоящий особняком:

  • ADC Noise Reduction. Этот режим останавливает процессор, но оставляет работать АЦП, систему внешних прерываний, интерфейс TWI, сторжевой таймер и таймер/счётчик2; соответственно, останавливаются только часы I/O, CPU и FLASH. Все это предназначено для того, чтобы уменьшить внутренние помехи (возникающие при переключении транзисторов самой микросхемы) при работе АЦП - так получаются измерения более высокой точности.

Получаем следующие таблички:

Дрема - чип просыпается практически мгновенно:

Почти вся периферия выключена Выключена только самая "шумная" периферия
Idle Noise Reduction

Сон - сильно уменьшает потребление энергии:

Просыпаемся медленно Просыпаемся быстро
не знаем сколько времени прошло
и можем проснуться только извне
Power-down Stand By
знаем сколько времени прошло
и можем проснуться по таймеру
Power-Save Extended Stand By

А вот и принцип работы программы:

Алгоритм работы программ
Рис. 1. Работа программы со спящим режимом и программы без экономии электропитания

Получается, что в большинстве режимов микросхема вообще не понимает, что была в спящем режиме: только-только выполнила последнюю перед sleep; команду, а тут уже какое-то прерывание дергает.

А теперь посмотрим техническую часть)

За работу со спящими режимами отвечает регистр MCUCR

Регистр MCUCR
Рис. 2. Регистр MCUCR в ATtiny 2313 и ATmega 8/32

Нам потребуются биты SM0, SM1 И SM2, если он есть - чтобы задать тип спящего режима. Так, например, в ATmega8 спящие режимы задаются так:

Определение спящих режимов в ATmega8
Рис. 3. Определение спящих режимов в ATmega8

После определения с типом режима остается только разрешить его - установить бит SE в 1.

Ну и, наконец, останется перевести в нужный момент микросхемку баинькать - ассемблеровской командой asm sleep;, нужно будет только продумать выход)

Вот пример программки для atmega8, у которой на выводе INT0 (PD2) есть кнопка, а на PB0 висит светодиодик; нажимаем на кнопку - светодиодик немножко горит и уходит в спящий режим до следующего нажатия.

//переопределяем типы
typedef unsigned char byte;

sbit ddrButton at ddD2_bit;  //кнопка генерации
sbit pinButton at pinD2_bit;
sbit portButton at portD2_bit;

sbit ddrLight at ddB0_bit;  //вывод для светодиода, на выход с подтяжкой, включается 0 по кнопке
sbit portLight at portB0_bit;

byte flagButton = 0; //флаг нажатия кнопки; нажата - 1

void INT0_interrupt() iv IVT_ADDR_INT0 //нужно написать как минимум пустую функцию -
                                       //потому что компилятор сам не создает. Иначе не работает
{
   flagButton = 1;
}

//обработка нажатия кнопки - с учётом дребезга
void buttonLight()
{
   if(flagButton) //если нажата кнопка
   {
      portLight = 0;   //включаем светодиод
      delay_ms(500);
      portLight = 1;   //выключаем светодиод
      flagButton = 0;
   }
}

void main()
{
   //инициализация всех используемых портов
   ddrB =  0;
   portB = 0;
   ddrD =  0;
   portD = 0;
   //инициализация кнопки - на вход с подтяжкой
   portButton = 1;
   ddrButton = 0;
   //инициализация светодиодика, которая включается по 0 и нажатию кнопки - на выход и в 1
   portLight = 1;
   ddrLight = 1;

   //настраиваем на внешние прерывания
   GICR.INT0 = 1; //разрешаем внешнее прерывание INT0
   MCUCR.ISC00 = 0;  //прерывание генерируется по логическому 0 на INT0
   MCUCR.ISC01 = 0;
   asm sei;//SREG.B7 = 1; //разрешаем прерывания в принципе (бит I); команды аналогичны
   //определяем спящий режим  :   SM2-SM1-SM0 = 000 - режим Idle; 010 - PowerDown
   MCUCR.SM2 = 0;
   MCUCR.SM1 = 1;
   MCUCR.SM0 = 0;
   MCUCR.SE = 1;  //разрешили спящий режим
   while(1)
   {
      buttonLight();
      asm sleep;  //в спящий режим
   }
}

Программка работает и в режиме Idle, и в режиме Power-down. Но при этом в процессе написания и отладки было выяснено, что в даташите не просто так написано, что в режиме Power-down схема долго просыпается. Изначально прерывание было поставлено по нисходящему фронту, при переходе вывода INT0 с 1 на 0 (ISC01 = 1, ISC00 = 0). Так вот, в режиме Idle все работало прекрасно, а вот в режиме Power-Down фронт менялся до того, как микросхема очухается - соответственно, никакой обработки прерывания и не происходило. В общем, надо следить, чтобы в режимах, где схема долго возвращается в полноценное рабочее состояние, источник прерывания действовал от пробудки до самого этого полноценного рабочего состояния.

ИТОГО: в режиме Idle можно снизить потребление до 5 мА, в режиме Power-down - до 1 мкА! Пляшем от счастья)

 

Автор - Moriam