Мультиплексирование кнопок и LED-дисплея

В статье «Динамическая индикация: экономим выводы МК» мы разобрались с семисегментными LED-индикаторами и научились работать с ними в динамическом режиме. В этой статье перейдем к устройствам ввода информации в девайс, а именно к механическим кнопкам.

Когда мы подключали семисегментный индикатор в динамическом режиме, то мы выяснили, что тратим мы 9 контактов МК + один контакт на каждый дополнительный индикатор. Если мы хотим прицепить еще и кнопки, с помощью которых можно управлять нашим прибором, то нужно потратить еще некоторое количество выводов микроконтроллера. А можно и не тратить, а подключить их к тем де контактам МК, что и индикаторы, как и сделано в говорящих LED-часах. Там рассказано, как сделать так, чтобы все это правильно работало. Если кратко, то перед каждым новым обновлением дисплея мы настраиваем порты микроконтроллера, к которым подключены кнопки на вход и считываем значение с него. После этого возвращаем все как было. Реализуем один из вариантов этого алгоритма.

Перед началом работы, поговорим немного о таком явлении, как дребезг контактов. Это несколько быстрых замыканий и размыканий контактов кнопки в момент нажатия. Возьмем кнопку без фиксации от корпуса ПК и проверим ее на ее дребезг. Соберем следующую схему:

Рис. 1. Исследуемая схема с кнопкой

К выводу OUT подключаем осциллограф и смотрим, что там происходит:

Рис. 2. Осциллограмма сигнала на кнопке в момент нажатия

Что и требовалось ожидать! Перед нажатием напряжение на кнопке составляет +5,2 вольта, потом мы на нее нажимаем и начинается куча переключений, затем все устаканивается и на кнопке появляется ноль. Переходный процесс занимает примерно 280 мкс. Чтобы понимать масштаб времени приведу пример. При моргании человек закрывает глаза примерно на 100 мс, или 100*1000=100000 мкс. А дребезг контактов у нас составляет 280 мкс. Однако, микроконтроллер, работающий на частоте 8 МГц одну команду выполняет за 0,125 мкс, так что если не принять специальных мер, то одно нажатие юзера на кнопку микроконтроллер воспримет как несколько, что нехорошо. Бороться с дребезгом будем программным способом.

За основу возьмем схему и программу из статьи про динамическую индикацию. Кнопки подключил к тому же порту, что и сегменты индикатора. В итоге, получаем такую схему:

Рис. 3. Схема подключения индикатора и кнопок к AVR

Теперь займемся программированием. На этот раз потренируемся в IAR AVR 5.40.0. Вся задача заключается в доработке функции DispProcess(), которая отвечает за вывод информации на LED-дисплей. Вот код модифицированной функции:

Сразу после AllDigitsOff() вставляем конечный автомат, который занимается опросом кнопок. Он выполняется в начале каждого цикла обновления дисплея, когда led_index == 0. Таким образом, при частоте обновления 100 Гц, опрос кнопок выполняется так же с частотой 100 Гц. Для начала настраиваем порт, к которому подключены сегменты индикатора на вход и включаем внутренние подтягивающие резисторы порта. Далее, в c_btns читаем, что у нас на выводах. Далее, заходим в сам конечный автомат. Начальное состояние у нас 0 (ноль). В нем мы проверяем, не нажата ли какая-нибудь кнопка, и если это так, то в last_btns сохраняем значение на входе для дальнейшего его использования и переходим в состояние 1. Если какая-либо кнопка нажата, то соответствующий бит в c_btns будет сброшен в ноль.

Через 1/100=10 мс наступает следующий цикл работы конечного автомата и мы попадаем в состояние 1. Тут происходит проверка текущего состояния кнопок c_btns с ранее сохраненным состоянием last_btns. Если они равны, то это означает, что нажатие действительно произошло и все переходные процессы, связанные с дребезгом контактов завершены, и можно в переменную ButtonsStatus занести зажатую кнопку и перейти в состояние 2. Если же условие c_btns == last_btns не выполнилось, то сразу переходим в начальное состояние.

Через еще 10 мс мы попадаем в состояние 2, в котором будем находиться до тех пор, пока пользователь не отпустит все кнопки. Как только это произошло, переходим в начальное состояние 0, и система снова готова регистрировать нажатия.

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

Предполагается, что где-то в бесконечном цикле будет производиться непрерывный вызов функции GetButtonsStatus(). Поэтому, чтобы программа, использующая GetButtonsStatus() не воспринимала нажатую кнопку как многократное нажатие, эта функция сбрасывает флаг нажатой кнопки после своего вызова. Еще тут есть один небольшой момент. Возможна ситуация, при которой мы вызываем GetButtonsStatus(), выполняется  tmp = ButtonsStatus, затем возникает прерывание, в котором переменной ButtonsStatus присваивается какое-то значение. Затем возвращаемся из прерывания назад, и выполняем ButtonsStatus = 0!!! Все, мы потеряли одно нажатие на кнопку. Это называется нарушение атомарности выполнения операций. Может возникать, когда к одной и той же переменной обращаются из разных потоков. Нарушение атомарности в некоторых случаях вызывает появление плавающего бага, который очень трудно отловить. Поэтому эти две операции помещаем между __disable_interrupt() и __enable_interrupt(), тем самым решаем проблему.

Ну и демонстрация. Сделаем счетчик, значение которого можно увеличивать и уменьшать кнопками + и -, причем считать он может от 0 до 99. Для этого добавим еще немного кода:

Собственно все)) Вот демонстрация работы для наглядности:

В данном примере используются только 2 кнопки, однако их количество можно увеличить до 8-и без изменения программы опроса. Только в main() нужно будет добавить обработчики нажатий на эти кнопки.

Архив с исходниками и моделью Proteus 8: https://yadi.sk/d/9JCOlRxc3NxGXD

 

 

Закладка Постоянная ссылка.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *