Антидребезг: как победить дрожание контактов

Решаем коварную проблему, из-за которой одно нажатие считается десятком.

Дребезг (debounce) — кратковременное дрожание контактов кнопки в момент нажатия: вместо одного чистого перехода вход несколько раз скачет между 0 и 1.

Откуда берётся дребезг

Кнопка — это две металлические пластины. Когда они соприкасаются, происходит микроскопический «отскок»: контакт замыкается и размыкается несколько раз за пару миллисекунд, прежде чем установится стабильно. Глаз этого не видит, но быстрый микроконтроллер успевает прочитать каждый скачок и решит, что кнопку нажали много раз.

Программный антидребезг по времени

Идея проста: зафиксировав изменение, игнорируем новые изменения некоторое время (обычно 20–50 мс), пока контакты не успокоятся. Логику задержки можно проверить на обычном Python — здесь только арифметика и условия, без модуля machine:

# Симуляция: серия «грязных» показаний кнопки с временными метками (мс)
readings = [(0, 1), (101, 0), (103, 1), (104, 0), (106, 0), (108, 0)]
# (время_мс, уровень)
DEBOUNCE = 20  # мс

stable = 1
last_change = -1000
presses = 0
for t, level in readings:
    if level != stable and (t - last_change) > DEBOUNCE:
        stable = level
        last_change = t
        if stable == 0:        # нажатие = переход в 0
            presses += 1
print("Засчитано нажатий:", presses)

Вывод:

Засчитано нажатий: 1

Грязная серия скачков на отметке ~100 мс засчиталась как одно нажатие, потому что повторные изменения внутри 20 мс игнорировались.

Тот же приём на плате

from machine import Pin
import time

button = Pin(15, Pin.IN, Pin.PULL_UP)
last = button.value()
last_change = time.ticks_ms()

while True:
    now = button.value()
    t = time.ticks_ms()
    if now != last and time.ticks_diff(t, last_change) > 20:
        last = now
        last_change = t
        if now == 0:
            print("Нажато!")
    time.sleep_ms(2)

time.ticks_ms() и time.ticks_diff() — специальные функции MicroPython для измерения интервалов, устойчивые к переполнению счётчика.

Как работает под капотом

Внутренний счётчик миллисекунд тикает непрерывно. Мы запоминаем момент последнего принятого изменения и сравниваем с ним каждое новое. ticks_diff корректно считает разницу, даже когда счётчик переполнился и «обнулился». Аппаратная альтернатива — RC-фильтр (резистор и конденсатор сглаживают скачки), но программный способ не требует деталей.

Частые ошибки

  • Игнорировать дребезг. Меню будет «проскакивать» пункты, счётчики накручиваться.
  • Брать слишком большое окно. 300 мс — и быстрые двойные нажатия теряются. 20–50 мс обычно оптимально.
  • Использовать time.time() вместо ticks_ms(). Для коротких интервалов на плате правильнее тики.

Итог

  • Дребезг — дрожание контактов, превращающее одно нажатие во множество.
  • Программный антидребезг игнорирует изменения короче порога (20–50 мс).
  • На плате используют ticks_ms() и ticks_diff() для надёжного измерения времени.
  • Отслеживайте именно фронт (переход), а не уровень.
Проверьте себя
1. Что такое дребезг контактов?
AПоломка кнопки
BКратковременное дрожание контактов, дающее несколько ложных переключений
CСлишком медленное нажатие
DЭлектрический разряд
2. Как работает программный антидребезг по времени?
AУвеличивает напряжение
BИгнорирует новые изменения входа в течение короткого окна (20–50 мс)
CОтключает кнопку
DЗамедляет процессор
3. Почему на плате для интервалов лучше ticks_ms()/ticks_diff(), а не time.time()?
AОни короче пишутся
BОни корректно считают разницу даже при переполнении счётчика
Ctime.time() не существует
DОни быстрее моргают светодиодом