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.

Idealizovaný záznam z osciloskopu znázorňující signál vstupu spínače. Signál začíná vysoký (rozepnutý spínač), spadne nízko, několikrát během několika milisekund zakmitá sem a tam a poté se ustálí nízko (sepnutý spínač).

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

Kondenzátor nakreslený jako dvě rovnoběžné vodorovné desky s dielektrikem (izolantem) mezi nimi. Vývod připojuje každou desku k vnějšímu terminálu -- A nahoře, B dole. Při přiložení napětí V na terminály se na obou deskách nahromadí stejné a opačné náboje +Q a -Q.

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ý“.

Graf znázorňující nárůst napětí kondenzátoru po exponenciální křivce z 0 V směrem k napájecí sběrnici. Čas τ = RC je vyznačen na ose x v místě, kde křivka dosáhne 63 % napájecího napětí.

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

Graf znázorňující pokles napětí kondenzátoru po exponenciální křivce z Vmax směrem k 0 V. Čas τ = RC je vyznačen na ose x v místě, kde křivka klesne na 37 % počátečního napětí.

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:

Vstup spínače s hardwarovým odstraněním zákmitů. Vcc se připojuje přes pull-up rezistor 10 kΩ dolů k uzlu. Tento uzel se připojuje k zemi přes spínač v jedné větvi a přes sériový rezistor 10 kΩ k pinu ve druhé větvi. Kondenzátor 100 nF mezi pinem a zemí doplňuje dolní propust.

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.