
Привет Хабр! Сегодня хочу поделиться опытом разработки небольшого модуля электронного циферблата на базе семисегментных дисплеев. Помимо этого, также рассмотрим способ устранения «дребезга контактов» механической кнопки, который я подсмотрел в одной известной книге и опробовал самостоятельно.
Вечная проблема
Думаю, каждый, кто работал со встраиваемыми системами, сталкивался с этой проблемой: когда проект требует подключения механической кнопки для самых разных задач, но всегда возникает одна и та же трудность — несовершенство механизма замыкания. То есть, вы нажимаете кнопку один раз, но контроллер воспринимает это как многократное нажатие. А в системах, где логика работы строится на количественном подсчёте нажатий, это может привести к серьёзным сбоям.

Даже подключение кнопки к пину с прерыванием не избавляет от проблемы: прерывание просто срабатывает несколько раз подряд. Конечно, вместо механической кнопки можно использовать сенсорную — она лишена этого недостатка. Однако это не всегда уместно, и внедрить её можно не во все системы. К тому же сенсорная кнопка может срабатывать произвольно — при случайном касании.
Программный метод борьбы
Некоторые решают эту проблему программно, то есть поручают решение этой задачи программной части. Например, когда я работал с одним модулем, мне дали готовую программу, которую необходимо было «допилить». В ней обработкой нажатия кнопки занимались сразу два прерывания: аппаратное и прерывание по таймеру.
Суть заключалась в следующем: после срабатывания первого прерывания по смене логического уровня с высокого на низкий (при нажатии кнопки) запускался аппаратный таймер, настроенный на определённый период — как правило, это 50 мс. После срабатывания таймера прерывание вызывало событие обработки нажатия: увеличение счётчика, смена положения стрелки в меню и т.д.

Метод надёжный, но у него есть недостатки: он требует использования таймера и может увеличивать сложность прошивки, особенно если в проекте задействовано много кнопок или используются другие временные процессы.
Аппаратный метод
Решение, взятое мною является аппаратным, построенным на простой RC-цепи и инвертирующем триггере Шмитта. Преимущество в том, что схема полностью снимает нагрузку с микроконтроллера. Решение я подсмотрел в книге Джереми Блума "Изучаем Arduino".
Вот основные компоненты:
Резистор 220 Ом (по книге — 100 Ом),
Конденсатор 10 мкФ,
Подтягивающий резистор 10 кОм,
Инвертирующий триггер Шмитта (например, 74HC14).
Первая часть схемы работает на RC-цепи, формирующей задержку по смене фронта, длительность которой определяется по формуле: . Подставим значения в формулу и получим 1 мс. Общий график сигнала RC-цепи представлен ниже.
Затем полученный сигнал поступает на инвертирующий триггер Шмитта — устройство, приводящее аналоговый сигнал к стабильным логическим значениям. В зависимости от модели и производителя, триггер Шмитта обладает порогами срабатывания по напряжению: 2,5 В для восходящего и 1,6 В для нисходящего фронта при амплитуде 5 В. Так как мы используем инвертирующий триггер Шмитта, он работает по обратному принципу: когда сигнал преодолевает верхний порог срабатывания, на выходе образуется логический ноль, а когда — нижний порог, логическая единица. Общая схема представлена ниже.

Я собрал данную схему в программе для моделирования электрических схем — Multisim, чтобы на программном осциллографе продемонстрировать работу схемы. Единственное, что я заменил — резистор 100 Ом на 220 Ом. Красным отмечен сигнал после RC-цепи, жёлтым — готовый сигнал, поступающий на пин прерывания после преодоления триггера Шмитта. На осциллограмме видны импульсы, происходящие в моменты нажатия кнопки. Для начала, за счёт RC-цепи происходит нисходящий фронт, и из-за этого на выходе триггера устанавливается логическая единица. Затем происходит плавное нарастание фронта, и, когда он проходит определённую грань, на выходе получается логический ноль. В этом и заключается работа всей схемы.

Пример практического применения: модуль индикации
Модуль представляет собой электронный циферблат на четырёх семисегментных индикаторах и двух светодиодах диаметром 3 мм (используются для индикации секунд, как на цифровых часах).
Архитектура:
-
Индикация организована через два сдвиговых регистра 74HC595.
Первый регистр управляет сегментами (A–G и DP),
Второй — выбором активного индикатора (общего анода или катода).
-
Управление регистрами осуществляется через три линии:
SER (DIO) — данные,
SRCLK (SCLK) — тактирование,
RCLK (LATCH) — защёлка.
Дополнительно используется пин ~OE (Output Enable), на который подаётся ШИМ-сигнал для регулировки яркости дисплея.
Перемычка Jumper1 позволяет выбрать: управлять двумя светодиодами с одного пина, либо независимо с двух.

Ключевым блоком данной схемы является управление механическими кнопками, подключёнными по схеме, разобранной ранее. Выходы с триггера Шмитта выведены на отдельные коннекторы. Индикация циферблата осуществляется с помощью STM32, данные на сегменты поступают по интерфейсу SPI, а управление осуществляется через четыре механические кнопки.
При желании данный модуль можно легко модифицировать, изменив количество кнопок на любое другое.
Внизу можно наблюдать 3D-модель платы, созданную в программе EasyEDA.
Спасибо за прочтение! Если есть что добавить или подправить — пишите, будет любопытно почитать!

Комментарии (4)
Flammmable
01.07.2025 20:20Значительная часть входных буферов любых цифровых устройств представляет из себя нечто очень похожее на инвертор из двух транзисторов. Проблема в том, что если напряжение на входе этого инвертора равно примерно половине напряжения логической единицы, то (при)открытыми оказываются оба транзистора и через буфер начинает течь т.н. сквозной ток. Если фронт достаточно короткий по длительности, то это неприятное промежуточное состояние проходит быстро. Если же фронт делать пологим (как в методе, описанном автором), то возможны различные неприятные эффекты.
По этой причине, а также чтобы не городить кучу RC-цепочек с непонятным разбросом параметров, в реальности, когда требуется избавить от дребезга сразу несколько кнопок, применяют специальные микросхемы подавления дребезга вроде MC14490. Которые после первого фронта на входе, отсчитывают N тактов и лишь затем выдают значение на выходе.
Да, кстати, я уверен, что автор сгенерил в LLM эту и все предыдущие свои статьи. Но учитывая сколько
танцующих с волкамиговорящих с копипастой читателей набежало в его первую статью, я бы сказал, что это не так уж и плохо. Надеюсь, такие статьи (я имею ввиду конкретно "как избавиться от дребезга контактов") через год-другой будут выходить на Хабре каждый день или даже каждый час. Самому автору я желаю поскорей написать статью "как подключить лампочку накаливания к батарейке", пока это не стало мейнстримом.
juramehanik
01.07.2025 20:20Емкость 10мкф крайне перебор. 10нф-100нф на практике достаточно. ну можно просто не вешать обработку кропки на прерывание там где это возможно, столько проблем сразу уйдет.
Danchkin_Sab Автор
01.07.2025 20:20Касательно ёмкости конденсатора могу сказать что путём эксперимента были проверены разные значения, в том числе и 100нф и в конце было выявлено что 10 мкФ оптимальнее всего, а что же касается прерывания бывают моменты когда их использование корректнее всего
Paulus_Engeneer
Можно добавить к программному методу еще один:
Скрытый текст
создаем программный счетчик, который инкриминируется, если кнопка нажата и декриментируется если кнопка отжата (без переполнения);
создаем флаг, который принимает свое значение в зависимости от пересечения верхнего или нижнего установленного порога (обеспечит гистерезис положения нажато/отжато);
добавляем опрос кнопки в бесконечном цикле.
Это позволит сэкономить аппаратный счетчик и деньги на рассыпухе и площади платы