Low power run (еще один режим энергосбережения)

Есть еще один режим энергосбережения, который я не описал в своей статье про способы экономии энергии в STM8L. Его нельзя назвать «спящим режимом», потому что ядро продолжает работать, пусть и на низкой частоте. Но путем отключения самых «прожорливых» частей МК в этом режиме можно добиться потребления около 4-5мкА. Называется он — Low Power Run Mode, для простоты буду называть его LPR.


В режиме LPR МК продолжает работать, но благодаря отключению высокоскоростного тактового генератора, флеш памяти и прочих ненужных вещей, мы получаем потребление в районе 5мкА (LCD при этом был включен, см. фото).

Отключать все «ненужные вещи» приходится вручную, поэтому процесс перехода в LPR может затянуться:
1) Отключить всю неиспользуемую периферию
2) Отключить все прерывания
3) Перейти в RAM
4) Переключиться на LSI\LSE (внутренний низкоскоростной генератор/внешний часовой кварц)
5) Отключить HSI и HSE (высокоскоростные тактовые генераторы)
6) Отключить питание флеш и EEPROM памяти
7) Переключить внутренний стабилизатор напряжения в низкопотребляющий режим

Пункты с 1 по 5 можно выполнять в произвольном порядке (ну, естественно, отключать HSI надо после переключения на LSI ;-)).

Надо заметить, что в этом режиме нельзя использовать АЦП — ему необходим тактовый сигнал не меньше 1МГц. Ни LSE (32.768кГц), ни LSI (38кГц) такую частоту не дадут. Так-же скорее всего не будет работать и DAC — ему нужно около 370мкА в то время как маломощный стабилизатор может дать только 200мкА

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

Первая задача, которая стоит перед нами — загнать все функции, работающие в LPR, в оперативную память. В IAR это делается очень просто. Перед определением функции надо добавить модификатор __ramfunc.
__ramfunc void foo(void) 
{
  //Код, находящийся в RAM
};

Обозначение IN_RAM( void foo(void) ) означает абсолютно то-же самое, а этот IN_RAM прописан в библиотеке:

#define IN_RAM(a) __ramfunc a

При старте контроллера код функции foo будет копироваться из флеш памяти в RAM. Адрес, по которому будет располагаться функция, выбирается автоматически.

Процесс перехода в LPR режим начинается с переключения на низкочастотный генератор (LSI или LSE).
CLK->CKDIVR = 2; //Предделитель равен 4.
 CLK->SWCR |= CLK_SWCR_SWEN; //Начинаем переключение  
 CLK->SWR = 0x2; //Переключаемся на LSI
 while ((CLK->SWCR & CLK_SWCR_SWBSY) != 0); //Ждем, пока переключение завершится

Тут используется автоматический режим переключения между тактовыми генераторами. Сначала поднимаем бит SWEN в регистре CLK_SWCR — тем самым разрешая переключение. Затем кладем в регистр CLK_SWR код того источника, на который хотим переключиться: 0x1 — HSI, 0x2 — LSI, 0x4 — HSE, 0x8 — LSE. После этого поднимается флаг SWBSY и начинается переключение, а мы ждем его завершения, поглядывая на этот флаг.

Подробнее про систему тактирования можно прочитать в этой статье.

Когда переключение на LSI закончится, МК будет работать на частоте 38кГц/Предделитель. При старте установлен делитель на 8, а значит тактовая частота МК будет 4.75кГц. Из-за этого может не работать ЖК контроллер, который по-умолчанию тактируется от LSI (без делителя). Необходимо, чтобы частота ЖК контроллера была меньше или равна частоте МК.

После перехода на LSI, надо отключить высокоскоростной генератор — HSI. Если этого не сделать, то к потреблению прибавится сотня микроампер. Благо, отключается он в одно действие:
CLK->ICKCR &= ~CLK_ICKCR_HSION; //Отключение HSI

Следующим пунктом идет отключение питания флеш и EEPROM памяти:
FLASH->CR1 |= FLASH_CR1_EEPM; //Отключение памяти

Естественно, после этого попытка обратится к переменной в EEPROM или flash памяти приведет к ошибке. При попытке перейти на функцию, расположеную в флеш памяти произойдет тоже самое. Поэтому функции, которые нам понадобятся в LPR надо взять с собой в оперативку.

Так-же не стоит забывать и про прерывания — таблица с их векторами живет в памяти программ, которая теперь не доступна. Поэтому все прерывания надо отключить. Выбирайте любой способ на вкус:
__disable_interrupts
asm(«sim»);

После того, как память отключена, остается последний шаг — отключить основной стабилизатор напряжения (ядро STM8 питается от 1.8В через внутренний стабилизатор):
CLK->REGCSR |= CLK_REGCSR_REGOFF;

После установки этого бита стабилизатор перейдет в ULP (Ultra low power) режим.

Добро пожаловать в матрицу LPR


Что в этом режиме можно делать? Да почти тоже самое, что в обычном. Не считая того, что размер кода ограничен размером оперативки (минус стек, минус переменные), частота ограничена частотой LSI\LSE, прерывания отсутствуют, а попытка обратится к функции из флеш памяти приведет к зависанию МК.

Можно использовать почти всю периферию (за исключением ADC — он пролетает по частоте), опрашивать сенсорные кнопочки, что-нибудь считать (вообще-то считать лучше на макс. частоте), общаться по SPI / UART / I2C, и т.д. Кстати, вот эта табличка поможет прикинуть общее потребление МК с учетом запущеной периферии:



Почти любую периферию можно настроить так, чтобы она потребляла минимум тока. Например контроллер LCD после уменьшения контрастности и частоты обновления стал кушать на пару микроампер меньше.

Вместо циклов-задержек или циклов при ожидании какого-то события от периферии можно использовать режим WFE (Wait for event). В нем ядро МК останавливается до тех пор, пока выбраная периферия не сгенерирует событие. Таким событием может быть например приход байта по SPI или переполнение таймера.

Первым делом надо настроить источник события — у него должно быть разрешено соответствующее прерывание, хотя использоваться оно не будет. Затем через регистры WFE_CRx (их всего 3 штуки) выбираем источник события, который разбудит МК. Например так:
WFE->CR3 |= WFE_CR3_TIM4_EV;

Это событие от таймера №4. Полный список событий я тут приводить не буду, а пошлю вас за ним на 74 страницу RM0031.

Затем запускаем источник события (включаем таймер, или передаем бит по SPI) и выполняем инструкцию
asm("wfe");

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

На самом деле, WFE сильно экономит энергию, когда МК работает на высоких частотах. Но в LPR ядро и так почти ничего не потребляет, поэтому WFE может дать лишь пару микроампер экономии.

Кстати, в halt режим из LPR уходить нельзя — ведь пробуждение из него возможно только от прерывания, которые в LPR запрещены.

Wake up!

Теперь поговорим о том, как проснуться и выйти из LPR.

Тут повторяется та-же процедура, только в обратном порядке. Сначала включим основной стабилизатор питания:

CLK->REGCSR &= ~CLK_REGCSR_REGOFF; //Включаем основной стабилизатор
 while ((CLK->REGCSR && CLK_REGCSR_REGREADY)==0); //Ждем, пока он запустится


Запускается шарманка не сразу, поэтому перед тем, как включать все остальное, надо подождать, пока стабилизатор войдет в рабочий режим. Кстати, в ULP режиме этот стабилизатор можен выдавать ток до 200мкА, а в обычном — до 25мА.

После стабилизатора можно запускать флеш:

FLASH->CR1 &= ~FLASH_CR1_EEPM; //Включаем флеш память
 while ((CLK->REGCSR && CLK_REGCSR_EEREADY)==0); //Ждем, пока она будет готова


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

Теперь, когда память программ включена, можно переключится на HSI:

CLK->SWCR |= CLK_SWCR_SWEN; //Начинаем переключение  
 CLK->SWR = 0x1; //Переключаемся на HSI
 while ((CLK->SWCR & CLK_SWCR_SWBSY) != 0); //Ждем, пока переключение завершится 
 CLK->CKDIVR = 3; //Предделитель = 8.


Таким образом мы вернулись к изначальной частоте — 2МГц.

Разрешить прерывания и запустить нужную периферию можно в любой момент. Только первые два пункта должны выполняться по порядку, остальные — произвольно.

For Example


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



Пара уточнений для тех, кто решит покопаться в коде:

IN_RAM это тоже самое, что __ramfunc

BAR1_ON и похожие конструкции совместно с функцией LCD_bar(); рулят полоской с правой стороны дисплея. BAR0_ON — зажечь нижний сегмент, BAR1_ON — второй снизу, и т.д. Они просто записывают значения в массив t_bar. Для того, чтобы потушить всю полоску, массив надо обнулить.

Функция LCD_GLASS_Init — стандартная процедура инициализации ЖК — была изменена для уменьшения потребления. Я снизил контраст и частоту обновления.

Функция LCD_bar(); была выкинута в оперативную память, так-как используется когда флеш отключена.

Полезные ссылки


STM8L family power management — полезный аппноут, где описываются не только энергосберегающие режимы, но и другие способы снижения потребления МК.

Как использовать этот режим и использовать-ли его вообще — дело разработчика, и зависит это в первую очередь от задачи. Я за время написания этой заметки так и не придумал ни одной задачи, где-бы этот режим полностью себя оправдывал и его нельзя было-бы заменить связкой Active Halt + AWU* + Внешние прерывания.

*AWU — функция, позволяющая МК автоматически выходить из halt режима по прошествии некоторого времени. В STM8L15x реализована в составе RTC, а в STM8L101 — как самостоятельный модуль.

P.S. На заметку: Нажатая кнопка, которая подтянута к +3V резистором в 20к потребляет столько-же, сколько 30 МК STM8L152C6 работающих в Low Power Run режиме. Так-то!
  • +4
  • 15 октября 2011, 19:03
  • dcoder
  • 2
