![](https://habrastorage.org/getpro/geektimes/post_images/58e/be7/95c/58ebe795cc6e7d63c1a04ada61733024.jpg)
Чтобы сэкономить порт, разработчики прибегают к невероятным ухищрениям. Например, на одном форуме мне встретился способ управления сдвиговым регистром по одному порту (данные и тактовый импульс) через RC-цепочку. Некоторые такие способы приводят к снижению надежности работы устройства и ухудшению его повторяемости (зависят от параметров конкретного экземпляра микросхемы), поэтому прибегать к ним следует с осторожностью, проанализировав все «за» и «против».
Однако есть способ найти «лишний» порт, который прост, работает стабильно и может быть применен во многих случаях. Это — использование вывода RESET.
На самом деле мне известны целых два способа.
Первый состоит в том, что у МК есть специальный флаг (fuse) RSTDISBL, который, будучи запрограммированным (установленным в «0») превращает вывод RESET в обычный порт ввода-вывода, который можно использовать наряду с другими. Этот способ прост, но имеет один существенный недостаток: после такого переключения МК невозможно будет запрограммировать с помощью низковольтного программатора, обычно самого распространенного типа устройств. Если вы вдруг захотите зашить в МК новую программу, потребуется программатор, использующий напряжение 12Вольт на выводе RESET. Поскольку такие программаторы менее распространены (собственно, их достоинство проявляется только в такой ситуации с переключенным RESET, в остальном они ничем не лучше низковольтных), я считаю этот метод не очень подходящим при разработке (а не копировании готового) устройства.
Второй способ был придуман мною при разработке игрушки «Светофор». По задумке светофор должен был сигналить на два направления (2 красных, 2 зеленых, 1 общий желтый сигнал) и кроме того хотелось, чтобы была возможность переключть режимы работы между обычным и «ночным» (мигающий желтый). Поэтому мне были нужны 5 выходов для управления светодиодами и один вход для кнопки переключения режима.
Самым маленьким в линейке 8-битных AVR-микроконтроллеров является Attiny13 и его современные аналоги ATtiny25, ATtiny45, Attiny85, отличающиеся, в первую очередь, большим объемом памяти. Эти устройства изготовлены в 8-контактном корпусе, поэтому, если вычесть выводы для подачи напряжения питания, остается максимум 6 портов ввода-вывода, но один из них занят RESET, значит остается 5 портов. Этого достаточно для управления светодиодами, но остается нерешенным вопрос с кнопкой.
И тогда мне пришла в голову идея использовать вход сброса для подключения кнопки (см. схему).
![](https://habrastorage.org/getpro/geektimes/post_images/d6f/b3d/78c/d6fb3d78c4e832e922584192001c6220.png)
Работает такой вариант следующим образом (обозначения на примере ATtiny13): в МК есть регистр статуса MCUSR, биты 0..3 которого устанавливаются в «1» в зависимости от того, какая причина вызвала сброс МК:
бит 0 — сброс при включении питания.
бит 1 — сброс по низкому уровню на выводе RESET.
бит 2 — сброс по срабатыванию детектора низкого напряжения питания.
бит 3 — сброс по срабатыванию сторожевого таймера.
В процедуре начальной инициализации МК (вектор прерывания 0) можно проверить регистр MCUSR и выяснить, из-за чего произошел сброс. После проверки нужно очистить регистр, записав в его биты «0».
В своей конструкции я проверял, произошел ли сброс по низкому уровню на выводе RESET. Если да, значит пользователь нажал кнопку переключения режима. В таком случае программа переключает режим работы.
Обратите внимание: независимо от причины перезагрузки МК, все его внутренние устройства будут переведены в исходное состояние. Это означает, что процедура начальной инициализации должна всякий раз заново запустить нужные устройства (таймеры, порты ввода-вывода и т.п.) в нужных режимах. Состояние же регистров и памяти при сбросе по низкому уровню на выводе RESET не меняется (что и позволяет переключать режим или делать другое управляющее действие), однако при сбросе по включению питания или по понижению напряжения питания нужно проинициализировать память и регистры, поскольку значения в них могут быть неопределенными.
Надеюсь, что описанный способ будет кому-то полезен. С удовольствием отвечу на ваши вопросы и предложения.
UPD: Раз уж в комментах зашла речь, приведу еще одну схему, не совсем по теме, но думаю, будет полезно. Стояла задача сделать часы-таймер для игры «Брейн-Ринг». Поскольку дело было в почти полевых условиях и под рукой было мало провода, нужно было придумать, как сделать пульты, чтобы по двухпроводной линии и управлять светодиодом и контролировать нажатие кнопки. Была разработана такая схема:
![](https://habrastorage.org/getpro/geektimes/post_images/8dc/68f/f41/8dc68ff41f407f269d699b1dbc74cebc.png)
Работает следующим образом: когда надо проверить состояние кнопки, подаем на PD0 уровень «0» и проверяем PD1. Когда кнопка будет нажата, там появится «0». Чтобы зажечь светодиод, подаем на PD0 «1», а на PD1 «0». Поскольку специфика игры в том, чтобы ждать, когда кто-то нажмет кнопку, и только потом зажигать его светодиод, то не понадобилось даже динамической индикации — сначала ждем нажатия, потом зажигаем светодиод.
При этом был интересный момент: в отладчике все отлично работало, а при тестировании на макете при старте сразу обнаруживалась нажатая кнопка. Поломав голову, я попробовал добавить задержку между инициализацией порта PD1 на ввод (с подтягивающим резистором) и проверкой его состояния. Оказалось, что из-за емкости длинных проводов до пульта (порядка 10 метров) уровень сигнала не сразу достигал «1» за счет подтягивающего резистора. Задержка была всего в пару микросекунд, но МК работает быстро и это оказалось существенным :)
UPD2: Прошивка для светофора (исходник на ASM и скомпилированный код) лежит здесь. Обратите внимание на строки 56-72, в них находится описанная логика распознавания типа сброса.