3.11. Odstraňování zákmitů (debouncing)#

Spínač se kreslí jako dokonalý kontakt v poloze otevřeno nebo zavřeno, ale kontakty skutečného spínače nepřecházejí mezi oběma stavy čistě. Zakmitávají – během několika milisekund mnohokrát naváží a přeruší elektrický kontakt, než se ustálí. Vstup GPIO, který čte pin, to vidí jako shluk hran; nepozorná smyčka s pollingem napočítá pro jeden skutečný stisk několik „stisků“ a obsluha přerušení se na jeden skutečný stisk spustí několikrát.

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).

Zakmitávající spínač před ustálením vytvoří shluk rychlých přechodů.#

Odstraňování zákmitů (debouncing) je postup filtrování zakmitávání tak, aby se každý fyzický stisk zaregistroval jako jediná událost. Řeší to dva přístupy – softwarový (časové pravidlo ve firmwaru) nebo hardwarový (malý filtr na vodiči). Vzájemně se nevylučují.

3.11.1. Softwarové odstraňování zákmitů#

Myšlenka spočívá v zapamatování okamžiku, kdy se vstup naposledy změnil, a v zamítnutí dalších změn v krátkém okně od této časové značky. Zákmity kontaktu obvykle trvají méně než 10 ms; skutečný stisk trvá 50 – 100 ms; okno 30 – 50 ms zachytí všechny zákmity, aniž by blokovalo skutečné stisky.

Ve smyčce s pollingem přečtěte pin, porovnejte jej s poslední stabilní hodnotou a změnu přijměte teprve poté, co uplynulo okno pro odstranění zákmitů:

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)

U čtení řízeného přerušením použijte stejné časové pravidlo uvnitř obsluhy a poté skutečný stisk předejte do hlavního kontextu pomocí micropython.schedule() (viz Vstup 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 filtruje zákmity podle časové značky a zařadí callback do fronty; handle_press běží zpět v hlavním kontextu, kde jsou alokace a pomalé I/O bezpečné.

3.11.2. Hardwarové odstraňování zákmitů#

Hardwarové odstraňování zákmitů filtruje zakmitávání elektricky, ještě než vůbec dorazí na pin. Standardním nástrojem je kondenzátor.

Kondenzátor je dvouvývodová součástka, která uchovává elektrický náboj. Fyzicky jde o dvě vodivé desky držené v malé vzdálenosti od sebe, oddělené izolantem (dielektrikem).

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.

Deskový kondenzátor: dva vodiče oddělené izolační vrstvou.#

Přiložení napětí na jeho terminály vytlačí na obě desky stejné a opačné náboje; vztah je

Q = C × V

kde Q je uložený náboj (coulomby), V je napětí na kondenzátoru a C je jeho kapacita (farady). Kapacita je dána konstrukcí součástky; větší kapacita znamená více uloženého náboje při stejném napětí.

Důsledek: kondenzátor nemůže své napětí změnit okamžitě. Náboj přitékající dovnitř nebo odtékající ven musí projít jakýmkoli odporem, který je v cestě, a tento odpor určuje, jak rychle se napětí může měnit.

3.11.2.1. Časová konstanta RC#

Nabíjení kondenzátoru přes rezistor vytvoří plynulý exponenciální nárůst směrem k napájecímu napětí, nikoli skok. Charakteristickým časem tohoto nárůstu je časová konstanta RC:

τ = R × C

Po jedné τ dosáhne kondenzátor přibližně 63 % napájecího napětí. Po 5 τ je přes 99 % – pro praktické účely „plně nabitý“.

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.

Kondenzátor se nabíjí po exponenciální křivce. τ = RC je čas potřebný k dosažení 63 % konečného napětí.#

Vybíjení přes rezistor sleduje zrcadlový obraz: napětí exponenciálně klesá ze své počáteční hodnoty k nule, po jedné τ klesne na 37 % počátečního napětí a po 5 τ pod 1 %.

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.

Kondenzátor se vybíjí po exponenciálním poklesu. τ = RC je čas potřebný k poklesu na 37 % počátečního napětí.#

3.11.2.2. Obvod pro odstranění zákmitů#

Kondenzátor mezi vstupním pinem a zemí, napájený přes sériový rezistor, tvoří dolní propust. Rychlé špičky nemají čas nabít nebo vybít kondenzátor přes tento rezistor; pin zůstává blízko napětí, na němž byl před špičkou. Pomalé změny – záměrný stisk – kondenzátor nabijí nebo vybijí a čtení je následuje.

R1 táhne vysokou stranu spínače nahoru k Vcc, čímž vzniká surový signál spínače, který zakmitává. R2 a C pak tento signál dolnopropustně vyfiltrují do pinu:

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.

Hardwarové odstranění zákmitů: R2 a C dolnopropustně filtrují surový signál spínače, než dorazí na pin.#

Typické hodnoty: R1 = 10 (pull-up), R2 = 10 (sériový), C = 100 nF.

Když je spínač rozepnutý, proud teče Vcc → R1R2 → kondenzátor (v sérii) a nabíjí kondenzátor na Vcc s τ_charge = (R1 + R2) × C = 2 ms.

Když se spínač sepne, uzel spínače se přitáhne k zemi a kondenzátor se vybíjí pouze přes R2 k této zemi s τ_discharge = R2 × C = 1 ms.

Obě hrany jsou RC-filtrované. Protože kondenzátor sedí na svém vlastním uzlu, za R2 od spínače, čistě se přepíná mezi Vcc (rozepnuto) a 0 V (sepnuto) – v ustáleném stavu nemusí v žádném z případů protékat R1 žádný proud.

3.11.3. Volba mezi nimi#

  • Software je výchozí volbou. Nestojí žádné součástky, práh se snadno ladí a funguje na jakémkoli pinu, který CPU čte.

  • Hardware se vyplatí kvůli součástkám, když zákmity dorazí k něčemu jinému než k pollingovému kódu CPU – k přerušení, které se nesmí spustit dvakrát, k hardwarovému čítači, k periferii bez vlastního filtru.

Softwarové a hardwarové odstraňování zákmitů spolu také pokojně koexistují: malý RC filtr potlačí nejhorší špičky a softwarové okno pro odstranění zákmitů pokryje to, co zbude.