Файлы в топике: LowPowerRun.zip, DSCN2400.JPG

Комментарии (37)

RSS свернуть / развернуть
Не разобрался как уместить функции (подпрограммы) используемые для умножения, деления и т.п. в ОЗУ?
0
  • avatar
  • ZiB
  • 15 октября 2011, 20:37
Неа. и это печально, ибо придется писать свои
0
Граммар-наци mode on: «автоматически выходить из halt рЕжима». А так плюс, полюбому :). Спасибо. От какого минимального количества фруктов работать будет не проверял? :)
0
Спасибо grammar nazi. Исправил. Работает от 3-4 кусочков яблок, но не долго (пару часов). Можно еще попробовать разные конфигурации (последовательное и параллельное соединение).

Когда я был мелкий — тоже игрался с такими самодельными батарейками (запускал от них калькулятор). Дольше всего он работал от какого-то дико острого соуса. Надо искать рецепт :)
0
Лучше играй с конфигурацией. Так, тонкая пластинка яблока меж двух дисков явно проработает дольше.
0
Да и ток больше отдаст.
0
антоновка?
0
семиринка
0
а если подключить к человеческому телу электроды бедет работать?)))))
или нами ток не вырабатываетса?
(ну это так — мысли в слух)
0
Не будет. Но если грамотно спрятать в устройстве батарейку и транзистор (лампочка oss'a), то можно сильно вынести мозг неподготовленным зрителям :)
+1
ионистор можно запаять куда нить на плату ))))
0
Он на батарейку похож.
0
Пара мелких таблеткобатареек на 1.5V поместятся под ЖК стекляшку ;)
0
или кондеров россыпь.
хотя не. утекать будут быстрее, чем девайс их кушать, наверное.
0
Да, кондеры это не торт
0
И с утилизацией нет проблем — взял сожрал
0
  • avatar
  • Sgm
  • 17 октября 2011, 16:45
Да, это кстати весомый аргумент :)
0
Это после обогащения яблочка солями неизвестных металлов? Я бы не стал…
0
Да ну ладно, после ролтона не страшно
0
  • avatar
  • Sgm
  • 17 октября 2011, 23:49
Не вижу, из чего там минус. На алюминий похоже. Не совсем уж дрянь, но и не сахар. Особенно учитывая, что получаются органические солю алюминия, скорее всего водорастворимые. Но оно хотя бы не задерживается в организме.

А роллтон ИМХО не такой уж и страшный.
0
Я недавно его ел. Раньше был чуть съедобнее.
0
минус из гвоздя. из чего гвоздь — не знаю :)
0
Железо в основном. Тоже не так уж и плохо, хотя проблемы быть могут.
0
херня, на форуме уже был срач, на тему вредности алюминия и т.п. Это сколько же надо яблок сожрать, утыканных гвоздями, чтобы проблемы появились…
0
а вдруг латентная аллергия на люминий
0
тогда премию дарвина.
0
Говорят вещества с алюминием приводят к болезне альтцгеймера. Поэтому не рекомендуют использовать алюминиевую посуду
0
Правильная посуда должна быть серебряной.
Известно ещё из горького опыта А. Македонского…
0
Серебро тоже не самая полезная штука. Плюс неудобно по чисто физическим свойствам (мягкое, очень хорошо проводит тепло). Я предпочитаю нержавейку.
0
Наверное, действительно, лучше нержавейку или эмалированную посуду. Серебро, да, тяжелый металл
0
Ну, так оно ж как бы имеет бактерицидные свойства. :) От холеры как-то больше шансов загнуться, чем от накопления Ag в организме…
Насчёт того, что тяжёлый металл, вряд ли стоит особо беспокоиться — ибо оно довольно-таки инертно химически. Если же нужна полная гарантия — тогда из тантала надо посуду делать. Не реагирует даже с царской водкой. :)
0
Чет сомневаюсь, что оно инертно. Довольно быстро темнеет. Бактерицидные свойства — да. Но чтоб ненавредить человеку нужно ну уж совсем микроскопические дозы. А не посуду из него делать.
«Если же нужна полная гарантия» можно вполне нержавейкой обойтись :)
0
Вот-вот, от этого вашего серебра даже бактерии дохнут! А мне потом что, чай с дохлыми микробами пить?!
А если серьезно — оно неудобное и дорогое. От бактерий же есть более надежные средства защиты.
0
А можно как нибудь сделать питание из дармовых WiFi радиоволн?
0
Вот тут обсуждалось. Самому очень интересно :)
0
Спасибо. Получился простой и понятный пример использования энергосберегающего режима, в отличие от той программы, что идет вместе с платой. Вот только без вызова процедуры LCD_GLASS_Init() удаеться получить только 40мка, а с ней 4.1мка. Походу какой-то выход получаеться нагруженным. Пока не разобрался.
0
Всмысле без нее? LCD инициализируется вручную или вообще не ипользуется?
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.