3.11. Усунення брязкоту

Перемикач зображується як ідеальний розімкнутий або замкнутий контакт, але контакти реального перемикача не перемикаються різко між двома станами. Вони брязкочуть – встановлюють і розривають електричний контакт багато разів протягом кількох мілісекунд перед тим, як стабілізуватися. GPIO-вхід, який читає вивід, сприймає це як сплеск фронтів; необережний цикл опитування рахує кілька «натискань» для одного реального натискання, а обробник переривань виконується кілька разів на одне фактичне натискання.

An idealised scope trace showing a switch input signal. The signal starts high (open switch), drops low, bounces back and forth several times within a few milliseconds, then settles low (closed switch).

Перемикач із брязкотом видає сплеск швидких переходів перед стабілізацією.

Усунення брязкоту – це практика фільтрації брязкоту, щоб кожне фізичне натискання реєструвалося як одна подія. Задачу вирішують два підходи – програмний (правило за часом у мікропрограмі) або апаратний (невеликий фільтр на проводі). Вони не є взаємовиключними.

3.11.1. Програмне усунення брязкоту

Ідея полягає в тому, щоб запам’ятати момент останньої зміни вхідного сигналу та відкидати подальші зміни в короткому вікні від цього моменту часу. Брязкіт контактів зазвичай триває менше 10 мс; реальне натискання займає 50 – 100 мс; вікно в 30 – 50 мс захоплює весь брязкіт, не блокуючи справжніх натискань.

У циклі опитування слід читати вивід, порівнювати з останнім стабільним значенням і приймати зміну лише після закінчення вікна усунення брязкоту:

import time
from machine import Pin

button = Pin("P0", Pin.IN, Pin.PULL_UP)
last_state  = 1
last_change = 0
DEBOUNCE_MS = 50

while True:
    now = time.ticks_ms()
    state = button.value()
    if state != last_state and time.ticks_diff(now, last_change) > DEBOUNCE_MS:
        last_change = now
        last_state = state
        if state == 0:
            do_action()
    time.sleep_ms(10)

Для читання на основі переривань слід застосовувати те саме правило за часом всередині обробника, а потім передавати реальне натискання до головного контексту через micropython.schedule() (дивіться GPIO-вхід):

import time
import micropython
from machine import Pin

button = Pin("P0", Pin.IN, Pin.PULL_UP)
last_irq = 0
DEBOUNCE_MS = 50

def handle_press(pin):
    do_action()

def on_press(pin):
    global last_irq
    now = time.ticks_ms()
    if time.ticks_diff(now, last_irq) < DEBOUNCE_MS:
        return
    last_irq = now
    micropython.schedule(handle_press, pin)

button.irq(handler=on_press, trigger=Pin.IRQ_FALLING)

ISR фільтрує брязкіт за часовою міткою та ставить зворотний виклик в чергу; handle_press виконується назад у головному контексті, де виділення пам’яті та повільний ввід-вивід безпечні.

3.11.2. Апаратне усунення брязкоту

Апаратне усунення брязкоту фільтрує брязкіт електрично, перш ніж він досягне виводу. Стандартним інструментом є конденсатор.

Конденсатор – це двоклемний компонент, що зберігає електричний заряд. Фізично він являє собою дві провідні пластини, що знаходяться на невеликій відстані одна від одної, розділені ізолятором (діелектриком).

A capacitor drawn as two parallel horizontal plates with a dielectric (insulator) between them. A lead connects each plate to an external terminal -- A on top, B on bottom. Equal and opposite charges +Q and -Q accumulate on the two plates when a voltage V is applied across the terminals.

Плоский конденсатор: два провідники, розділені ізолюючим шаром.

Прикладання напруги до його клем спричиняє накопичення рівних і протилежних зарядів на двох пластинах; залежність виражається формулою

Q = C × V

