Довольно часто можно встретить статьи, посвященные использованию режимов пониженного энергопотребления. В большинстве случаев в них описываются достоинства и недостатки конкретного микроконтроллера, а все рекомендации сводятся к обобщенной фразе — используй режимы сна.
В данной статье, мне бы хотелось немного углубиться в эти рекомендации и поведать читателю о методах снижения энергопотребления, с которыми довелось столкнуться лично при разработке одного из устройств.
Предыстория такова, что потребовалось разработать логгер аналоговых сигналов. Схемотехнически никаких сложностей:
— 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 суток.
Автор: Darti