3.11. Pergésmentesítés

A kapcsolót tökéletes nyitott vagy zárt érintkezőként szokás ábrázolni, ám egy valódi kapcsoló érintkezői nem váltanak tisztán a két állapot között. Pattognak – néhány ezredmásodpercen belül sokszor zárnak és bontanak elektromos kapcsolatot, mielőtt megállapodnának. A lábat olvasó GPIO bemenet ezt élek sorozataként látja; egy figyelmetlen lekérdező hurok egyetlen tényleges megnyomásra több „megnyomást” számol, egy megszakításkezelő pedig egyetlen megnyomásra többször fut le.

Egy idealizált oszcilloszkóp-ábra, amely egy kapcsoló bemeneti jelét mutatja. A jel magas szinten indul (nyitott kapcsoló), leesik alacsonyra, néhány ezredmásodpercen belül többször ide-oda pattog, majd alacsonyon állapodik meg (zárt kapcsoló).

Egy pattogó kapcsoló a megállapodás előtt gyors átmenetek sorozatát hozza létre.

A pergésmentesítés a pattogás kiszűrésének gyakorlata, hogy minden fizikai megnyomás egyetlen eseményként jelenjen meg. Két megközelítés oldja meg ezt – a szoftveres (egy időzítési szabály a firmware-ben) vagy a hardveres (egy kis szűrő a vezetéken). Ezek nem zárják ki egymást.

3.11.1. Szoftveres pergésmentesítés

A lényeg, hogy megjegyezzük, mikor változott utoljára a bemenet, és elutasítsuk a további változásokat ezen időbélyeg rövid időablakán belül. Az érintkezőpattogás jellemzően 10 ms alatt tart; egy valódi megnyomás 50–100 ms-ig; egy 30–50 ms-os időablak minden pattogást elkap anélkül, hogy a valódi megnyomásokat blokkolná.

Egy lekérdező hurokban olvasd be a lábat, hasonlítsd össze az utolsó stabil értékkel, és csak akkor fogadj el változást, ha a pergésmentesítési időablak letelt:

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)

Megszakítás-vezérelt olvasásnál alkalmazd ugyanazt az időzítési szabályt a kezelőn belül, majd a valódi megnyomást add át a fő kontextusnak a micropython.schedule() segítségével (lásd GPIO bemenet):

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)

Az ISR időbélyeg alapján szűri a pattogásokat és sorba állítja a visszahívást; a handle_press a fő kontextusban fut, ahol a memóriafoglalás és a lassú I/O biztonságos.

3.11.2. Hardveres pergésmentesítés

A hardveres pergésmentesítés elektromosan szűri ki a pattogást, még mielőtt az elérné a lábat. A szokásos eszköz egy kondenzátor.

A kondenzátor egy kétkivezetéses alkatrész, amely elektromos töltést tárol. Fizikailag két, egymástól rövid távolságra tartott vezető lemez, amelyeket egy szigetelő (a dielektrikum) választ el.

Egy kondenzátor két párhuzamos vízszintes lemezként ábrázolva, közöttük egy dielektrikummal (szigetelővel). Egy-egy kivezetés köti az egyes lemezeket egy külső kapocshoz -- A felül, B alul. Egyenlő nagyságú és ellentétes előjelű +Q és -Q töltések halmozódnak fel a két lemezen, amikor V feszültséget kapcsolunk a kapcsokra.

Egy síkkondenzátor: két vezető, amelyeket egy szigetelő réteg választ el.

Ha feszültséget kapcsolunk a kapcsaira, az egyenlő nagyságú és ellentétes előjelű töltéseket hajt a két lemezre; az összefüggés a következő

Q = C × V

ahol a Q a tárolt töltés (coulombban), a V a kondenzátoron eső feszültség, a C pedig a kapacitása (faradban). A kapacitást az eszköz felépítése rögzíti; nagyobb kapacitás azonos feszültségen több tárolt töltést jelent.

A következmény: egy kondenzátor nem tudja azonnal megváltoztatni a feszültségét. A be- vagy kiáramló töltésnek át kell haladnia az útban lévő ellenálláson, és ez az ellenállás határozza meg, milyen gyorsan változhat a feszültség.

3.11.2.1. RC időállandó

Egy kondenzátor ellenálláson keresztüli feltöltése sima, exponenciális emelkedést eredményez a tápfeszültség felé, nem pedig ugrást. Ennek az emelkedésnek a jellemző ideje az RC időállandó:

τ = R × C

Egy τ elteltével a kondenzátor a tápfeszültség körülbelül 63 %-át éri el. 5 τ után már több mint 99 %-nál tart – gyakorlati szempontból „teljesen feltöltött”.

Egy grafikon, amely egy kondenzátor feszültségének exponenciális görbe menti emelkedését mutatja 0 V-tól a tápsín felé. A τ = RC idő az x tengelyen van jelölve, ahol a görbe eléri a tápfeszültség 63 %-át.

Egy kondenzátor exponenciális görbe mentén töltődik. A τ = RC az az idő, amely alatt eléri a végső feszültség 63 %-át.

Az ellenálláson keresztüli kisütés a tükörképet követi: a feszültség exponenciálisan csökken a kezdeti értékéről nulla felé, egy τ elteltével a kiindulási feszültség 37 %-ára, 5 τ után pedig 1 % alá esik.

Egy grafikon, amely egy kondenzátor feszültségének exponenciális görbe menti esését mutatja Vmax-ról 0 V felé. A τ = RC idő az x tengelyen van jelölve, ahol a görbe a kiindulási feszültség 37 %-ára esik.

Egy kondenzátor exponenciális lecsengés mentén sül ki. A τ = RC az az idő, amely alatt a kiindulási feszültség 37 %-ára esik.

3.11.2.2. A pergésmentesítő áramkör

Egy bemeneti láb és a föld közé kötött, soros ellenálláson keresztül táplált kondenzátor aluláteresztő szűrőt alkot. A gyors tüskéknek nincs idejük feltölteni vagy kisütni a kondenzátort ezen az ellenálláson keresztül; a láb közel marad ahhoz a feszültséghez, amelyen a tüske előtt volt. A lassú változások – egy szándékos megnyomás – feltöltik vagy kisütik a kondenzátort, és az olvasott érték követi azt.

Az R1 felhúzza a kapcsoló magas oldalát Vcc-re, így egy nyers, pattogó kapcsolójelet hoz létre. Az R2 és a C ezután aluláteresztő szűrővel szűri ezt a jelet a lábra:

Egy kapcsoló bemenet hardveres pergésmentesítéssel. A Vcc egy 10 kΩ-os felhúzó ellenálláson keresztül egy csomóponthoz csatlakozik. Ez a csomópont az egyik ágon a kapcsolón keresztül a földhöz, a másik ágon pedig egy 10 kΩ-os soros ellenálláson keresztül a lábhoz csatlakozik. Egy 100 nF-os kondenzátor a láb és a föld között teszi teljessé az aluláteresztő szűrőt.

Hardveres pergésmentesítés: az R2 és a C aluláteresztő szűrővel szűri a nyers kapcsolójelet, mielőtt az elérné a lábat.

Tipikus értékek: R1 = 10 (felhúzó), R2 = 10 (soros), C = 100 nF.

Amikor a kapcsoló nyitott, az áram a Vcc → R1R2 → kondenzátor (sorba kapcsolva) útvonalon folyik, és Vcc-re tölti a kondenzátort τ_charge = (R1 + R2) × C = 2 ms időállandóval.

Amikor a kapcsoló zár, a kapcsoló csomópontja a földre szorul, és a kondenzátor egyedül az R2 ellenálláson keresztül ürül erre a földre τ_discharge = R2 × C = 1 ms időállandóval.

Mindkét él RC-szűrt. Mivel a kondenzátor a saját csomópontján ül, az R2 után a kapcsoló felől nézve, tisztán leng a Vcc (nyitott) és 0 V (zárt) között – egyik esetben sem kell áramnak átfolynia az R1 ellenálláson állandósult állapotban.

3.11.3. Választás közöttük

  • A szoftveres az alapértelmezett. Nem kerül alkatrészbe, a küszöbérték könnyen hangolható, és bármelyik lábon működik, amelyet a CPU olvas.

  • A hardveres akkor éri meg az alkatrészeket, amikor a pattogás valami máshoz jut el, nem a CPU lekérdező kódjához – egy megszakításhoz, amely nem szabad, hogy kétszer lépjen működésbe, egy hardveres számlálóhoz, vagy egy saját szűrővel nem rendelkező perifériához.

A szoftveres és a hardveres pergésmentesítés békésen meg is fér egymás mellett: egy kis RC szűrő elnyomja a legrosszabb tüskéket, egy szoftveres pergésmentesítési időablak pedig lefedi a maradékot.