де Q – накопичений заряд (кулони), V – напруга на конденсаторі, а C – його ємність (фарадах). Ємність визначається конструкцією пристрою; більша ємність означає більший накопичений заряд за тієї самої напруги.

Наслідок: конденсатор не може змінити свою напругу миттєво. Заряд, що надходить або виходить, повинен проходити через будь-який опір на шляху, і цей опір визначає швидкість зміни напруги.

3.11.2.1. Стала часу RC

Заряджання конденсатора через резистор створює плавний експоненціальний підйом до напруги живлення, а не ступінчасту зміну. Характерний час цього підйому – стала часу RC:

τ = R × C

Після одного τ конденсатор досягає приблизно 63 % напруги живлення. Після 5 τ – понад 99 % – «повністю заряджений» для практичних цілей.

A graph showing a capacitor's voltage rising along an exponential curve from 0 V toward the supply rail. The time τ = RC is marked on the x-axis where the curve reaches 63 % of the supply voltage.

Конденсатор заряджається по експоненціальній кривій. τ = RC – час досягнення 63 % кінцевої напруги.

Розряджання через резистор відбувається дзеркально: напруга експоненціально спадає від початкового значення до нуля, знижуючись до 37 % початкової напруги після одного τ і до менш ніж 1 % після 5 τ.

A graph showing a capacitor's voltage falling along an exponential curve from Vmax toward 0 V. The time τ = RC is marked on the x-axis where the curve drops to 37 % of the starting voltage.

Конденсатор розряджається по експоненціальному спаду. τ = RC – час спадання до 37 % початкової напруги.

3.11.2.2. Схема усунення брязкоту

Конденсатор між вхідним виводом та землею, що живиться через послідовний резистор, утворює фільтр низьких частот. Швидкі імпульси не встигають зарядити або розрядити конденсатор через цей резистор; вивід залишається близько до тієї напруги, яка була до імпульсу. Повільні зміни – навмисне натискання – заряджають або розряджають конденсатор, і показання слідує за ними.

R1 підтягує верхній бік перемикача до Vcc, створюючи сирий сигнал перемикача з брязкотом. R2 і C потім фільтрують цей сигнал фільтром низьких частот перед подачею на вивід:

A switch input with hardware debouncing. Vcc connects through a 10 kΩ pull-up resistor down to a junction. That junction connects to ground through the switch on one branch, and through a 10 kΩ series resistor to the Pin on the other branch. A 100 nF capacitor between Pin and ground completes the low-pass filter.

Апаратне усунення брязкоту: R2 і C фільтрують сирий сигнал перемикача фільтром низьких частот перед подачею на вивід.

Типові значення: R1 = 10 (підтяжка), R2 = 10 (послідовний), C = 100 nF.

Коли перемикач розімкнутий, струм тече Vcc → R1R2 → конд. (послідовно), заряджаючи конденсатор до Vcc зі τ_charge = (R1 + R2) × C = 2 ms.

Коли перемикач замикається, вузол перемикача фіксується на землі, і конденсатор розряджається лише через R2 до цієї землі зі τ_discharge = R2 × C = 1 ms.

Обидва фронти фільтруються RC-ланцюгом. Оскільки конденсатор знаходиться у власному вузлі, нижче по схемі від R2 відносно перемикача, він чисто перемикається між Vcc (розімкнутий) і 0 В (замкнутий) – у жодному з усталених станів не потрібно, щоб струм протікав через R1.

3.11.3. Вибір між ними

  • Програмний – це варіант за замовчуванням. Він не потребує компонентів, поріг легко налаштувати, і він працює на будь-якому виводі, який читає CPU.

  • Апаратний варто використовувати, коли брязкіт досягає чогось іншого, ніж код опитування CPU, – переривання, яке не повинно спрацьовувати двічі, апаратний лічильник, периферійний пристрій без власного фільтра.

Програмне та апаратне усунення брязкоту також мирно співіснують: невеликий RC-фільтр придушує найгірші імпульси, а програмне вікно усунення брязкоту покриває решту.