Наличие у контроллеров STM32, да и практически любых других, режима энергосбережения STANDBY, который фактически представляет собой полное отключение (работает только RTC и сторожевой таймер, потребление составляет первые микроамперы, а состояние не сохраняется), дает возможность простейшим способом сделать включение и выключение устройства на таком МК нажатием кнопки, в том числе и задействованной под другие функции, без каких-либо дополнительных элементов. Есть, однако, несколько подводных камней, и в этой статье я расскажу, как на них не попасть.
Базовое решение
Итак, мы вешаем кнопку, соединяющую при нажатии вход PA0/WKUP, подтянутый к земле, с питанием. Нажатие этой кнопки будет пробуждать МК, а затем эту линию можно настроить, как вход, и при ее нажатии (лучше длительном) вызывать процедуру перехода в режим 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)
Кто-то может сказать, что написал я об общеизвестных вещах. Но лично мне такая инструкция позволила бы сэкономить в свое время массу времени и сил, да и денег, напарываясь на те или иные вроде бы детские ошибки.
Автор: jar_ohty