Найти тему
Разумный мир

Микроконтроллеры для начинающих. Часть 38. Использование режимов сна AVR

Оглавление

В предыдущей статье мы кратко, можно сказать в обзорном режиме, познакомились с различными режимами работы и энергопотребления микроконтроллеров. А теперь познакомимся поближе с использованием режимов сна для различных микроконтроллеров. И начнем с AVR.

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

Мы рассмотрим прямую работу с регистрами микроконтроллера, так как это позволяет избежать влияния компилятора. Но, поскольку очень многие используют компилятор avr-gcc, познакомимся и с предоставляемыми им функциями и возможностями.

Режимы сна

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

Режимы сна ATtiny2313A. Из документации
Режимы сна ATtiny2313A. Из документации
Режимы сна ATtiny13A. Из документации
Режимы сна ATtiny13A. Из документации
Режимы сна ATmegax8. Из документации
Режимы сна ATmegax8. Из документации

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

Кроме того, от используемого режима сна зависит, какими способами можно разбудить микроконтроллер.

Об основных особенностях режимов сна (энергосбережения) я уже говорил в предыдущей статье. А вот про приведенные здесь таблицы нужно рассказать немного подробнее.

Группа столбцов Active Clock Domains показывает, какая часть системы распределения тактового сигнала остается активной. Это мы будем рассматривать отдельно, в последующих статьях (да, в микроконтроллерах все очень взаимозависимо!), по небольшая иллюстрация нам сейчас будет полезна

Распределение тактовых сигналов ATmega8. Из документации
Распределение тактовых сигналов ATmega8. Из документации

Вокруг AVR Clock Control Unit вы можете увидеть те самые тактовые сигналы, которые и входят в группу Active Clock Domains в приведенных таблицах.

Теперь мы можем увидеть и понять основные зависимости работы микроконтроллера в режиме сна.

  • Во всех режимах сна прекращается подача тактового сигнала (останавливается работа!) CPU Core и RAM. Обратите внимание, что остановка ОЗУ не означает выключение его питания. А значит, не приводит к потере хранящейся в ОЗУ информации. Не теряется и содержимое внутренних регистров процессора.
  • Во всех режимах сна останавливается работа Flash и EEPROM.
  • Во всех режимах сна, кроме Idle, останавливается работа портов ввода/вывода и таймеров с синхронных режимах. Обратите внимание, что остановка не приводит к изменению состояния выводов портов. И направление (вход или выход), и установленные уровни (1 или 0) сохраняются. Остановка таймеров в синхронных режимах не приводит к изменению состояния их счетчиков.
  • Работа таймеров в асинхронных режимах, например, Timer2 в ATmegax8, останавливается отдельно и только в режимах сна Power Down и Standby.
  • Работа АЦП останавливается во всех режимах сна, кроме Idle и ADC Noise Reduction. Собственно говоря, основное различие между этими режимами заключается в том, что в режиме ADC Noise Reduction дополнительно останавливается работа портов и таймеров в синхронных режимах. Это делается для уменьшения помех преобразованию.

Следующая группа столбцов, Oscilators, показывает останавливается ли работа тактовых генераторов. В ATtiny есть только системный тактовый генератор, а в ATmega и дополнительный тактовый генератор для работы таймеров в асинхронных режимах. Нас сейчас не интересует, какой именно вариант системного тактового генератора выбран (и как он выбирается). Останов тактового генератора не только снижает энергопотребление, но и ограничивает количество способов пробуждения микроконтроллера и увеличивает время пробуждения.

Способы пробуждения микроконтроллера показаны в группе столбцов Wake-up Sources. Особых комментариев здесь не требуется. Прерывания будут рассматриваться отдельно, как я уже не раз говорил. Порты ввода-вывода нам тоже еще только предстоит изучать.

А вот про WDT, сторожевой таймер, сказать пару слов стоит. Если работа WDT не запрещена, он может пробудить микроконтроллер из любого, самого глубокого, режима сна. Кроме того, он работает независимо от остальных модулей микроконтроллера. Основное назначение WDT - предотвращение зависаний микроконтроллера. Но может использоваться и для других целей. Мы еще будем рассматривать работу сторожевых таймеров отдельно.

Наконец последний столбец, Software BOD Disable, показывает возможность временного отключения мониторинга питания в некоторых режимах сна. Что такое BOR (Brown Out Reset) и зачем нужен я говорил в статье "Микроконтроллеры для начинающих. Часть 35. Немного о электрических параметрах микроконтроллеров". А BOD это Brown Out Detector, то есть, собственно схема контроля напряжения питания. BOD может быть включен/выключен и настроен глобально при прошивке микроконтроллера (через fuses). Во включенном состоянии он постоянно потребляет некоторый ток. При необходимости BOD может быть временно отключен программно при переходе в режим сна.

