STM32 режимы пониженного энергопотребления.
Как видно, схема питания разделена на домены и каждый из них отвечает за питание определённой периферии/части МК.
Уменьшить потребление МК можно несколькими способами, самое простое, что можно сделать — это понизить тактовую частоту или отключать шины APB и AHB, когда они не используются. Другой вариант, который требует чуть больше знаний — это использовать режимы низкого энергопотребления, кстати, их всего три:
Перед тем как начать описывать режимы хотелось бы отметить, что переход в любой из режимов пониженного энергопотребления осуществляется с помощью ассемблерных инструкций WFI и WFE, их отличие заключается в том, что если контроллер был отправлен в режим пониженного энергопотребления с помощью WFI(wait for interrupt), то выйти из этого режима он может только по прерыванию, если с помощью WFE(wait for event), то выйти из этого режима он может по наступлению события. Хотелось бы напомнить, что событие — это периферийное прерывание, которое не было разрешено в NVIC.
Для режима sleep характерно следующее, прекращается выполнение кода программы, прекращается тактирование ядра, периферия работает, все вывода сохраняют своё состояние.
Вход в режим sleep осуществляется с помощью инструкций WFE и WFI, когда:
Выход из режима sleep по событию(WFE), позволяет начать выполнение программы с того места, где она была остановлена.
Источником события может быть:
Что касается потребления, то тут надо смотреть документацию на конкретный камень, например, минимальное потребление STM32F103VE составляет 0.45mA.
Режим stop характерен остановкой PLL, HSI и HSE генераторов, при этом все пины ввода-вывода сохраняют своё состояние.
Вход в stop режим осуществляется с помощью инструкций WFE и WFI, когда:
Выход из режима stop в обоих случаях(WFI и WFE) инициирует сигнал на любой из линий EXTI, только в одном случае сконфигурированной в режиме прерывания, в другом — в режиме события.
Что касается энергопотребления, то в этом случае оно составляет десятки микроампер и зависит от режима стабилизатора напряжения.
Standby режим характеризуется самым низким потреблением, около 2мкА и потерей всех данных, то есть при выходе из этого режима контроллер будет вести себя также как при нажатии на кнопку Reset.
О том что контроллер вышел из standby режима говорит установленный флаг SBF. В standby режиме все выводы находятся в высокоимпедансном состоянии кроме NRST, WKUP(если сконфигурирован), Tamper(если сконфигурирован).
Для наглядности оставлю здесь эту таблицу и поясню, что в столбце entry описаны биты которые надо сконфигурировать, а не установить.
Отладку в режимах пониженного энергопотребления можно разрешить, установив соответствующие биты в регистре DBGMCU_CR.
Регистр управления питанием(PWR_CR).
Биты 31:9 – зарезервированы, заполнены нулями.
DBP(Disable backup domain write protection) — 1 в этом бите разрешает доступ к регистрам резервного копирования и RTC, 0 – запрещает.
PLS[2:0](PVD level selection) — задают уровень порога срабатывания детектора напряжения.
PVDE(Power voltage detector enable) — 1 в этом бите включает PVD, 0 – отключает.
CSBF(Clear standby flag) — всегда читается как ноль, установка 1 в этот бит очищает SBF(StandbyFlag).
CWUF(Clear wakeup flag) — всегда читается как ноль, установка 1 в этот бит очищает WUF(Wakeup Flag), но происходит это спустя два такта системного генератора.
PDDS(Power down deepsleep) — когда контроллер находится в режиме глубокого сна(Deepsleep), запись 1 в этот бит переводит его в standby режим, при записи 0 — в stop режим, статус стабилизатора напряжения зависит от LPDS, это относится только к stop режиму.
LPDS(Low-power deepsleep) — 0 в этом бите включает стабилизатор напряжения в stop режиме, 1 — переводит стабилизатор в режим пониженного энергопотребления в stop режиме.
Регистр управления/статуса (PWR_CSR)
Биты 31:9 – зарезервированы, заполнены нулями.
EWUP(Enable WKUP pin) — 1 в этом бите разрешает использовать пин WKUP для пробуждения микроконтроллера по нарастающему фронту, при установленном 0 — запрещает, в этом случае пин WKUP используется как обычный GPIO. Автоматически сбрасывается после ресета.
Биты 7:3 – зарезервированы, заполнены нулями.
PVDO(PVD output) — этот бит изменяется аппаратно и имеет силу, только когда установлен бит PVDE. Единица в этом бите означает, что VDD/VDDA опустилось ниже порогового значения, выбранного битами PLS[2:0], 0 — VDD/VDDA выше порогового значения, выбранного битами PLS[2:0].
WUF(Wakeup flag) — этот бит устанавливается аппаратно, а сбрасывается с помощью POR/PDR(power on reset/power down reset — при включение/выключение питания) или установкой бита CWUF в регистре PWR_CR, 1 в этом бите говорит о том, что пробуждающее событие пришло с пина WKUP или от будильника RTC.
Узнать какой вывод реализует функцию WKUP, можно в программе STM32CUBE.
Ну и в заключение код, позволяющий входить в режим standby и выходить из него по переднему фронту на пине WKUP, в моём случае(STM32F103VET) — это вывод A0.
Для получения более подробной информации можно обратиться к AN2629 Application note
Оптимизация энергопотребления STM32: практическое руководство
В сети довольно много статей про работу микроконтроллеров STM32 в энергоэффективных устройствах — как правило, это устройства на батарейном питании — однако среди них прискорбно мало разбирающих эту тему за пределами перечисления энергосберегающих режимов и команд SPL/HAL, их включающих (впрочем, та же претензия относится к подавляющему большинству статей про работу с STM32).
Тем временем, в связи с бурным развитием умных домов и всевозможного IoT тема становится всё более актуальной — в таких системах многие компоненты имеют батарейное питание, и при этом от них ожидаются годы непрерывной работы.
Восполнять данный пробел мы будем на примере STM32L1 — контроллера весьма популярного, достаточно экономичного и при этом имеющего некоторые специфические именно для этой серии проблемы. Практически всё сказанное будет также относиться к STM32L0 и STM32L4, ну и в части общих проблем и подходов — к другим контроллерам на ядрах Cortex-M.
Практический результат должен выглядеть примерно так, как на фотографии выше (и да, о применимости мультиметров и других средств измерения к подобным задачам тоже поговорим).
Режимы энергосбережения в STM32L1
Основа основ экономии батарейки — это основные режимы энергосбережения процессора. Они у каждого производителя и в каждой серии контроллеров свои (конкретный набор представляет собой вендорское расширение стандартных режимов ядра Cortex-M с различными нюансами относительно периферии, напряжений питания и т.п.).
Конкретно у STM32L1, который относится к экономичной серии контроллеров и в связи с этим, помимо прочего, получил расширенный набор настроек питания, мы имеем следующее:
Кроме этого, неплохо бы запретить прерывания (на способности внешних и внутренних событий выводить процессор из сна это не скажется) и дождаться завершения сохранения данных из регистров в память, если вдруг таковое идёт, командой DSB.
Например, вот так выглядит уход в режим Stop:
WFI — блокирующая инструкция, на ней процессор уйдёт в глубокий сон и не выйдет из него, пока не случится какого-нибудь прерывания. Да, повторюсь, несмотря на то, что мы явным образом выключили прерывания, процессор на них отреагирует и проснётся — но обрабатывать начнёт только после того, как мы включим их обратно. И в этом есть глубокий смысл.
В коде выше после WFI не просто так идёт некая переинициализация рабочих частот — дело в том, что из глубокого сна L1 всегда выходит на частоте 4,2 МГц и с внутренним генератором MSI в качестве источника этой частоты. Во многих ситуациях вы очевидным образом не хотите, чтобы обработчик прерывания, разбудившего процессор, начал выполняться на этой частоте — например, потому, что у вас слетят частоты всех таймеров, UART и прочих шин; поэтому мы сначала восстанавливаем рабочие частоты (или, если хотим остаться на MSI, пересчитываем нужные шины под 4,2 МГц), а потом уже ныряем в прерывания.
На практике наиболее часто используются два режима — Run и Stop. Дело в том, что LP Run мучительно медленный и не имеет смысла, если процессору нужно выполнять какие-то вычисления, а не просто ждать внешних событий, а Sleep и LP Sleep не слишком экономичны (потребление до 2 мА) и нужны, если вам нужно сэкономить хоть сколько-нибудь, но при этом оставить работающую периферию и/или обеспечить максимально быструю реакцию процессора на события. Такие требования бывают, но в целом не очень часто.
Режим Standby обычно не используется, так как после него из-за обнуления ОЗУ невозможно продолжить с того же места, на каком вы остановились, а также есть некоторые проблемы с внешними устройствами, о которых мы поговорим ниже, и которые требуют аппаратных решений. Впрочем, если устройство разрабатывалось с расчётом на это, Standby можно использовать как режим «выключено», например, при длительном хранении этого устройства.
Собственно, на изложении этого большинство руководств обычно торжественно обрывается.
Проблема в том, что, следуя им, вы получите печальные 100-200 мкА реального потребления вместо обещанных даташитом 1,4 мкА в Stop при работающих часах — даже на эталонной отладке Nucleo, не имеющей вообще никаких внешних чипов, датчиков и т.п., на которые это можно было бы списать.
И нет, ваш процессор исправен, в errata ничего нет, а вы всё сделали правильно.
Просто не до конца.
Синдром беспокойных ног
Первая проблема STM32L1, про которую некоторые статьи упоминают, но чаще вспоминают только на форумах, когда на третий день обсуждения, откуда взялись те самые 100-200 мкА, кто-то вспоминает про существование AN3430 и доходит в нём до 19-й страницы — это состояние ножек по умолчанию.
Замечу, что даже сама STMicro к вопросу относится спустя рукава, и в большинстве документов, где оптимизация энергопотребления рассматривается, ограничивается одной-двумя фразами с советом притянуть неиспользуемые ножки к земле или перевести в режим аналогового входа, без объяснения причин.
Печаль заключается в том, что по умолчанию все ножки сконфигурированы как цифровые входы (0x00 в регистре GPIOx_MODER). На цифровом входе всегда стоит триггер Шмитта, улучшающий помехозащищённость этого входа, при этом он совершенно самостоятелен — это простейший логический элемент, буфер с гистерезисом, который не требует внешнего тактирования.
В нашем случае это означает, что тактирование мы в режиме Stop выключили, а триггеры Шмитта продолжили работать как ни в чём не бывало — в зависимости от уровня входного сигнала они переключают свои выходы в 0 и 1.
При этом часть ножек процессора в типовой схеме у нас висит в воздухе — то есть, никакого внятного сигнала на них нет. Было бы неверно думать, что отсутствие внятного сигнала означает, что на этих ножках 0 — нет, на этих ножках из-за их высокого входного сопротивления какие-то случайные помехи неустановленной величины, от наводок и перетекания тока с соседних дорожек до Первого канала телевидения, если у ноги достаточно длинная дорожка, чтобы служить антенной (впрочем, аналоговое ТВ в России скоро выключат, что должно привести к некоторому снижению энергопотребления неправильно сконфигурированных микроконтроллеров).
В соответствии с этими флуктациями ножка некоторым случайным образом переключается между 0 и 1. КМОП-логика потребляет ток при переключении. То есть, висящая в воздухе ножка процессора, сконфигурированная в режиме цифрового входа, потребляет заметный ток сама по себе.
Выход из этого простой — при старте программы надо все ножки сконфигурировать в состояние аналогового входа; у STM32 оно формально есть для всех ножек без исключения, независимо от того, подключены они к АЦП или нет, и от цифрового входа отличается только отсутствием триггера Шмитта на входе.
Для этого достаточно записать во все регистры GPIOx_MODER значение 0xFF. FF, проще всего это сделать, как уже говорилось выше, прямо на старте, а потом по ходу пьесы уже переконфигурировать отдельные ноги так, как надо в данном устройстве.
Здесь возникает, правда, проблема второго порядка — хорошо, если у вас прошивка работает на одном конкретном контроллере, и поэтому вы всегда знаете, чему в GPIOx равно x. Хуже, если прошивка универсальна — у STM32 может быть до 8 портов, но может быть и меньше; при попытке записи в несуществующий в данной модели контроллера порт вы получите Hard Fault, т.е. аварийную остановку ядра.
Впрочем, даже этот случай можно обойти — Cortex-M позволяет проверять адреса на их валидность, причём в случае M3 и M4 проверка вообще достаточно тривиальна, а на M0 требует некоторой магии, но реализуема (подробности можно почитать тут, раздувать ими эту статью не будем).
То есть, в общем случае, стартовали процессор, настроили частоты — и сразу же прошлись по всем наличествующим портам GPIO, записав им в MODER единички (код ниже написан под RIOT OS, но в целом понятен без комментариев и может быть в три минуты переложен на любую другую платформу).
Замечу, что это касается только серии L1, в L0 и L4 опыт был учтён, и они по умолчанию при старте конфигурируют все порты как аналоговые входы.
Аккуратно проделав все эти процедуры, вы заливаете прошивку в готовое устройство… и получаете 150 мкА в режиме Stop на процессоре и всех выключенных внешних чипах, при том, что самые пессимистичные ваши оценки, исходящие из даташитов на всё, что у вас вообще припаяно на плате, дают никак не выше 10 мкА.
Более того, дальше вы пробуете увести процессор в Standby вместо Stop, т.е. просто выключить его почти полностью — и вместо того, чтобы упасть, энергопотребление возрастает ещё втрое, вплотную приближаясь к половине миллиампера!
Не надо паниковать. Как вы уже догадались, вы всё сделали правильно. Но не до конца.
Синдром беспокойных ног — 2
Следующая проблема состоит из двух частей.
Первая — достаточно очевидная: если устройство ваше состоит не из одного микроконтроллера, то важно не забывать, что у внешних чипов тоже есть входные сигналы, на которых висят триггеры Шмитта, и которые, более того, могут пробуждать внутреннюю логику чипа. Например, чип, уводимый и выводимый из своего сна командой по UART, при любом шевелении на этой шине будет пытаться прочитать с неё данные.
При каких условиях они оказываются в воздухе?
Во-первых, при уходе контроллера в режим Standby все GPIO переводятся в состояние High-Z, с высоким сопротивлением — то есть, по сути, подключённые к ним внешние чипы оказываются в воздухе. Исправить это программно в STM32L1 нельзя (в других сериях и других контроллерах бывает по-разному), поэтому единственный выход — в системе, в которой предполагается использование Standby-режима, входы внешних чипов должны быть притянуты к земле или питанию внешними резисторами.
Конкретный уровень выбирается так, чтобы линия с точки зрения чипа была неактивна:
Понять и простить это нельзя, но запомнить и исправить можно: для интерфейсов, ведущих себя подобным образом, в функцию ухода сна надо добавить принудительное переключение их в обычные GPIO с уровнями, соответствующими неактивным уровням данного интерфейса. После выхода из сна интерфейсы можно восстановить.
Например, тот же SPI перед уходом в сон (для простоты я беру код из операционки RIOT OS, понятно, что то же самое легко реализовать на регистрах):
Обратите внимание, что выходы здесь настраиваются не как GPIO_OUT с уровнем 0 или 1, а как входы с подтяжкой к 0 или 1 — это момент не принципиальный, но обеспечивает дополнительную безопасность, если вы ошибётесь и попробуете поиграть в тяни-толкая с каким-нибудь внешним чипом, тянущим эту ножку в другую сторону. С GPIO_OUT можно устроить короткое замыкание, с GPIO_IN с подтяжкой — никогда.
Кроме того, не затрагивается сигнал SPI CS — в данном случае он формируется программно, то есть обычным GPIO, и своё состояние во сне сохраняет уверенно.
Для восстановления состояния ножки при выходе из сна достаточно при входе записать значения регистров, которые будут изменены (MODER, PUPDR, OTYPER, OSPEEDR — смотрите по ситуации в конкретном случае), в переменные, а при выходе из сна из переменных раскатать их обратно в регистры.
И вот теперь… та-дааам! Заглавная картинка. Полтора микроампера.
Но праздновать рано. На этом мы закончили статическую оптимизацию энергопотребления, и впереди нас ждёт динамическая.
Ахиллес против черепахи
Что лучше — больше кушать и быстрее бегать или меньше кушать, но медленнее бегать? В случае с микроконтроллерами ответ на этот вопрос дважды нетривиален.
Во-первых, рабочие частоты можно менять в очень широких пределах — от 65 кГц (LP Run) до 32 МГц в обычном режиме. Как и у любой КМОП-микросхемы, у STM32 есть две слагающие в энергопотреблении — статическая и динамическая; от частоты зависит вторая, первая же постоянна. В результате энергопотребление будет снижаться не так быстро, как рабочая частота и производительность, и в зависимости от задачи оптимальная с точки зрения энергоэффективности частота может оказаться разной — там, где надо ждать какого-то события, но почему-то нельзя уйти в сон, будут эффективны низкие частоты, там, где надо только молотить числа — высокие. В типовых «средних по больнице» задачах обычно не имеет смысла спускаться ниже 2-4 МГц.
Во-вторых, и это менее тривиальный момент, скорость выхода из сна зависит от рабочей частоты и способа её получения.
Наихудший случай — это выход из сна на частоту 32 МГц от внешнего кварца (напомню, что просыпается STM32L1 на внутреннем генераторе на 4 МГц), потому что состоит он из трёх этапов:
Эти задержки могут быть несущественны с точки зрения энергопотребления для системы, которая просыпается редко (не более раза в секунду), но там, где период между просыпаниями составляет десятки миллисекунд и меньше, а сами просыпания коротки, накладные расходы начинают вносить уже вполне измеримую добавку даже с учётом, что в ходе пробуждения процессор потребляет сравнительно небольшой ток.
Что с этим можно сделать? В общем-то ответ очевиден: стараться избегать использования внешнего кварца. Например, программа, в которой есть редкие тяжёлые подзадачи, требующие точного тактирования (скажем, из тривиальных — обмен данными по UART), и частые простые подзадачи, может внутри себя при каждом пробуждении решать по тем или иным признакам, надо ли вообще сейчас уходить на внешний кварц, или же проще (и быстрее!) будет выполнить текущую задачу на генераторе MSI, на котором процессор и так уже проснулся, не тратя кучу времени на инициализацию частот.
При этом, однако, может потребоваться подстройка частот тактирования периферии, а также подстройка режимов доступа к флэш-памяти (число циклов задержки), напряжения питания ядра процессора (в STM32L1 оно выбирается из трёх возможных значений) и т.п. Впрочем, что касается режимов работы ядра и памяти — часто можно забить на их подстройку, выбрав рекомендуемые для максимальной используемой частоты, так как неоптимальная работа ядра на меньших частотах не даст существенного изменения практической производительности и энергопотребления из-за малого объёма задач, на этих частотах выполняемых.
Хотя все подобные меры относятся уже к тонкой подстройке режимов (и, например, большинство ОС и библиотек ничего даже близко похожего из коробки не умеют), в некоторых случаях они могут дать снижение среднего потребления масштаба единиц процентов, а иногда и больше. Представьте себе, например, водосчётчик, который каждые 50 мс опрашивает контакты геркона, при этом собственно сам опрос занимает несколько десятков микросекунд — хотите вы к этому времени добавить
500 мкс на пробуждение контроллера.
Невыносимо долгая секунда
Ещё одна проблема, которая не имеет прямого отношения к энергосбережению, но неизбежно в связи с ним встречается — как отсчитывать промежутки времени меньше 1 секунды?
Дело в том, что на STM32L1 есть всего один таймер, работающий в режиме Stop — это RTC, штатная единица времени у которого — 1 секунда. Вместе с тем, в программах постоянно встречаются интервалы времени в единицы, десятки и сотни миллисекунд, взять хотя бы тот же водосчётчик.
Как быть? Сбегать на процессоры, имеющие таймеры LPTIM, способные тактироваться от 32768 Гц? Хороший вариант, на самом деле, но не всегда необходимый. Можно и без него.
Не на всех STM32L1, но начиная с Cat. 2 (это процессоры STM32L151CB-A, STM32L151CC и новее) блок RTC дополнился новым регистром — SSR, SubSeconds Register. Точнее, не столько дополнился, сколько сделал его видимым пользователю, плюс добавились субсекундные будильники ALRMASSR и ALRMBSSR.
Этот регистр не содержит в себе каких-то понятных единиц времени, его на скорую руку сделали из технического внутреннего счётчика. В STM32L1 тикающий на 32768 Гц часовой генератор проходит через два счётчика-делителя, асинхронный и синхронный, которые суммарно в штатном режиме делят его на 32768, чтобы получить 1-секундный тик для часов. Так вот, SSR — это просто текущее значение второго счётчика.
Хотя SSR считает не в миллисекундах, а в своих единицах, размерность этих единиц можно менять, меняя соотношение делителей синхронного и асинхронного счётчика, при этом сохраняя их общий коэффициент равным 32768, чтобы получить на входе RTC стандартную 1 секунду. Зная эти коэффициенты, можно посчитать и цену одного деления SSR в миллисекундах, а отсюда уже перейти к программированию субсекундных будильников.
Надо заметить, что асинхронный предварительный счётчик — экономичнее, чем синхронный SSR, и потому ставить его в 1, а на SSR входную частоту уже делить на 32768, получив отсчёт всего 30 мкс, энергетически невыгодно. Для себя мы определили оптимальным значением для предварительного делителя 7, для синхронного — 4095 ((7+1)*(4095+1) = 32768). При дальнейшем уменьшении предварительного делителя энергопотребление RTC начинает измеримо расти — на доли микроампера, но так как мы сравниваем это с «эталонным» 1,4 мкА в режиме Stop, то даже доли имеют значение. По умолчанию у STM32L1 эти значения — 127 и 255, т.е. цена отсчёта около 4 мс, что немного грубовато.
Если вы хотите поковыряться в коде, то в своё время мы доработали штатный драйвер RTC из RIOT OS до поддержки RTC_SSR и миллисекундных интервалов. Пользуемся с тех пор буквально на каждом шагу (и так как мы работаем в ОС, то поверх него висит ещё и сервис, позволяющий лёгким движением руки навешивать на один аппаратный таймер практически сколько угодно задач с произвольными периодами).
Этот же подход переносится на контроллеры STM32L0 и STM32L4, все модели которых имеют регистр RTC_SSR; это позволяет не возиться с таймерами LPTIM и унифицировать код для разных платформ.
Как понять, что мультиметр врёт
Разумеется, после всех оптимизаций встаёт законный вопрос: а чего, собственно, мы добились? Без знания ответа на него можно было бы и вовсе ограничиться одним WFE с правильно настроенными флагами, уйти в сон и получить свои 200-500 мкА.
Наиболее традиционный способ измерения тока — разумеется, мультиметр. Понять, что он врёт на нагрузке типа микроконтроллера с его динамическим потреблением, очень просто — если он включён, значит, врёт.
Это, впрочем, не означает, что мультиметр в данном вопросе бесполезен. Надо просто уметь его применять.
Во-первых, мультиметр — очень медленная штука, типовое время на один отсчёт в нём масштаба секунды, типовое время смены состояния микроконтроллера — масштаба микросекунд. В системе, которая меняет своё потребление в таком темпе, мультиметр покажет просто случайные величины.
Однако, одна из интересующих нас неслучайных величин — это потребление микроконтроллера в режиме сна; если оно заметно превышает величину, которую мы прикинули по даташитам, значит, что-то явно не так. Это потребление статичной системы, то есть, может быть измерено мультиметром.
Наиболее тривиальный способ, показанный на заглавной фотке — мультиметр в режиме микроамперметра, который сейчас есть в большинстве моделей среднего уровня, и имеет хорошую точность и отличное разрешение. UT120C имеет разрешение 0,1 мкА при паспортной точности ±1% ±3 разряда, чего нам хватит за глаза.
Проблема с этим режимом одна — у мультиметров в нём большое последовательное сопротивление, масштаба сотен ом, поэтому в нормальном режиме микроконтроллер с таким мультиметром в цепи питания просто не запустится. К счастью, положения «mA» и «uA» практически во всех приборах на шкале рядом, гнёзда для измерения на обоих диапазонах одни, так что можно спокойно запустить контроллер на пределе «mA», а когда он уйдёт в сон, перещёлкнуться на «uA» — это происходит достаточно быстро, чтобы контроллер не успел потерять питание и перезагрузиться.
Обратите внимание, что если у контроллера случаются всплески активности, этот способ неприменим. В прошивке устройства с фотографии, например, каждые 15 секунд сбрасывается сторожевой таймер — в эти моменты мультиметр успевает показать что-то в районе 27 мкА, что, конечно, не имеет отношения ни к чему, кроме погоды на Марсе. Если в вашей системе что-то сколь угодно короткое происходит чаще чем раз в 5-10 секунд, мультиметр будет попросту врать.
Другой способ измерения статического (я вот прямо выделяю это слово) потребления мультиметром — это измерение падения на внешнем шунте. Если требуется измерять сверхмалые токи масштаба единиц-десятков микроампер, то надо поставить большой (например, 1 кОм) шунт, а параллельно ему — диод Шоттки в прямом включении. При падении на шунте более 0,3 В диод будет открываться и ограничивать падение напряжения, а до 0,3 В можно спокойно измерять падение мультиметром на милливольтовом диапазоне, 1 мВ = 1 мкА.
Измерять типовым мультиметром падение на низкоомном шунте, увы, не получится — приборы среднего класса даже если показывают что-то ниже 100 мкВ, точность в этом диапазоне имеют прискорбную. Если же у вас есть хороший настольный прибор, способный показать 1 мкВ, мои советы вам уже не нужны.
Однако статика — это хорошо, а как быть с динамикой? Как оценить то же влияние разных частот на среднее энергопотребление?
Вот здесь всё сложно.
Давайте запишем базовые требования:
И всё это под одно-единственное применение.
В принципе, подобное устройство можно сконструировать самому — нужны в первую очередь очень хорошие ОУ с минимальным смещением входа, вроде OPA2335. Таких ОУ ставится на один и тот же шунт 2-3 штуки с разными коэффициентам усиления, все они заводятся на разные входы АЦП (при таком подходе вполне можно использовать встроенный в микроконтроллер), далее при каждом съёме данных программно определяется, какой из ОУ в данный момент не перегружен, с него показания и засчитываются.
Проблема скорости передачи данных на компьютер решается достаточно просто — так как для практических целей нам интересно в первую очередь среднее потребление системы в реальной жизни, микросекундные отсчёты можно собирать в набортном микроконтроллере измерителя и выдавать наверх среднее арифметическое за какой-то разумный миллисекундного масштаба период.
Кроме того, как показывает практика, весьма полезно иметь измеритель-логгер пусть простой и не слишком точный, но всегда находящийся под рукой — чтобы не получить неожиданностей с поломанным каким-то изменением прошивки энергосбережением.
Мы, например, такой встроили на наш стандартный USB-адаптер UMDK-RF, который постоянно используется при отладке прошивок — в нём уже есть SWD-программатор с поддержкой протокола DAPLink, мостик USB-UART и логика управления питанием, соответственно, измеритель потребления достался ему практически бесплатным довеском. Сам по себе измеритель представляет собой шунт 1 Ом и усилитель INA213 (усиление в 50 раз, типовое смещение нуля 5 мкВ):
Усилитель подключён прямо на вход АЦП микроконтроллера (STM32F042F6P6), АЦП отрабатывает с периодом 10 мкс по аппаратному таймеру, а наверх по USB выдаются усреднённые данные за 100-мс интервал. В результате, поменяв что-то в логике прошивки, можно просто пойти покурить или выпить кофе, оставив устройство на столе, а вернувшись, посмотреть в график типа такого:
Точность такого «бесплатного» устройства, разумеется, невысокая — при 12-битном АЦП и одном усилителе минимальный квант составляет 16 мкА, но именно для быстрой и регулярно оценки поведения отлаживаемых устройств с точки зрения энергопотребления оно крайне полезно. В конце концов, если вы наворотите в прошивке или устройстве что-то не то, то с очень высокой гарантией вылезете из единиц микроампер как минимум в сотни, а это будет хорошо заметно.
Отдельный приятный бонус — так как данные выдаются в виртуальный COM-порт в текстовом виде (значения в микроамперах), можно расположить окно терминала рядом с окном, показывающим консоль устройства, и смотреть на энергопотреблением одновременно с отладочными сообщениями.
Хвастаюсь я этим не просто так, а чтобы предложить всем желающим использовать данный минимальный (и очень дешёвый!) программатор-отладчик в собственных проектах.
Срисовать схему можно тут (исходник в DipTrace), утянуть прошивку — здесь (бранч umdk-rf, при сборке цель UMDK-RF, основана на проекте dap42). Схема нарисована хоть и сумбурно, но, надеюсь, основные моменты понятны, прошивка написана на C с использованием libopencm3 и собирается обычным arm-none-eabi-gcc. В качестве дополнительных функций у прошивки есть управление питанием, отлов сигналов о перегрузке с управляющих ключей и ввод подключённого к ней контроллера в его родной бутлоадер длинным нажатием кнопки.
NB: если вы хотите, чтобы кнопка boot заводила собственный контроллер программатора в его бутлоадер штатным образом, у неё должна быть изменена полярность подключения, в прошивке убрана правка option bytes контроллера при первой загрузке и программный вход в бутлоадер, а также изменена полярность прерывания для штатных функций этой кнопки.
Посмотреть на то, как делается измерение тока на паре ОУ с разными коэффициентами усиления (например, с целью усовершенствования вышеописанного отладчика под свои задачи), можно тут (стр. 9), более традиционный альтернативный вариант — с одним ОУ и недешёвым 24-битным АЦП — есть у TI (EnergyTrace на стр. 5).









