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-фільтр придушує найгірші імпульси, а програмне вікно усунення брязкоту покриває решту.