Спящий режим
Микросхема стала кушать слишком много энергии?
Перестало хватать мелочи на батарейки?
Тогда спящий режим идёт к Вам!
Проблема ненасытной микросхемы возникла, когда маленькое устройство – генератор одноразовых паролей – сначала даже не смогло включиться от батарейки cr2032, а потом и вовсе покалечило программатор – сгорел светодиодик, не выдержав большого тока. Тогда проблема отложилась – кое-как, с понижением частоты работы, с выключением подсветки у дисплея и прочих плясок, а то сдавать надо было в тот же день, разбираться времени не было – но работала кроха считанные минуты…
Но вот теперь пора все-таки разобраться!
Посмотрим, почему микросхема вообще кушает энергию: в ней есть много-много транзисторов, работающих в так называемом ключевом режиме (по типу обычной кнопки). Основное потребление мощности идет в момент переключения ключа (здесь когда-нибудь будет ссылка на статейку "как устроена микросхема"), то есть чем меньше переключений - тем меньше потребляет чип. Переключения происходят в основном по тактовому импульсу, соответственно, чем меньше их число (тактовая частота), тем меньше потребление. При этом нужно помнить, что точность осциллятора (генератора наших тиков-тактов) тем выше, чем выше частота - именно поэтому во многих микросхемах есть внутренний делитель на 8 частоты осциллятора, и тогда, с одной стороны, снижается частота работы микросхемы, а следовательно, и энергопотребление, а с другой - точность остается достаточной для работы с такими чувствительными к косякам точности устройствам, как последовательный интерфейс USART.
Если же оставить все как есть, то сам чип на 8 МГц потребляет примерно 10,2 мА. В щелочной же пальчиковой батарейке - 1700-3000 мА * ч. Получается, что микросхема проработает от 17 часов до 12 суток - а правильную "большую" батарейку нужно поискать! (а в плоской литиевой, которую мы использовали - вообще 210 мА * ч, горе-печаль-беда Т_Т)
В общем, вариант "оставить все как есть" совершенно не прокатывает для автономных устройств, таких как пульты от телевизора, дистанционные выключатели, беспроводные звонки, устройства охраны, часы и метеостанции с ЖКИ, и примочки для LaserTag ˆˆ
Есть достаточно способов снизить потребление микросхемы:
- уменьшить частоту осциллятора
- вообще выключить осциллятор
- уменьшить количество переключающихся вентелей, отключив ядро и/или дополнительные генераторы и таймеры
- отключить аналоговую периферию, которая требует кучу энергии
- перевести какие только можно ножки в Z-состояние (здесь когда-нибудь будет ссылка на статейку "как устроена микросхема")
Часть этих способов автоматически выполняется при включении спящего режима. Рассмотрим их подробнее:
- Idle – в этом режиме отключается CPU – процессор, наш обработчик команд – и, соответственно, flash-память. При этом остаются включенными последовательный интерфейс SPI, USART, аналоговый компаратор, аналого-цифровой преобразователь (ADC), интерфейс TWI – он же I2C, все таймеры, в том числе и сторжевой, ну и система прерываний. Если нам что-нибудь не нужно – компаратор, АЦП или сторжевой таймер, то до засыпания можно отключить это ручками, и сэкономим ещё больше.
Этот режим буквально создан для работы с периферией, когда нужно быстро проснуться по команде от внешних интерфейсов и не тормозить. Получается даже не сон, а так... легкая дрема
- Power-Down - тут останавливается практически всё, кроме обработки внешних прерываний, интерфейса TWI и сторжевого таймера.
Вызывать в таком состоянии прерывания могут только внешний сброс-reset, сброс по сторжевому таймеру, при провале напряжения. Также возможны прерывания при работе TWI и внешние прерывания.
Большинство счётчиков и таймеров тут тоже останавливаются – таким образом, в таком режиме с микросхемой могут работать только асинхронные интерфейсы.
В общем, тут чип засыпает намертво, проснуться самому ему достаточно проблематично, а уж если разбудили - просыпаться он будет мучительно долго, пропуская мимо ножек всю информацию от прерываний, например(
- 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
Рис. 2. Регистр MCUCR в ATtiny 2313 и ATmega 8/32
Нам потребуются биты SM0, SM1 И SM2, если он есть - чтобы задать тип спящего режима. Так, например, в 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