Оптимизация энергопотребления STM32: практическое руководство

    Привет, Хабр!

    В сети довольно много статей про работу микроконтроллеров STM32 в энергоэффективных устройствах — как правило, это устройства на батарейном питании — однако среди них прискорбно мало разбирающих эту тему за пределами перечисления энергосберегающих режимов и команд SPL/HAL, их включающих (впрочем, та же претензия относится к подавляющему большинству статей про работу с STM32).

    Тем временем, в связи с бурным развитием умных домов и всевозможного IoT тема становится всё более актуальной — в таких системах многие компоненты имеют батарейное питание, и при этом от них ожидаются годы непрерывной работы.

    Восполнять данный пробел мы будем на примере STM32L1 — контроллера весьма популярного, достаточно экономичного и при этом имеющего некоторые специфические именно для этой серии проблемы. Практически всё сказанное будет также относиться к STM32L0 и STM32L4, ну и в части общих проблем и подходов — к другим контроллерам на ядрах Cortex-M.



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

    Режимы энергосбережения в STM32L1


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

    Конкретно у STM32L1, который относится к экономичной серии контроллеров и в связи с этим, помимо прочего, получил расширенный набор настроек питания, мы имеем следующее:

    • Run — обычный режим. Всё включено, вся периферия доступна, частота до 32 МГц.
    • Low Power Run (LP Run) — специальный режим с рабочей частотой в пределах 131 кГц и максимальным потреблением, считая всю периферию, 200 мкА. В режиме LP Run стабилизатор питания процессора переходит в специальный экономичный режим, что экономит до полусотни микроампер по сравнению с работой на той же частоте в режиме Run.
    • Sleep — приостановка работы ядра, но с сохранением всех тактовых частот. Периферия процессора может продолжать работать, если ядро ей не нужно, но может быть и автоматически отключена.
    • Low Power Sleep (LP Sleep) — сочетание Sleep с переходом стабилизатора в экономичный режим. Тактовая частота не выше 131 кГц, общее потребление не выше 200 мкА.
    • Stop — полная остановка всех тактовых частот, кроме «часового» генератора 32768 Гц, внешнего или внутреннего. В случае с STM32L1 в этом режиме продолжают работать только часы реального времени, всё остальное полностью останавливается; в более новых процессорах некоторая периферия может тактироваться от низкой частоты. Почти все ножки процессора сохраняют своё состояние. Содержимое ОЗУ сохраняется, внешние прерывания продолжают работать.
    • Standby — полное выключение ядра процессора, ОЗУ и всей периферии, кроме часов реального времени. ОЗУ не сохраняется (т.е. с точки зрения ПО, уход в Standby практически аналогичен передёргиванию питания — начинай всё с начала), продолжают тикать RTC. Внешние прерывания не работают, кроме трёх специальных ножек WKUPx, переключение которых из 0 в 1 пробуждает процессор.

    Вход в каждый из режимов осуществляется достаточно просто — надо установить флаги в трёх-пяти регистрах, после чего (для режимов сна) позвать инструкцию WFI или WFE, это стандартная инструкция Cortex-M, означает «Wait For Interrupt» и «Wait For Event». В зависимости от флагов (они описаны в Reference Manual процессора, для STM32L1 это RM0038) процессор сам свалится на этой команде в нужный режим.

    Кроме этого, неплохо бы запретить прерывания (на способности внешних и внутренних событий выводить процессор из сна это не скажется) и дождаться завершения сохранения данных из регистров в память, если вдруг таковое идёт, командой DSB.

    Например, вот так выглядит уход в режим Stop:

    /* флаг PDDS определяет выбор между Stop и Standby, его надо сбросить */
    PWR->CR &= ~(PWR_CR_PDDS);
                
    /* флаг Wakeup должн быть очищен, иначе есть шанс проснуться немедленно */    
    PWR->CR |= PWR_CR_CWUF;
    
    /* стабилизатор питания в low-power режим, у нас в Stop потребления-то почти не будет */
    PWR->CR |= PWR_CR_LPSDSR;
    
    /* источник опорного напряжения Vref выключить автоматически */
    PWR->CR |= PWR_CR_ULP;
    
    /* с точки зрения ядра Cortex-M, что Stop, что Standby - это режим Deep Sleep */
    /* поэтому надо в ядре включить Deep Sleep */
    SCB->SCR |=  (SCB_SCR_SLEEPDEEP_Msk);
    
    /* выключили прерывания; пробуждению по ним это не помешает */
    unsigned state = irq_disable();
    
    /* завершили незавершённые операция сохранения данных */
    __DSB();
    
    /* заснули */
    __WFI();
    
    /* переинициализация рабочих частот */
    init_clk();
    
    /* после просыпания восстановили прерывания */
    irq_restore(state);
    

    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, но в целом понятен без комментариев и может быть в три минуты переложен на любую другую платформу).

    #if defined(CPU_FAM_STM32L1)
        /* switch all GPIOs to AIN mode to minimize power consumption */
        GPIO_TypeDef *port;
        
        /* enable GPIO clock */
        uint32_t ahb_gpio_clocks = RCC->AHBENR & 0xFF;
        periph_clk_en(AHB, 0xFF);
        
        for (uint8_t i = 0; i < 8; i++) {
            port = (GPIO_TypeDef *)(GPIOA_BASE + i*(GPIOB_BASE - GPIOA_BASE));
            if (cpu_check_address((char *)port)) {
                port->MODER = 0xffffffff;
            } else {
                break;
            }
        }
        
        /* restore GPIO clock */
        uint32_t tmpreg = RCC->AHBENR;
        tmpreg &= ~((uint32_t)0xFF);
        tmpreg |= ahb_gpio_clocks;
        periph_clk_en(AHB, tmpreg);
    #endif
    

    Замечу, что это касается только серии L1, в L0 и L4 опыт был учтён, и они по умолчанию при старте конфигурируют все порты как аналоговые входы.

    Аккуратно проделав все эти процедуры, вы заливаете прошивку в готовое устройство… и получаете 150 мкА в режиме Stop на процессоре и всех выключенных внешних чипах, при том, что самые пессимистичные ваши оценки, исходящие из даташитов на всё, что у вас вообще припаяно на плате, дают никак не выше 10 мкА.

    Более того, дальше вы пробуете увести процессор в Standby вместо Stop, т.е. просто выключить его почти полностью — и вместо того, чтобы упасть, энергопотребление возрастает ещё втрое, вплотную приближаясь к половине миллиампера!

    Не надо паниковать. Как вы уже догадались, вы всё сделали правильно. Но не до конца.

    Синдром беспокойных ног — 2


    Следующая проблема состоит из двух частей.

    Первая — достаточно очевидная: если устройство ваше состоит не из одного микроконтроллера, то важно не забывать, что у внешних чипов тоже есть входные сигналы, на которых висят триггеры Шмитта, и которые, более того, могут пробуждать внутреннюю логику чипа. Например, чип, уводимый и выводимый из своего сна командой по UART, при любом шевелении на этой шине будет пытаться прочитать с неё данные.

    Соответственно, если все эти ноги повесить в воздухе, ничего хорошего мы не получим.

    При каких условиях они оказываются в воздухе?

    Во-первых, при уходе контроллера в режим Standby все GPIO переводятся в состояние High-Z, с высоким сопротивлением — то есть, по сути, подключённые к ним внешние чипы оказываются в воздухе. Исправить это программно в STM32L1 нельзя (в других сериях и других контроллерах бывает по-разному), поэтому единственный выход — в системе, в которой предполагается использование Standby-режима, входы внешних чипов должны быть притянуты к земле или питанию внешними резисторами.

    Конкретный уровень выбирается так, чтобы линия с точки зрения чипа была неактивна:

    • 1 для UART TX
    • 0 для SPI MOSI
    • 0 для SPI CLK при SPI Mode 0 или 1
    • 1 для SPI CLK при SPI Mode 2 или 3
    • 1 для SPI CS

    Во-вторых, на STM32 при использовании режима Stop (sic!) состояние GPIO, подключённых к внутренним аппаратным блокам интерфейсов, может быть… разным. То есть, тот же интерфейс SPI, будучи сконфигурированным, в Stop совершенно внезапно оказывается то ли цифровым входом, то ли вообще High-Z — с соответствующими последствиями для висящих на нём внешних чипов. При том, что документация заявляет сохранение состояния ног, априори полагаться на это можно, только если вы используете ноги в качестве обычных GPIO.

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

    Например, тот же SPI перед уходом в сон (для простоты я беру код из операционки RIOT OS, понятно, что то же самое легко реализовать на регистрах):

    /* specifically set GPIOs used for external SPI devices */
    /* MOSI = 0, SCK = 0, MISO = AIN for SPI Mode 0 & 1 (CPOL = 0) */
    /* MOSI = 0, SCK = 1, MISO = AIN for SPI Mode 2 & 3 (CPOL = 1) */
    for (i = 0; i < SPI_NUMOF; i++) {
        /* check if SPI is in use */
        if (is_periph_clk(spi_config[i].apbbus, spi_config[i].rccmask) == 1) {
            /* SPI CLK polarity */
            if (spi_config[i].dev->CR1 & (1<<1)) {
                gpio_init(spi_config[i].sclk_pin, GPIO_IN_PU);
            } else {
                gpio_init(spi_config[i].sclk_pin, GPIO_IN_PD);
            }
    
            gpio_init(spi_config[i].mosi_pin, GPIO_IN_PD);
            gpio_init(spi_config[i].miso_pin, GPIO_AIN);
        }
    }
    

    Обратите внимание, что выходы здесь настраиваются не как 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 МГц), потому что состоит он из трёх этапов:

    • собственно выход процессора из сна
    • стабилизация генерации кварца (1-24 МГц)
    • стабилизация генерации PLL (32 МГц)

    Собственно выход процессора из сна здесь — наименьшая проблема, при частоте 4,2 МГц он занимает примерно 10 мкс. А вот стабилизация кварца может занимать до 1 мс (хотя обычно для высокоскоростных резонаторов всё же быстрее, порядка нескольких сотен микросекунд), выход на режим PLL — ещё 160 мкс.

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

    Что с этим можно сделать? В общем-то ответ очевиден: стараться избегать использования внешнего кварца. Например, программа, в которой есть редкие тяжёлые подзадачи, требующие точного тактирования (скажем, из тривиальных — обмен данными по 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 мкВ, мои советы вам уже не нужны.

    Однако статика — это хорошо, а как быть с динамикой? Как оценить то же влияние разных частот на среднее энергопотребление?

    Вот здесь всё сложно.

    Давайте запишем базовые требования:

    • диапазон по току не менее 1 мкА — 100 мА (10^5)
    • период измерения не более 10 мкс
    • падение напряжения не выше 100 мВ
    • продолжительность измерения — неограниченная

    Если мы просто напрямую переложим это в цифры, то получим сравнительно быстрый и не менее чем 18-битный АЦП с входным смещением менее 30 мкВ, аналоговый фронтенд, способный измерять напряжения от 1 мкВ, и быстрый интерфейс к компьютеру, который позволит нам всё это передать и сохранить.

    И всё это под одно-единственное применение.

    Понимаете, да, почему такие вещи на каждом углу по десять баксов не лежат? Keysight N6705C в первом приближении отвечает нашим требованиям, вот только стоит он $7960.

    Из более бюджетных решений, например, SiLabs встраивает измерение тока в свои отладки — характеристики их Advanced Energy Monitoring (AEM) System зависят от конкретной модели отладки, при этом наибольшая проблема у них со скоростью измерения. В старых «стартовых наборах» STK3300/3400 это всего 100 Гц, на более новых отладках STK3700/3800 (легко узнаваемых по чёрному текстолиту) — 6,25 кГц, а в старших моделях отладок серии DK может доходить до 10 кГц, но и стоят они уже $300+. Для серьёзных задач SiLabs официально рекомендует вышеупомянутый Keysight.

    В принципе, подобное устройство можно сконструировать самому — нужны в первую очередь очень хорошие ОУ с минимальным смещением входа, вроде 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).

    P.S. Обратите внимание, что при отладке с подключённым UART или JTAG/SWD через их ножки также может утекать небольшой ток, которого не будет при реальной эксплуатации устройства. Так, на UMDK-RF в SWD подтекает около 15 мкА (и поэтому на заглавной фотографии измерения мультиметром делаются на старой версии платы, без SWD), а на STM32 Nucleo бывали и случаи с паразитным расходом через SWD порядка 200 мкА. Используемые для измерения отладочные платы необходимо на такие особенности проверять — либо отключая их интерфейсные линии, если такая возможность есть, либо сравнивая результаты с потреблением устройства, измеренным без установки на отладку, например, мультиметром в статичном режиме.

    Вместо заключения


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

    Комментарии 94

      +1
      Вы там мысли читаете, что ли? :D
      Прямо сейчас отлаживаю девайс с L051 в основе.
      P.S. Насчёт режима неиспользуемых выводов — читал, что best choice — неиспользуемые выводы на плате соединять с землёй, программно выставить режим выхода и притянуть вниз. У себя, собственно, так и сделал.
        +1
        Я бы не стал соединять выводы прямо с землёй. Нужен хотябы токоограничивающий резистор на аварийные случаи. Что если программа дёрнет этот выход в лог.1? А если группу выводов соединишь, а они будут переключаться вразнобой? Получатся неприятные сквозняки.
          0
          Ну этот вариант да, с точки зрения ошибок программирования — самый опасный. Но я перепроверяю всё по 3-4 раза.
          По умолчанию(в моём случае) выводы — аналоговые входы.
          Последовательность инициализации у меня такая:
          1. Включение тактирования порта.
          2. Запись 1 в соответствующий бит BRR(переключение выхода на 0).
          3. Включение режима OD.
          4. Включение пина на выход.
            +1
            И тем не менее есть вероятность самопроизвольного переключения содержимого регистра — от брака до космического излучения. Был даже один контроллер, который по мере работы переключал выходы к которым подключен индикатор в режим входа. И так постепенно гасли сегменты индикатора без видимой периодичности и логики. Перезапускаешь — и моментально всё становится в порядке. Поэтому, если можно защитить аппаратно, надо это делать. p.s. есть SMD-сборки резисторов по 4 в одном корпусе, места практически не занимают.
              0
              Эх, даже возразить нечего)
              Да, надо, но это надо расширять BOM, что не всегда удаётся :(
              Тогда самое лёгкое, что остаётся — перевод на аналоговый вход или в режим выхода и притянуть к земле.
              Хотя, если подумать — ведь можно перевести пин в выход типа OD и заблокировать (в STM32 можно заблокировать изменение конфигурации пина). Тогда, если вдруг сменится состояние — пин просто повиснет в воздухе. Хотя, наверняка, сама блокировка пина может подглючить.
                0
                Вплоть до того что каждый переход в сон обновлять состояние регистров, если проблема и произойдёт то распространяться она будет только на один цикл сон-работа, ну это порядка секунды, минуты но не вечность. Вот такое небольшое дополнение в процедуру входа в сон многократно повышает живучесть схемы. Правда, это имеет свою цену — дольше переход в сон и конкретное потребление электричества в целом. Тут надо играться уже с вероятностями возникновения события «нештатное состояние» и сравнивать с дополнительным потреблением энергии на его преодоление.
                Где-то видел статистику исследования возникновения ошибок в RAM серверов, там где память порядка 128Гб ошибки из-за космического излучения возникают с частотой порядка одной в минуту. И были ещё исследования в 80-е годы динамической памяти объёмом 1Мб без коррекции — там тоже оценивалась возможная частота ошибок от космического излучения в 1 ошибку в минуту на мегабайт(старая технология, по нормам 1.2мкм). Но сейчас все модули RAM с внутренней коррекцией ошибок и поболее тонким нормам. И тем не менее, ошибки при больших объёмах всё же просачиваются и не спасают даже современные методы коррекции ошибок, но по крайней мере ошибки хотябы регистрируются.
                  +1
                  Так более тонкие нормы гораздо хуже с точки зрения сбоев от одиночных частиц) Они разве что от хлопка дверью не переключаются.
                    0
                    Наоборот. У ячеек по более тонким нормам сечение захвата меньше, вероятность попасть частицей в ячейку при прочих равных условиях гораздо меньше.
                      +1
                      Зато количество ячеек намного больше и порог сбоя на порядок ниже. Поэтому вероятность попадания в каждую конкретную ячейку ниже, а вероятность сбоя в любой из имеющихся ячеек выше.
                        0
                        Ну казалось бы что должно быть выше… но она такая, ядерная физика. Сечение захвата у ячеек меньше, и это сечение не связано с физическим размером ячейки когда она исчисляется десятками атомных слоёв. На атомном уровне всё гораздо сложнее.
                          +1
                          Вам казалось бы, а у меня цифры есть) И цифры почему-то говорят, что все там прекрасно пропорционально площади.
                          А ещё например при попадании частицы в чип, у которого «десятки атомных слоев», могут сбиться сразу несколько ячеек памяти одновременно. Я лично видел десять в эксперименте.
                    0
                    Эм, а какой выход в итоге? Просто обновлять состояния всех регистров каждый раз — фигня же какая-то. Или про регистры GPIO речь?
                      0
                      Выход… либо взаимный контроль, либо и вправду периодически «обновлять» регистры GPIO. Обычные регистры то и так в процессе обновляются, главное алгоритм разработать так чтобы ловил грубые ошибки и не допускал накопление ошибки из-за одиночных сбоев. Как это делают с обшивкой самолёта — вся обшивка разделена на небольшие сектора с уменьшенной прочностью, если где-то образуется трещина то она идёт только до границы сектора и дальше не распространяется. Такими же должны быть надёжные программы, с ловушками и ограничениями по распространению ошибки, обладающими свойством самовосстановления после сбоя.
                      Это в принципе не так сложно, но повышает надёжность и устойчивость программы к одиночным сбоям.
                      Например, алгоритм вычисления среднего методом подвижного окна. Среднее можно каждый раз считать по окну, суммировать все значения в буффере и делить на размер окна — это надёжный но ресурсоемкий алгоритм. Можно не считать каждый раз значение, а посчитать его только в первый раз а при каждом сдвиге окна на шаг — вычитать убывающее значение и прибавлять прибывающее, но из-за сбоя значение средней суммы может не отражать действительность и чтобы этого не происходило выполнять периодически полный рассчет суммы в окне, тогда если ошибка и появится она действовать будет максимум до следующего полного пересчета. Так и ресурсы экономятся и защита от сбоев осуществляется.
                      0
                      А какие регистры перезаписывать? GPIO? А почему только их? А как содержимое памяти контролировать? Испорченный указатель с достаточно высокой вероятностью приведёт к хардфолту, испорченный флажок можно искать вечность…
                      Очень напоминает анекдот "- почему здесь ищешь? — здесь светлее!".
                        0
                        Потому что испорченный указатель это лишь программная ошибка, а тут можно выжечь сначала порт а потом и часть кристалла. К тому же, это не защита от ошибок а устойчивость к внешнему воздействию. Указатель, приводящий к хардфолту как раз детектируется а опрокинувшийся регистр может находится в неподобающем состоянии до следующей перезагрузки, которая может состояться через год например.
                        Ну и да, эффективней сначала пошарить по светлым местам и только потом доставать фонарик и идти в темноту.
                          +1
                          Главное — пользователю объяснить, что «это лишь программная ошибка, вы расслабитесь, кофейку попейте».
                          Поменявшийся флажок где-то в середине логики — это такой же фатальный сбой. В лучшем случае (если система хорошо спроектирована и отлично протестирована) устройство всё аварийно отключит. В худшем — включит что-то не то…
                          Вывод — контроллер, у которого с исчислимой вероятностью течёт память, использовать нельзя совсем никак вообще абсолютно.
                          Троировать, экранировать. Разобраться наконец, почему тут место такое проклятое…
                    0
                    > есть SMD-сборки резисторов по 4 в одном корпусе

                    А чем именно обычное «притянуть вниз» не устраивает и надо дублировать снаружи?
                      0
                      Ценой ошибки. Хотя в наше время… вероятность ошибки помноженная на стоимость контроллера(или ошибки?) может оказаться дешевле нескольких резисторов. А может и не оказаться. И ещё во времена 155-х микросхем существовали рекомендации не вешать выводы микросхем напрямую на шины питания — обязательно нужен резистор если требуется надёжность от схемы. Импульсные помехи они коварны, достаточно короткого импульса по шине(чтобы на индуктивности дорожки между выводом питания и входом микросхемы образовалось напряжение больше 0.3В в минус) чтобы привести к тиристорному защёлкиванию входа и выгоранию части кристалла. Конечно есть технологии изготовления микросхем лишённых этого недостатка но они дороговаты и обычно называют радиационно стойкими микросхемами.
                        0
                        Конечно есть технологии изготовления микросхем лишённых этого недостатка но они дороговаты и обычно называют радиационно стойкими микросхемами.
                        Радиационностойкие микросхемы в подавляющем большинстве случаев делаются на тех же технологиях, что и обычные, специальные технологии почти забросили из-за дороговизны их поддержания при обсутствии больших серий продукции. Различия между обычными микросхемами и защищеннными от тиристорного эффекта (чаще автомобильными и другими силовыми, чем радстойкими) в первую очередь в топологии и схемотехнике, а не в технологии.

                        PS Кремний на изоляторе здесь не считаем специальной технологией, потому что у него очеь много других применений, помимо встроенной защиты от тиристорного эффекта.
                          0
                          Я не предлагаю вешать на шины питания, я предлагаю оставить в воздухе, но притянуть к земле встроенным в чип резистором
                            0
                            Надо смотреть что там за подтяжка, её может оказаться недостаточно. Собственно, аналоговый режим пина это и делает.
                              0
                              а сколько надо подтяжки?
                                0
                                1кОм хватит всем. А на самом деле, зависит от требований стойкости к ЭМИ, ведь даже 50Ом подтяжка с небольшой дорожкой может сработать как эффективная антенна для ЭМИ определённой частоты. Благо в быту такие поля не встречаются.
                                  0
                                  В чипах порядка 7-50кОм, как я понимаю.
                                    0
                                    В STM32 типовая — 50 кОм.
                                      0
                                      Этого мало?
                                        0
                                        Для ножки, на которой снаружи ничего не висит — по уши.
                            0
                            155-я серия давно на пенсии, а баечки живее всех живых…
                            Долбали как-то наши изделия наносекундными помехами (даже генератор специальный купили в сытые годы). 8 киловольт в разъем (RS232, USB) приводят к резету, пока TVS'ами всё не обвешаешь. Сгоревших приборов я что-то не припоминаю…
                              0
                              Это были не баечки, а рекомендации производителя, чем точно обусловлены — утверждать не стану, может быть «у нас так принято», но точно были в документации.
                      0
                      Что если программа дёрнет этот выход в лог.1?

                      Сгорит (точнее даже — может сгореть. Контроллеры нынче довольно живучие) опытный образец. Скатертью дорожка.
                      Главное, чтоб у заказчика не в нужный момент ненужный выход не включился. Дыма будет не на 5, а на 500 баксов…
                        0
                        Я слышал STM-ы как раз очень чувствительны к перегрузкам и перенапряжениям. Кроме того, закороченный вывод может привести к локальному разогреву кристалла. А посаженный на землю без резистора — словить спайк и тиристорное защелкивание.
                          0
                          В случае, который я описал далее — вывод просто повиснет в воздухе(OD же).
                        0
                        best choice — неиспользуемые выводы на плате соединять с землёй


                        С точки зрения энергопотребления разницы с AIN нет.
                          0
                          Олег, если есть возможность — хочется посмотреть разницу потребления между analog input / pullup (pulldown) input / output.
                          Вообще как-то неочевидно, что плавающий, по сути, аналоговый вход, лучше чем однозначно подтянутый.
                            0
                            Никакой измеримой. Ток утечки цифрового входа — максимум 50 нА, типовой будет как минимум десятикратно меньше, так что если вы не собираетесь у 100-ногого чипа на все ноги подтяжку вешать, это даже измерить в бытовых условиях будет трудно.

                            Плавающий аналоговый вход не лучше и не хуже цифрового.
                        0
                        Вопрос не в тему статьи, может знает кто как программно попасть в бутлоадер на STM32L073?
                        Переход по адресу *(uint32_t *)(0x1FF00000 + 4) запускает механизм выбора банка флэша и в результате — опять попадаем на User Flash memory Bank1.

                        Если кто-то готов предложить что-то типа этого — повторюсь, не работает:
                        volatile uint32_t addr = 0x1FF00000;
                        void (*SysMemBootJump)(void);
                        __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
                        __set_MSP(*(uint32_t *)addr);
                        SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));
                        SysMemBootJump();
                        

                          0
                          хочу предостеречь:
                          лучше вызывать так

                          __attribute__( ( naked ) )
                          void jump_main(uint32_t stack, uint32_t func)
                          {
                          	__set_MSP(stack);
                          	(*(void (*)())(func))();
                          }
                          

                          иначе если новый стек указывает на конец РАМ (что обычно и бывает),
                          но компилятор то не знает что стек сменился, и он при вызове функции может что нибудь в стек сохранить или из него считать, в том случае если функция делающая вызов бутлоадера будет обычной, не naked.
                          И в этом случае получим либо мусор в стеке либо вылет за пределы РАМ и прерывание исключения и то не всегда и на не на всех кортексах м — есть опции отключить такие прерывания.
                          Но когда функция «naked» то компилятор ничего не делает ни при заходе в неё ни при вызове других вложенных функций из неё. Мне это кажется более безопасным и в своё время помогло избежать очень пакостных багов и зависаний.
                            0
                            Спасибо! Но конкретно с этим камнем проблема не в отсутствии naked, а в реализации dual bank флэша. Точнее того, как загрузчик с ним работает.
                            0

                            А нельзя использовать wdt и просто написать while (0) ?

                              0
                              А как это поможет попасть в загрузчик, а не просто перезагрузить прошивку?
                                0
                                Ну вообщем довольно легко, я конечно не знаю применимо ли это к STM32L073, но на stm32f103 я устанавливаю байт в RTC Backup Registers (например RTC_BKP_DR1) и проверяю его в загрузчике. RTC Backup Registers сохраняют своё значение при сбросе.

                                Вот специально писал описание, кто то просил, ну и себе оставил, на память.
                                github.com/denruss/stm32_MyDfu

                                П.С. Прочитал про STM32L073, у вас там 20-byte backup register
                                  0
                                  Системный загрузчик. В system flash. Не свой загрузчик. Со своим загрузчиком все очевидно.
                                    0

                                    Ну не верно понял

                              0
                              Вы не сможете использовать загрузчик (именно ту часть, которая конфигурирует USB в DFU режим). Если у вас BOOT0 в «0», то запустится лишь алгоритм выбора банка памяти для загрузки (выбор производится по наличию корректного указателя на стек), при этом сначала проверяется второй банк, затем первый. Если оба банка не содержат прошивку, запустится встроенный загрузчик.
                              Загрузчик также запустится, если BOOT0 будет в «1» и не будет проверять банки памяти.
                              Т.е. в вашей ситуации BOOT0 в «0», раз вы работаете из внутренней памяти, и в том банке, в котором вы работаете точно есть правильный указатель на стек — следовательно в загрузчик таким образом не попасть.
                              Вся эта информация есть в AN2606.
                                0
                                Обратите внимание на FLASH OPTION BYTES. Иногда, зависит от проца и зашитого в нем бутлодера, проц прыгнет но загрузчик не сработает если есть защита от чтения-записи
                                0
                                1. Из приведенного рисунка никак не следует, что аналоговый режим отключает ТШ — это точно?
                                2. А все таки, почему измерение не в нижнем плече — и у TI в их отладке датчик стоит в питании и у Вас — просто потому, что потребуется минусовое питание для операционника, или есть еще основания? Хотя у TI операционник все равно имеет питание +12В и -12В.
                                  0
                                  Судя по рисунку, в режиме аналогового входа подключается подтяжка к + питания, триггер шмидта не отключается но оказывается обезврежен. Впрочем, это решение лишь частичное — всеравно будет существовать уровень помехи который приведёт к опрокидыванию триггера, например тыкнуть антенной радиостанции работающей на передачу в девайс(у меня бесперебойник сходит с ума на 20см от работающего 2Вт передатчика).
                                    0

                                    Почему тогда просто не сделать это ноги входами с подтяжкой к питанию? Видел как-то такой совет на хабре.

                                      0
                                      Встроенная обычно очень слаба и только повышает порог помехи которая может просочится на вход, например близкий статический разряд от человеческих рук(модель обычной человеческой статики — 100В@1кОм, это та что без видимых искр и под которую рассчитывают ESD-защиту).
                                      Может, вы слышали проблему с ардуиной если понядеется на встроенную подтяжку вывода сброса(а там где-то около 50кОм) то она перезагружается от статики и даже от переключения реле рядом.
                                        0
                                        У меня на ардуино аллергия, извините. О рекомендациях Atmel слышал, что нужно подтягивать ресет. Но я и не знал, что внутри там еще своя подтяжка есть.
                                      0
                                      Судя по рисунку, в режиме аналогового входа подключается подтяжка к + питания

                                      Судя по рисунку (Очень интересный рисунок, кстати. Первоисточник я не нашёл. Во всех reference manual'ах схема чуток другая) в режиме аналогового входа НЕ подключается подтяжка. Подключается некая паразитная фигня со встречно включённым диодом в комплекте.
                                        0
                                        Первоисточник я не нашёл


                                        AN-не-помню-номера, посвященный как раз снижению энергопотребления. Чуть ли не единственный, в котором необходимость переключения в AIN вообще рассматривается более подробно, чем упоминанием в одну строчку.
                                          0
                                          Вероятно, речь о AN3430 How to achieve the lowest current consumption with STM32F2xx. Ревизия документа одна единственная.
                                          Но там вообще нет картинки со структурой порта.
                                          Ну да чёрт с ним, смысл везде одинаковый, а есть ли этот parasitic resistor, или нет — не суть важно. Главное, что он при номинальных режимах (напряжение на ножке не ниже земли и не ниже питания) не влияет.

                                          UPD. Нашёл. Эта картинка из AN4899 STM32 GPIO configuration for hardware settings and low-power consumption. На сайте ST его нет (!!). Надо будет покурить этот документ…
                                      +1
                                      В меня сейчас точно кинут тухлым помидором, но даже б-гмерзкий CubeMX предлагает загнать неиспользуемые ноги в аналоговый режим для экономии питания, причём не только для L семейства.
                                        0
                                        1) Точно
                                        2) Не люблю плавающую землю, даже если речь о масштабе 100 мВ
                                        0
                                        я перехожу по адресу + 1
                                          0
                                          UT120C имеет разрешение 0,1 мкА при паспортной точности ±1% ±3 разряда

                                          Погрешность, увы, указана для полной шкалы а это порядка 1мка в вашем диапазоне, что говорит о том что потребление контроллером измерено… на уровне погрешности измерения прибора и реальное запросто может отличаться на +-50% от того что в данном случае показал ваш прибор. Но это обычно не проблема, вы же измеряете токи для сравнения друг с другом на одном и том же приборе.

                                          положения «mA» и «uA» практически во всех приборах на шкале рядом, гнёзда для измерения на обоих диапазонах одни, так что можно спокойно запустить контроллер на пределе «mA», а когда он уйдёт в сон, перещёлкнуться на «uA» — это происходит достаточно быстро, чтобы контроллер не успел потерять питание и перезагрузиться.

                                          Токовый шунт на эти диапазоны прибора как правило один единственный и НЕ КОММУТИРУЕТСЯ вообще. Даже выключите прибор цепь не будет разорвана. Сделано это с одной единственной очевидной целью — чтобы на результаты измерения не влияло переходное сопротивление контактов переключателя(что механического, что электронного), и по этой же причине выводы измерения тока находятся отдельно.
                                            0
                                            Как вы себе представляете на одном шунте измерение тока от долей микроампера до 400 мА? Чисто технически? Вы либо на одном конце диапазона влетите в доли микровольта, либо на другом — в доли ватта.

                                            Шунт, разумеется, переключается.
                                              0
                                              Не знаю, ни в одном из моих более менее серьёзных приборов шунт не переключается. Один резистор и один вход под диапазон до 200(400)мА, другой для диапазона 10А. Причем они включены последовательно и жестко связаны. Может быть в дешёвых приборах шунты и переключаются…
                                                0
                                                В хотя бы одном из ваших серьёзных приборов микроамперный диапазон вообще есть?

                                                Не 200(400) мА, а микроамперный.
                                                  0
                                                  Хм, и правда переключается. На одном 5Ом/500Ом, на другом 2Ом/100Ом.
                                                  Но на третьем приборе, где разрешение в 10мкА там фикс шунт.
                                            0
                                            Для измерения есть ещё такая штука как логарифмический операционник. Скомпрессировать 5 декад а 10 бит как нефиг делать. Там и на 8 декад бывают.
                                            Более того, тут даже подойдёт «ненастоящий», от AD, где как раз внутри батарея операционников с разным коэффициентом и схема коррекции. Честный, на паре транзисторов, такой как TI LOG112/LOG114 не нужен.
                                              0
                                              От AD это Вы про какой?
                                                0
                                                AD8305/AD8307, например.

                                                Кстати, я совсем забыл (сам я для своей задачи выбрал TI LOG112), у них есть ещё и классические «двухтранзисторные», AD8304/AD8305, с токовым входом.

                                                И трёхдекадный (если хватит) ADL5306.

                                                В общем, выбор богатый.
                                              0

                                              Хорошая статья. Жаль только, что отладочные платы для LoRa у вас нельзя купить, чтобы на практике эти знания обкатать.

                                                0
                                                Я у TI купил )
                                                  +1

                                                  А можете поделиться ссылкой или названием платы?

                                                    0
                                                    присоединяюсь к вопросу.
                                                      0
                                                        0
                                                        А где, простите, там LoRa?
                                                          0
                                                          Там есть приемопередатчик диапазона 868 с достаточной шириной канала и выходной мощностью 16 дбм — все остальное в Ваших руках.
                                                            0
                                                            Этот передатчик умеет FSK и 802.15.4, сделать на нём LoRa-модуляцию… проблематично.
                                                              0
                                                              Конечно, задача непростая, даже если бы был доступ к радио ядру и его периферии, но если его совсем нет, то, пожалуй, что и нерешаемая.
                                                  0
                                                  а кто «круче» в плане малого потребления STM or EFM?
                                                    0

                                                    Интересно, а если не использовать OS, то можно получить ещё меньшее потребление?

                                                      +2
                                                      Большая часть статьи про режим STOP, как наличие ОС сказывается на потреблении в нем?
                                                        +1

                                                        Ну не в режиме стоп, а в режиме ран конечно.
                                                        Статья же называется оптимизация потребления;)

                                                          0
                                                          В RUN потребление зависит от тактовой частоты и количества активной периферии, от выполняемой задачи — нет, соответственно и от наличия ОС. Для экономии используют комбинацию режимов RUN, SLEEP (для коротких простоев) и STOP (для длительных ожиданий и если нужные Вам ожидаемые события способны вывести из этого состояния). Наличие ОС влияет только на организацию переходов между RUN и SLEEP. При наличие ОС переход в SLEEP обычно выполняют при входе в idle задачу. Без ОС, теоретически, Вы сможете сэкономить, если все Ваши задачи, кроме начальной инициализации, выполните полностью в обработчиках прерываний, при выходе из которых сразу возвращаться в SLEEP (или в более глубокий сон, если это возможно и ждать долго). Возможность и целесообразность такого решения зависит от конкретной задачи.
                                                            0
                                                            Ну в общем я так и делаю, в беспроводных сенсорах.
                                                            Только ни когда не использовал OS, поэтому хотелось бы сравнить цифры.
                                                              0
                                                              В ОС тоже ничто не мешаёт выполнять хоть вообще всё на свете только в обработчиках прерываний (кроме того, что в куче задач будет или адская каша, или выполняющиеся десятки и сотни миллисекунд обработчики).
                                                              0
                                                              Tickless ОС (в которой нет периодического таймера, тикающего независимо от наличия и периода выполнения задач в шедулере) на потребление не влияет.
                                                          0
                                                          В кубе можно поставить галочку: переключать неиспользуемые пины в аналоговые. Однако, если делать через HAL то переключает, если делать через LL включает высокий уровень на выход.
                                                            0
                                                            Очень полезную железку сделали. Надо б заморочиться, сделать подобную — регулярно требуется, а нету — приходится мультиметром тыкать…
                                                            К слову, у TI есть ещё один вариант измерителя: на программном DC/DC, который считает время включения/выключения ключа. Попробовать руки не дошли, к сожалению (вроде б тексасовцы отладку эту дарили...).
                                                            Подробности: схема, описание.
                                                              0
                                                              Сейчас собираюсь сделать приспособу для измерения такую:
                                                              Шунт + усилитель с большим КУ. Один канал осциллографа вешаем на шунт напрямую, второй к выходу усилителя. По первому каналу осциллографа смотрим потребление в рабочем режиме, по второму во время сна. Может не так точно, но проще некуда.
                                                                0
                                                                Во-первых, точность будет низкая, осциллографы обычно всего лишь 8-битные, во-вторых, автоматический логгер даёт отличную возможность оставить железку в рабочем режиме на неделю, а потом придти и посмотреть её среднее реальное потребление.
                                                                0
                                                                  0
                                                                  Очень интересная статья, спасибо, почти через все описанное пришлось столкнуться.
                                                                  Еще Вы не указали, что для серии L1 в блоке RTC есть еще один таймер — «Periodic auto-wakeup». Он также пробуждает контроллер из режима Stop, и его можно запрограммировать с периодом от 122 мкс до 32 секунд.
                                                                    0
                                                                    Он не подходит для организации нормальных часов, т.к. невозможно узнать, сколько натикало в данный момент.

                                                                    Его можно для каких-то одиночных задач использовать, типа сброса сторожевого таймера.
                                                                      0
                                                                      Так он и не предназначен для этого, проснулись по сигналу считали RTC и радуемся. Или ведём собственный подсчет.
                                                                        0
                                                                        Какой смысл нам просыпаться по его сигналу, если потом всё равно приходится считывать RTC, чтобы понять, сколько сейчас времени? Какую задачу это решает?
                                                                          0
                                                                          Можно использовать и в других задачах, кроме сброса сторожевого таймера. По-моему, используется где-то в проектах именно такой механизм, когда необходимо проверять состояния входов с заданной периодичностью (например, при опросе различных счетчиков с импульсными входами).
                                                                    0

                                                                    Поясните, пожалуйста, момент, зачем нужно irq_disable перед WFI. И что делает irq_disable — cpsid i или что?

                                                                      +1
                                                                      cpsid i

                                                                      Чтобы ничего не вылезло между моментом, когда мы собрались засыпать, и моментом, когда мы не только проснулись, но и восстановили частоты (обработчик прерывания, пытающийся что-то сделать на проце, работающем на 65 кГц после LP Sleep — это душераздирающее зрелище).
                                                                        0

                                                                        Спасибо, очень наглядно. Увидел, что Вы это и в статье объясняли, просто я сразу не разобрался.

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

                                                                    Самое читаемое