STM32 — грамотно включаемся от кнопки
- понедельник, 18 ноября 2024 г. в 00:00:10
Наличие у контроллеров STM32, да и практически любых других, режима энергосбережения STANDBY, который фактически представляет собой полное отключение (работает только RTC и сторожевой таймер, потребление составляет первые микроамперы, а состояние не сохраняется), дает возможность простейшим способом сделать включение и выключение устройства на таком МК нажатием кнопки, в том числе и задействованной под другие функции, без каких-либо дополнительных элементов. Есть, однако, несколько подводных камней, и в этой статье я расскажу, как на них не попасть.
Итак, мы вешаем кнопку, соединяющую при нажатии вход PA0/WKUP, подтянутый к земле, с питанием. Нажатие этой кнопки будет пробуждать МК, а затем эту линию можно настроить, как вход, и при ее нажатии (лучше длительном) вызывать процедуру перехода в режим STANDBY, если необходимо — с запросом подтверждения, предварительным сохранением данных, выключением вторичных источников питания и прочее. При этом не забудьте про дребезг: если выключиться, пока кнопка нажата, то с некоторой вероятностью все включится обратно в момент отпускания кнопки. Поэтому лучше дождаться отпускания, сделать маленькую паузу и тогда уж в STANDBY.
Обратите внимание: вход WKUP активизируется по нарастающему фронту.
Все это замечательно работает, на первый взгляд. Но если мы попробуем воплотить это в железе, мы встретим ряд граблей.
Сначала вы обнаружите, что это решение не дружит со сторожевым таймером IWDG. Режим STANDBY для него эквивалентен зависанию процессорного ядра. Код прекратит исполняться, собаку вовремя не покормят и она в заданное время нажмет на RESET. МК запустится с нуля и включит все обратно. Предотвратить это невозможно, IWDG можно только запустить и нельзя остановить. При этом есть определенная сложность в том, чтобы надежно отличить, что у нас произошло — начальный запуск по включению питания, выход из STANDBY — или же сработал сторожевой таймер: во всех случаях процессор стартует с чистого листа, сбросив все*). В том числе, почему‑то иногда оказываются сброшены и флаги причин сброса в RCC_CSR, которые должны использоваться в таких случаях по замыслу разработчиков STM32. Из‑за этого на эти флаги нельзя положиться — рано или поздно устройство, проснувшись от собачьего лая и поглядев во флаги, решит, что надо включаться. Я столкнулся с таким поведением на контроллере STM32L151, прибор на котором раз в несколько дней самопроизвольно включался от IWDG.
На самом деле, решение проблемы простое. Надо просто сразу после старта МК, вместо того, чтобы смотреть во флаги в RCC_CSR, проинициализировать порт PA0, к которому подключена кнопка, как вход, и тут же поглядеть, что там. Если нажали кнопку — вряд ли ту кнопку успеют отпустить за те десятки‑сотни микросекунд, что пройдут от старта МК до запуска функции main(), и мы увидим единицу на PA0. В этом случае продолжаем — все включаем, запускаем и работаем по программе дальше. Если же это IWDG обресетил МК, на порту, очевидно, будет ноль, и в таком случае мы просто сразу уходим обратно в STANDBY.
Будьте осторожны! Если вы самонадеянно посадили BOOT0 на землю на плате прямо под корпусом МК, а от SWD развели только две линии и землю (никогда так не делайте!), косяк при программировании в этом месте приведет к тому, что вы окирпичите контроллер, и чтобы раскирпичить его обратно, вам наверняка придется его выпаивать. Дело в том, что если МК уходит в STANDBY сразу же, как стартует, ST-Link к нему уже не подключится, и единственный способ выйти из этого положения - предотвратить запуск кода, переключив BOOT0. Иногда помогает Connection under reset, но только в случае, если между инициализацией и STANDBY проходит хоть какое-то время.
____________
*) По неизвестной причине и вопреки документации, после сброса по NRST, IWDG и выхода из STANDBY у многих, если не всех, МК семейства STM32 сохраняется содержимое оперативной памяти. Если переписать стартовый код, чтобы он не затирал при старте какую-то часть ОЗУ, это тоже можно использовать для распознавания состояния, записав туда перед засыпанием какой-нибудь 0xDEADBEEF. Но поскольку это недокументированная возможность, рекомендовать ее эксплуатацию нельзя.
Представим, что аккумулятор у нас разряжен, а МК - из тех, у которых нет отдельной ноги для литиевой батарейки (STM32L151/152 один из таковых). Поэтому RTC питаются от основного аккумулятора. На аккумуляторе еще 3,3-3,4 В и с учетом мизерного тока потребления в режиме STANDBY, часы и backup-регистры продержатся еще много месяцев, да и здоровью аккумулятора ничего пока что не грозит. И вот не подозревающий ни о чем пользователь устройства нажимает кнопочку, МК просыпается, поднимает основные питания, DC-DC начинают заряжать свои конденсаторы, еще не дай Махадев, включается дисплей с подсветкой, потребляющей 200 мА - ток потребления подскакивает. И аккумулятор моментально просаживается — до срабатывания его платы защиты. И аккумулятору нехорошо, и часы и backup‑регистры — слетели. Oни, разумеется, не слетят, если у контроллера есть отдельный VBAT, запитанный от батарейки, но аккумулятору все равно станет нехорошо.
Решение — сразу же, убедившись, что нас включают, а не IWDG зря шум поднимает, инициализируем АЦП (или иное приспособление для мониторинга батареи, например, компаратор) и проверяем, что с зарядом аккумулятора. Если плохо — не пытаемся ничего поднять и включить и сразу уходим в STANDBY. Можно разве что коротко пискнуть пищалкой, если она есть. И только если все в порядке, батарея способна поработать хотя бы несколько минут — включаемся полностью и работаем. Напряжение, ниже которого запрещен запуск, нужно подобрать экспериментально для той батареи, что вы применили. Ну и момент, когда гасить прибор по разряду батареи, следует выбрать с некоторым запасом, не стараясь вытянуть лишние тридцать секунд работы — а чтобы можно было неспешно завершить все дела в этом мире, сохранить настройки в NVRAM или Backup‑регистры RTC, а результаты последних операций на карту памяти, корректно закрыть файлы — и только тогда выключиться, оставив аккумулятор не высосанным в ноль, а с небольшим запасом энергии, который позволит ему благополучно дотянуть до зарядки под нагрузкой в виде RTC и других источников микропотребления.
Вы думали, что перейдя в режим STANDBY, МК сохранит состояние ног? Как бы не так! Он их просто бросает, как блондинка из анекдотов — руль. То есть все они оказываются сконфигурированы по умолчанию — как входы, при этом, разумеется, отваливаются и все сконфигурированные ранее внутренние подтяжки.
Следствий этого два: если вы понадеялись на внутренние подтяжки, то зря, в режиме STANDBY их не будет. Если входы имеющейся на плате периферии имеют высокий импеданс и не подтянуты ни к земле, ни к питанию внешними цепями, их состояние окажется непредсказуемым. Вторичные питания, управляемые от контроллера, начнут хаотически включаться и выключаться. Где‑то потечет сквозной ток из‑за того, что напряжение на входе логического элемента оказалось посередине между нулем и единицей. Где-то в углу платы начнет чернеть и обугливаться какой-нибудь резистор. Результат будет мало походить на поведение выключенного прибора и как минимум, он будет бессмысленно пожирать электричество, как максимум — просто не сможет потом корректно включиться (а то и погорит что‑нибудь).
Со входами самого МК та же картина. Входами станут все 144 ноги! Ну, минус питания, земли и вход‑выход кварца. Питание с их логических элементов не будет снято, и все наводки и помехи будут их переключать туда‑сюда. Результатом окажется то, что МК в режиме STANDBY вместо 5 мкА будет потреблять 5 мА.
Решение — ни одного вывода без внешней подтяжки. Куда тянуть — определяется в соответствии с логикой схемы: например, чтобы источники вторичного питания при потере управления отключались, а периферийные устройства переводились в неактивное состояние.
Как справедливо отметил @zurabob,в новых сериях STM32 (в частности, G0) появилась возможность конфигурации внутренних подтяжек и в режиме STANDBY, и этой возможностью нужно воспользоваться.
In the Standby mode, the I/Os can be configured either with a pull-up (refer to PWR_PUCRxregisters (x=A, B, C, D, F), or with a pull-down (refer to PWR_PDCRx registers (x=A, B, C,D, F)), or can be kept in analog mode.
И даже это еще не все. Вас наверняка подстерегут и
Допустим, ваш прибор имеет на борту дисплей, подключенный по SPI или I2C. Хотя бы «народный» 0,9 или 1,5" OLED на SSD13xx.
Вроде такого, ага.
Допустим, вы по его питанию поставили какой-нибудь ключик типа p-МОП транзистора и решили, что проблем с управлением его питанием у вас больше нет. Но как только вы переведете вашу конструкцию в режим STANDBY, вы обнаружите, что дисплей не погас, а только потускнел, стал неприятно мерцать и постепенно заполняться каким-то мусором.
Что случилось? А случился эффект, про который еще Хоровиц и Хилл писали: КМОП-микросхемы иногда могут успешно работать, получая питание не через штатный вывод для его подачи, а через какой-нибудь сигнальный, связанный в этом случае с шиной питания через защитный диод. Разорвав цепь питания, мы оставили подтянутыми к питанию (см. «Третьи грабли») все интерфейсные линии, идущие к индикатору. Через них на дисплей поступает достаточно питания, чтобы он пытался работать.
Решение — оставляя часть схемы без питания, не забудьте позаботиться и о том, чтобы в нее в это время не заходили и управляющие сигналы. Кстати, это не только к странному поведению и паразитному потреблению может привести — может и сгореть. Защитные диоды на то и защитные, что стоят на крайний случай, а не для штатного их применения и постоянного пропускания через них тока.
Столько авторов, которые первый раз открыли документацию на вкладке "First steps" и считают, что немедленно обязаны поделиться своими новыми знаниями с окружающим миром. (@lesskop)
Кто-то может сказать, что написал я об общеизвестных вещах. Но лично мне такая инструкция позволила бы сэкономить в свое время массу времени и сил, да и денег, напарываясь на те или иные вроде бы детские ошибки.