Управление выбором режимов сна

Раз режимы сна могут быть разными, а засыпание производится единственной командой SLEEP, должен быть какой то способ выбора режима.

Разрешение перехода в режим сна

Начнем с того, что переход в режим сна может быть вообще запрещен. Причем по умолчанию, при включении питания, переход в режим сна именно запрещен. За разрешение/запрещение возможности перехода в сна отвечает бит SE. Если бит SE=0 (значение по умолчанию), то команда SLEEP просто ничего не делает. Если же бит SE=1, то команда SLEEP переводит микроконтроллер в режим сна.

Но тут есть один важный нюанс! Дело в том, что бит SE может находиться в разных регистрах, и в разных местах регистров, для разных микроконтроллеров. Подробности, как и всегда, следует искать в документации на микроконтроллер.

Для рассматриваемых нами в статье ATtiny13A и ATtiny2313A бит SE располагается в регистре MCUCR (MCU Control Register), бит 5. А для ATmegax8 в регистре SMCR ( Sleep Mode Control Register), бит 0.

Регистр MCUCR микроконтроллера ATtiny2313A. Из документации
Регистр MCUCR микроконтроллера ATtiny2313A. Из документации
Регистр MCUCR микроконтроллера ATtiny13A. Из документации
Регистр MCUCR микроконтроллера ATtiny13A. Из документации
Регистр SMCR микроконтроллера ATmegax8. Из документации
Регистр SMCR микроконтроллера ATmegax8. Из документации

Выбор режима сна

Если переход в режим сна разрешен, то конкретный вариант сна выбирается битами SMx, которые располагаются в тех же регистрах, что и бит SE. В нашем случае это SM0 и SM1 для ATtiny и SM0, SM1, SM2 для ATmega. Иллюстрации приведены выше.

Обратите внимание на еще один нюанс! Биты SMx могут находиться в разных местах регистров. Это хорошо видно на примере ATtiny13 и ATtiny2313, я не случайно выбрал для примеров именно эти микроконтроллеры.

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

Выбор режима сна для ATtiny2313A. Из документации
Выбор режима сна для ATtiny2313A. Из документации
Выбор режима сна для ATmegax8. Из документации
Выбор режима сна для ATmegax8. Из документации

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

Обратите внимание, что по умолчанию выбран режим сна Idle. Если вы ограничитесь лишь разрешением переходом в сон, то команда SLEEP переведет микроконтроллер именно в этот режим.

Временное запрещение BOD

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

Временным отключением мониторинга питания в режиме сна занимаются два бита - BODS и BODSE.

Собственно отключением BOD занимается бит BODS. Бит BODSE является стробом для записи состояния BODS во внутренний триггер монитора питания. По умолчанию монитор питания работает в режимах сна.

По традиции, эти два бита могут располагаться в разных регистрах.

Регистр BODCR в ATtiny2313. Из документации.
Регистр BODCR в ATtiny2313. Из документации.

В ATtiny13A тоже используется регистр BODCR, и его формат точно такой же. Но вот адрес регистра другой - 0х30.

В ATmegax8 используется регистр MCUCR.

Регистр MCUCR в ATmegax8. Из документации.
Регистр MCUCR в ATmegax8. Из документации.

И биты располагаются в другом месте этого регистра.

Алгоритм временного отключения BOD при переходе в сон такой:

  1. Установить биты BODS и BODSE в состояние логической 1
  2. В течении 4 тактовых циклов после шага 1 установить бит BODSE в состояние логического 0.
  3. Теперь у вас есть 3 тактовых цикла, что бы выполнить команду SLEEP и перевести микроконтроллер в сон с отключенным BOD.

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

Обратите внимание, что в отличии от всего описанного ранее, процедуру временного запрета BOD нужно выполнять каждый раз при переводе микроконтроллера в режим сна, если это действительно требуется.

Дополнительные возможности ограничения потребления в режиме сна

Этот вопрос не касается непосредственно режимов сна, он касается управления питанием, поэтому я лишь упомяну об этой возможности.

В предыдущей статье я говорил, что в микроконтроллере есть регистры PRR с помощью которых можно отключить питание не нужных модулей. И это не просто остановка их работы, а действительно отключение питания. Исключением является TWI, у которого отключается не питания, а тактирование модуля (останов).

