Опыт разработки low power устройств на STM32L

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

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

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

    — 11 каналов АЦП
    — Bluetooth
    — SD карта
    — OLED дисплей 128х64
    — питание от одной батарейки ААА

    Идея была такова: пользователь включает прибор, с помощью кнопок и дисплея настраивает параметры и запускает измерения. Далее каналы оцифровываются и сохраняются на карту памяти. Опционально можно включить Bluetooth, чтобы любой момент, со смартфона посмотреть измерения в реальном времени, либо выгрузить ранее сохраненные на карту данные. В режиме измерений, устройство должно было жить от одной батарейки ААА 3 суток.

    Для читателя не сведущего в расчетах, приблизительные прикидки:

    В сутках 24 часа, в среднем батарейка ААА номиналом 1.5В отдает 600-1000мАч, поэтому в худшем случае устройство должно потреблять 600/(24*3) = 8,3мА, в лучшем 1000/ (24*3) = 13мА. Но тут есть очень важная особенность: это потребление по 1.5В. Карта памяти и микроконтроллер работают на 3В, поэтому в пересчете на 3В грубо эта цифра должна быть в 2 раза меньше, т.е. 4-6мА. При включении Bluetooth и дисплея требования были более «мягкие», поэтому их не учитывали.

    В выборе платформы сомнений не было — STM32, в основном из-за доступности и рекомендаций других разработчиков, да и платформа давно освоена. L0 не подошла из-за отсутствия камней с необходимым количеством фарша, поэтому выбор был сделан в пользу STM32L151. Были мысли насчет STM32L4, но цена на тот момент оказалась выше, и явных причин для его выбора не было.

    На тот момент, у меня не было опыта разработки именно low power устройств, но в целом, требования и предварительные расчеты энергопотребления по даташиту + cubemx, на первый взгляд показывали, что все должно сойтись. Чтобы немного проникнуться, включите обычный микроконтроллер, например STM32F103 на максимальную частоту 72МГц и увидите потребление только одного процессора в десятках мА, без периферии. Даже обычный красный светодиод потребляет 10мА по 1.9В. Поэтому предполагалось что устройство будет большую часть времени спать.



    Схемотехнических особенностей устройство не содержит. Использовалась уже проверенная схема питания. Из 1.5В накачивалось в 3В, АЦП часть питалась от 2.5В. При снижении питания до 0.95В устройство отключалось.



    Первая проблема, с которой пришлось столкнуться — как получать данные с АЦП. По факту было 11 каналов, каждый из которых оцифровывался на свой частоте, кроме того некоторые каналы 12 битные, некоторые 8 битные. Суммарный поток данных около 6.5кБайт в секунду. Вариантов было два: 1. забирать через DMA. 2. запускать преобразования по таймеру и забирать в прерывании.

    Неискушенный читатель скажет, что DMA+таймер рулят, можно запустить преобразования и увести CPU в сон. Однако, в L151 АЦП всего один, поэтому его нельзя запустить одновременно на разных частотах. Если один канал опрашивается с частотой 1Гц, а второй 2кГц, то при работе через DMA придется оба придется опрашивать на 2кГц. Минусы очевидны — дополнительный расход оперативной памяти, дополнительное разгребание буфера с участием CPU, дополнительное потребление DMA.

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

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

    Не смотря на разнообразие режимов сна у L1, они довольно сильно ограничены. Например, вы хотите запутить DMA+ADC и отключить CPU? Тогда единственный режим, который вы сможете заюзать это Sleep, потому что АЦП тактируется от HSI, а HSI работает только в Sleep.



    Темой дальнейших исследований, стало какой интерфейс использовать для карты памяти — SPI или SDIO. К сожалению, деталей точно не помню, но при посекторной записи потребление было приблизительно одинаковое, но в мультиблоке из-за соотношения скорость записи/энергопотребление выбор был однозначно в пользу SDIO.

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

    Еще одним куском работы стала файловая система FAT. Требовалось, чтобы карта памяти при подключении к ПК была видна на любом компе без дополнительного софта. С учетом особенностей требовался только FAT32. При этом, чтобы данные не терялись при внезапном отключении питания.

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

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

    Что касается самих карт памяти SD, это отдельная история. Не смотря на их распространенность, на 100% достоверных данных о том, как работают карты на просторах Интернета просто нет. Информацию пришлось собирать по крупицам. Проблемы, с которыми мне довелось столкнуться две: 1. По-разному потребляют 2. По-разному работает перенос секторов.

    С проблемой 1, бороться просто невозможно. Простой пример. Берешь карту — записываешь сектор, на это тратится N мА. Перестаешь писать — карта продолжает потреблять эти N мА еще например 64мс, ничего не делая. Берешь другую карту, она сразу же после записи сектора перестает потреблять.

    Проблема 2. Возможно кто-то слышал, что есть такая штука как wear leveling, вкратце это контроллер внутри карты памяти, который следит за тем чтобы сектора на карте изнашивались равномерно. По всей видимости единого стандарта на этот счет не существует, а скупое упоминание этого попадалось только (если не ошибаюсь) у toshiba. Поэтому есть причины полагать, что работает этот контроллер совершенно по-разному в разных картах. А в некоторых картах, вообще отсутствует.

    Проявляется это так, пишешь много раз в один и тот же сектор. У тех карт, у которых по видимому нет контроллера, через N записей сектор перестает читаться. У других карт, через определенные промежутки, время записи резко увеличивается однократно. Причем тесты показали, что это время может доходить до 1 секунды. Да, да, в этой цифре нет никакой ошибки. Вот пример записи 5000 раз в один и тот же сектор, видим периодическое увеличение времени записи.



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

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

    Итого, на STM32L151 удалось влезть в 10мА по 1.5В. В процессе разработки появилось много дополнительных хотелок, поэтому изначальная идея о том, что устройство будет спать оказалась в корне не верна. В целом это подходило под требование 3 суток, однако, выяснилось, что они должны включать 4мА дополнительной платы заказчика :). Единственной надеждой стал перенос проекта под STM32L4.

    Главным козырем STM32L4 стало 3 АЦП, вместо одного. Чем это хорошо? Вы можете запустить каждый АЦП преобразовывать независимо, т.е. АЦП+ДМА+таймер, больше не давала такого оверхеда как в L1. Теперь можно было сгруппировать каналы по количеству отсчетов за секунду. Это дало возможность уходить в сон для процессора чаще и тратить на разгребание буфера минимум времени.

    Сравните систему тактирования АЦП для L1



    И для L4



    Как уже говорилось ранее, измерения в L1 довольно ограничены. Например, АЦП тактируется напрямую от HSI, если вам нужно что-то измерять, то HSI должен быть включен на 16МГц и никак иначе. В STM32L4 практически все настраивается независимо друг от друга. АЦП можно тактировать от любого генератора.

    Самым приятным сюрпризом стал тактовый генератор MSI. Да, он есть и в L1, однако, как говорилось выше, от него нельзя завести АЦП. В моем случае, разница в потреблении между HSI 16МГц и MSI 8МГц была просто огромная.

    А вот исследования зависимости тактовой частоты от энергопотребления показали, что уменьшение тактовой до 4МГц не дает сильной разницы в потреблении, однако производительность сильно падает.

    Тактовые сигналы для остальной периферии, теперь тоже стало возможным занизить. Были и еще бонусные штуки у L4, вроде low power тактирования SDIO и ADC, когда тактирование включается непосредственно в моменты передачи. Применение аппаратного модуля CRC так же дало свои плоды. Сюда же, можно включить еще одну фичу, можно настроить периферию так, чтобы она автоматом отключалась при входе в Sleep.

    Существенным шагом, который позволил добиться результата, стало применение сжатия. Точнее, в первой версии устройства оно тоже присутствовало, использованный алгоритм был разработан заказчиком специально для этого устройства. Однако, тесты показали, что LZ4 жмет значительно лучше и тратит значительно меньше CPU. В среднем из 6.5кБ получалось 1,5-2кБ данных.

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

    В заключение могу сказать, что переход и цена L4 абсолютно оправданы для устройств, где требуется действительно низкое энергопотребление. В конце концов удалось достичь желаемого потребления 4.5-5.5мА по 1.5В. На тестах девайс успешно прожил от одной батарейки более 3 суток.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 25

      +1
      У EFM32 в спячке меньше и более продуманные.
        +7
        У вас энергопотребление довольно высокое, а вот будете работать с очень долгоживущими — годы на батарейке — устройствами, столкнётесь с ещё одной вещью, которая тривиальна, но про неё при этом говорят хорошо если в одном случае из десяти.

        Это GPIO.

        Они по умолчанию сконфигурированы как цифровые входы с триггером Шмитта. Если ножка висит в воздухе, у неё на входе шум, триггер Шмитта от этого шума дёргается туда-сюда. И жрёт. Потерять таким образом полсотни микроампер — как нефиг делать, при собственном потреблении МК в STOP + RTC в районе 1,5 мкА.

        Иногда советуют перевести все входы в состояние AIN — те же входы, но без триггера Шмитта.

        Так вот, это совет правильный, но только наполовину.

        Вторая половина заключается в том, что при наличии внешней цифровой периферии с входами, подключённым к GPIO МК, при переводе последних в AIN… оказавшись фактически в воздухе, жрать начинают уже входы периферии. Если это вход, от которого тактируется какой-нибудь SPI — там можно легко влететь уже в сотни микроампер.

        Поэтому:
        1) GPIO, работающие выходом на цифровую периферию и не имеющие внешней подтяжки, перед уходом в сон ставятся в состояние входа с подтяжкой или выхода, уровень соответствует неактивному состоянию периферии (SPI CS = 1, UART TX = 1 и т.п.)

        2) Остальные GPIO отправляются в AIN

        По этой же причине в Standby можно влететь в нехилое такое «мусорное» потребление периферии, если не предусмотреть на всех исходящих сигнала МК внешнюю подтяжку.
          0
          Вообще, для действительно низкопотребляющих случаев такие проблемы лучше решать на уровне топологии ПП.
            0
            Повесить на уровне топологии ПП на все шины внешние подтягивающие резисторы — очень так себе решение в качестве замены нескольких строчек кода.
              0
              Отличное решение.
              1) подтяжка внешняя может быть меньше внутренней и находиться до стабилизатора напряжения
              2) несколько строчек кода это 5-15 тактов, которые ядро могло бы и проспать.
                0
                Если подтяжка будет до стабилизатора, то будет фигня и перетекание тока куда не положено. Уменьшенная подтяжка просто не имеет смысла — когда она нужна она находится в неактивном состоянии — по подтяжке ток не течет и нет разницы сильная она или слабая, вот если что-то будет тянуть подтяжку в другую сторону, тогда надо думать. Кроме того слабая подтяжка уменьшит помехозащищённость(проверьте идею абсурдным увеличением подтягивающего резистора до величины в 1ГОм).

                Если 5-15 тактов настолько критичны к энергопотреблению, тогда зачем тактирование в 72Мгц? У вас там рабочий цикл измеряется в сотнях тысяч тактов… 15 тактов — это экономия на блохах.
                  0
                  1) подтяжка внешняя может быть меньше внутренней


                  Не играет никакой роли. В STM32 внутренняя подтяжка — 50 кОм, если вам этого много, поставьте во сне ножку в выход, будет подтяжка нулевого сопротивления.

                  2) несколько строчек кода это 5-15 тактов, которые ядро могло бы и проспать.


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

                  Не говоря уже про место на плате и число компонентов.
            0
            Можно взять измеритель тока (100nA — 50mA) и посмотреть реальное потребление устройства во всех режимах.
              0
              Я смотрел осциллографом падение напряжения на 1Ом резисторе. Метод вполне себе достоверный. Как упоминалось в статье, основная проблема это карта памяти, которая потребляет весьма неравномерно, иногда это короткие импульсы, а иногда прямо жрачка на несколько десятков миллисекунд.
                0
                Не могли бы вы привести свои данные по карточкам, которые вы использовали/замеряли потребление тока? По моим сведениям карты памяти от 40 до 150 мА могут кушать при записи. От производителя зависит. Заранее спасибо.
            0
            Спасибо за статью.
            Если не секрет, то какой Bluetooth вы использовали в этом проекте?
              0
              Использовался RN4678, потому что нужен был Dual mode и переконфигурация на ходу. Кроме того, он может работать на скорости 460800. Однако, модуль очень сырой, чтобы он завелся пришлось очень очень долго долбить Microchip.
              0
              Кстати, ничего не сказано про измерительные каналы. Если они на операционниках, то там ещё и инвертор напряжения должен стоять и сами ОУ потреблять будут. Если не секрет, что измеряете?
                0
                У меня почему-то с L4 постоянные проблемы, то Куб забудет закинуть часть исходников для сборки (FatFS например), то до какого-то момента все глобальные переменные всегда были равны 0 даже при явном ненулевом определении (использую связку: Куб, arm-none-eabi-gcc, makefile и VSCode).
                  0
                  А почему используется батарейка, а не аккумулятор?
                    0
                    так пожелал заказчик
                    +3
                    10 мА — не low-power

                    — Bluetooth
                    — SD карта
                    — OLED дисплей 128х64
                    — это тоже не low-power.

                    Количество данных настолько огромное что нужна жирная uSD — флешка? Нельзя было обойтись SPI low-power флэшкой?
                    Где схемотехника? В низкопотребляющих устройствах она не меньше важна чем софт.
                      0
                      В статье все написано, получаемый поток 6.5кБайт в секунду, за сутки 6.5*60*60*24 ~ 500мб.
                        0

                        я бы тогда сделал так: простой промежуточный буфер на SPI low-power флэшке на 1-2 мб, тогда SD карту как конечное хранилище пришлось бы включать очень редко. SD на низких скоростях весьма прожорливы (по сравнению с таким хорошим МК и прочей переферией).

                          +1
                          Есть SPI-SRAM. Или SPI-FRAM использовать, но цены на них пока ещё кусаются. На жалкие 32кБ FRAM-чипа можно взять флешку на 4...8Гб.
                          А эту мелкую флешку-буффер таким использованием можно затереть до дыр очень быстро.
                          • UFO just landed and posted this here
                        0
                        Я правильно понял, что на входе у вас стоит step up импульcник и постоянно молотит. Он же тоже потребляет. Да не много, но постоянно. Не измеряли сколько оно ест во время сна контроллера?

                          0
                          Молотит постоянно, оба питальника вместе потребляли 200мкА.
                          +2
                          Low power — это потребление в микроамперах и работами годами на круглой плоской батарейке.

                          Only users with full accounts can post comments. Log in, please.