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.
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).
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ý“.
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 %.
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:
Hardwarové odstranění zákmitů: R2 a C dolnopropustně filtrují surový signál spínače, než dorazí na pin.#
Typické hodnoty: R1 = 10 kΩ (pull-up), R2 = 10 kΩ (sériový), C = 100 nF.
Když je spínač rozepnutý, proud teče Vcc → R1 → R2 → 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.