Соответствующий модуль отключается (питание) при установке бита в 1 в регистре PRR. Адреса и формат PRR вы можете найти в документации на микроконтроллер.

Обратите внимание, что включение и выключение питания модулей через регистр PRR выполняется независимо от режима работы микроконтроллера!

Выход из режима сна

После всех настроек переход в любой из режимов сна выполняет команда SLEEP. А вот с выходом из режима сна ситуация несколько сложнее.

Для того, что бы источник пробуждения (они указаны в таблицах в начале статьи) действительно разбудил микроконтроллер необходимо разрешить прерывания в целом, и для необходимых источников в частности. Поскольку мы еще не изучали прерывания необходимо сказать, что за разрешение прерываний в глобальном масштабе отвечает бит I регистра SREG, который должен быть установлен в 1 для разрешения прерываний. И проще всего это сделать командой SEI.

Разрешение прерываний от отдельных модулей выполняется через их управляющие регистры. Например, для АЦП в ATtiny13A прерывания разрешает бит ADIE регистра ADCSRA. А бит ADIF отражает состояние запроса прерывания. А для таймеров существует сразу несколько разных прерываний.

Не расстраивайтесь, если это пока для вас не очень понятно. Мы еще познакомимся и с прерываниями, и с остальными модулями. В микроконтроллерах действительно многие вещи взаимосвязаны, поэтому приходится постоянно заглядывать вперед и оглядываться назад.

При возникновении во время сна разрешенного прерывания микроконтроллер просыпается, но начинает выполнять программу не сразу.

Сначала дается время, с запасом, для начала работы и стабилизации частоты тактовых генераторов. Если тактовые генераторы не останавливались, то такое ожидание занимает минимум времени (зависит от настроек микроконтроллера). А вот если выбран тактовый генератор с кварцевым резонатором, то времени потребуется гораздо больше. Точное время написано в документации на микроконтроллер, но в первом приближении можно считать, это займет до 100 мс.

После этого микроконтроллер сделает еще одну небольшую паузу (4 цикла) и перейдет к обработке прерывания, которое и вызвало пробуждение. Стоит заметить, что вам не обязательно действительно делать в прерывании что то серьезное. Так для пробуждения по таймеру дополнительная обработка может быть не нужна, а вот для АЦП может выполняться чтение результата и его усреднение.

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

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

Пара слов о WDT

Использование WDT для пробуждения микроконтроллера требует дополнительного пояснения. Во первых, WDT должен быть разрешен. Во вторых, он отработает независимо от разрешения прерываний в целом, и своего прерывания в частности. Поэтому он может и разбудить микроконтроллер, и выполнить его сброс. Поэтому при использовании WDT нужно соблюдать внимательность.

Как работает WDT мы рассмотрим в отдельной статье. Пока же достаточно знать, что это специальный таймер работающий от отдельного тактового генератора. Этот таймер нужно периодически сбрасывать, что бы его счетчик не переполнялся. Если произойдет переполнение счетчика (программа зависла), то микроконтроллер будет сброшен. Это изначальная функция сторожевого таймера. Однако, в AVR микроконтроллер может не сбрасываться, вместо сброса будет сгенерировано прерывание. Это прерывание и может использоваться для пробуждения микроконтроллера.

При этом WDT может работать в нескольких режимах.

  • Сброс
  • Прерывание
  • Прерывание и сброс

При использовании WDT для выхода из режима сна подходит режим "Прерывание". В остальных режимах микроконтроллер будет неизбежно сброшен (в последнем случае после обработки прерывания).

Использование режимов сна в программах на С

Теории на сегодня хватит, переходим к практике. Как я и говорил, мы рассмотрим прямую работу с регистрами оборудования. И только потом посмотрим, что нам предлагает avg-gcc.

Сначала нам нужно описать используемые регистры микроконтроллера ("Микроконтроллеры для начинающих. Часть 34. Мост к Си. Описываем регистры аппаратуры") . В качестве примера я использую ATmegax8. То есть, вы сможете прямо использовать все это, например, для ATmega328P. После описания регистров мы можем выбирать режим сна и переключаться в него.

Вот простой пример

Пример выбора режима сна и перехода в сон для ATmega328P. Пример мой.
Пример выбора режима сна и перехода в сон для ATmega328P. Пример мой.

А вот какой код для этого примера генерирует avg-gcc

Результат компиляции примера программы.
Результат компиляции примера программы.

Если вы внимательно читали мои предыдущие статьи, то здесь для вас абсолютно все знакомо и понятно. Обратите внимание, здесь я использовал тот факт, что адресное пространства ввода-вывода является частью пространства данных (адрес SMCR в пространстве ввода-вывода 0х33, а в пространстве данных 0х53).

Я не стал показывать настройку источников пробуждения микроконтроллера и обработку прерываний, так как мы еще не изучали ни прерывания, ни другие модули микроконтроллера. Но все таки кратко покажу, как можно настроить пробуждение от АЦП, раз уж я использовал в примере режим снижения помех для него.

Разрешение прерываний от АЦП выполняется установкой в 1 бита ADIE регистра ADCSRA. Если предположить, что этот регистр у нас уже где то описан, то это можно сделать так

ADCSRA.ADIE=1;

Ну а глобально разрешить прерывания, если они по какой то причине не разрешены ранее, можно так

asm volatile ("sei");

Как видно, нет ничего сложного.

Теперь немного коснусь случая, когда биты SMx идут не подряд. Примером является ATtiny2313, где между SM0 и SM1 вклинился бит SE. В этом случае не получится описать поле выбора режима сна как единое целое. Поэтому придется описывать отдельные биты и переключать их по отдельности. Здесь тоже нет ничего сложного, поэтому отдельного примера не будет.

Отключение BOD

Как я уже говорил выше, для временного отключения BOD требуется соблюдение специальной процедуры, включая временные параметры. Кажется, что написать такое на С нельзя. Давайте посмотрим на примере ATtiny13A

-14

Здесь два варианта. Первый с простым присваиванием нужных значений бит, когда остальные биты в регистре не используются. Второй для случая, когда в регистре есть и другие используемые биты. Примером может служить MCUCR в ATmegax8. Здесь я уже использую адрес в пространстве ввода вывода, поэтому прибавляю к нему 0х20, что бы скомпенсировать преобразование адреса компилятором. А вот результат компиляции

-15

Или в виде дампа двоичного образа

-16

Видно, что в первом варианте мы уложились в требования процедуры отключения. А вот во втором... А ведь тоже уложились. Между установкой обоих бит в 1 и сбросом бита BODSE должно пройти не более 4 циклов. Это означает, что между двумя командами OUT изменяющими регистр должно быть не более 3 циклов (один цикл будет выполняться собственно вторая команда OUT). У нас между командами OUT располагается IN (1 такт) и ANDI (1 такт). То есть, нам потребовалось 3 такта при допустимых 4.

Средства avr-gcc

Приведенные выше примеры компилировались avr-gcc, но не использовали предоставляемые им макросы и функции. Если вам больше нравится использовать готовые решения, то можете подключить файл sleep.h

#include <avr/sleep.h>

И воспользоваться определенными в нем макросами и функциями, которые позволяют не вспоминать, в каком регистре какие биты находятся. Я не буду полностью описывать этот файл, скажу лишь об основных моментах.

Разрешение и запрещение режима сна выполняется макросами

sleep_enable()

sleep_disable()

которые и выполняют изменение бита SE. Выбор режима сна осуществляется макросом

set_sleep_mode(mode)

Список режимов зависит от микроконтроллера. Допустимые режимы можно найти в соответствующем файле avr/ioxxxxx.h (например, для ATmega328p это будет файл avr/iom328p.h).

Вот такие режимы, например, можно использовать для ATmega328p

SLEEP_MODE_IDLE

SLEEP_MODE_ADC

SLEEP_MODE_PWR_DOWN

SLEEP_MODE_PWR_SAVE

SLEEP_MODE_STANDBY

SLEEP_MODE_EXT_STANDBY

Теперь наш пример для ATmega328P будет выглядеть так

-17

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

-18

Есть в sleep.h и временное отключение BOD, которое выполнено в виде встроенного ассемблерного кода

sleep_bod_disable()

Ее реализация почти повторяет то, что мы получили компиляцией с С, но одну команду меньше. В качестве упражнения найдите эту разницу и попробуйте разобраться, из-за чего это получается.

Заключение

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

Почему я начал именно с режимов сна? Во первых, это логическое продолжение темы питания (экономия питания). Во вторых, при изучении других модулей микроконтроллеров мы будем постоянно касаться вопросов их работы, в том числе, в режимах сна. И нам потребуется понимание, что же такое, сон микроконтроллера.

Что касается именно AVR, то какой вариант, прямая работа с регистрами или подключение sleep.h, будете использовать именно вы, решать только вам. Я предпочитаю не использовать дополнительные уровни абстракции там, где они не являются жизненно необходимыми.

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

До новых встреч!