3.11. Värähtelyn poisto

Kytkin piirretään täydellisenä auki- tai kiinni-koskettimena, mutta todellisen kytkimen koskettimet eivät napsahda puhtaasti näiden kahden tilan välillä. Ne värähtelevät – muodostavat ja katkaisevat sähkökontaktin useita kertoja muutaman millisekunnin aikana ennen vakautumistaan. Nastaa lukeva GPIO-tulo näkee tämän reunojen sarjana; huolimaton kyselysilmukka laskee useita ”painalluksia” yhdelle todelliselle painallukselle, ja keskeytyskäsittelijä suoritetaan useita kertoja yhtä todellista painallusta kohti.

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

Värähtelevä kytkin tuottaa nopeiden siirtymien sarjan ennen vakautumistaan.

Värähtelyn poisto on käytäntö, jossa värähtely suodatetaan niin, että jokainen fyysinen painallus rekisteröityy yhtenä tapahtumana. Tähän on kaksi ratkaisua – ohjelmistopohjainen (ajoitussääntö laiteohjelmistossa) tai laitteistopohjainen (pieni suodatin johdossa). Ne eivät sulje toisiaan pois.

3.11.1. Ohjelmistopohjainen värähtelyn poisto

Ideana on muistaa, milloin tulo viimeksi muuttui, ja hylätä lisämuutokset lyhyen ikkunan sisällä tästä aikaleimasta. Koskettimien värähtely kestää tyypillisesti alle 10 ms; todellinen painallus kestää 50 – 100 ms; 30 – 50 ms:n ikkuna nappaa kaikki värähtelyt estämättä todellisia painalluksia.

Kyselysilmukassa lue nasta, vertaa sitä viimeiseen vakaaseen arvoon ja hyväksy muutos vasta, kun värähtelyn poiston ikkuna on kulunut:

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)

Keskeytyspohjaisissa luennoissa sovella samaa ajoitussääntöä käsittelijän sisällä ja siirrä sitten todellinen painallus pääkontekstiin micropython.schedule()-funktion kautta (katso GPIO-tulo):

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 suodattaa värähtelyt aikaleiman perusteella ja asettaa takaisinkutsun jonoon; handle_press suoritetaan takaisin pääkontekstissa, jossa muistinvaraus ja hidas I/O ovat turvallisia.

3.11.2. Laitteistopohjainen värähtelyn poisto

Laitteistopohjainen värähtelyn poisto suodattaa värähtelyn sähköisesti, ennen kuin se edes saavuttaa nastan. Vakiotyökalu on kondensaattori.

Kondensaattori on kaksinapainen komponentti, joka varastoi sähkövarausta. Fyysisesti se koostuu kahdesta sähköä johtavasta levystä, joita pidetään lyhyen matkan päässä toisistaan ja jotka on erotettu eristeellä (dielektrikum).

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.

Levykondensaattori: kaksi johdinta, jotka on erotettu eristekerroksella.

Liittimien yli kytketty jännite ajaa yhtä suuret ja vastakkaiset varaukset kahteen levyyn; suhde on

Q = C × V

jossa Q on varastoitu varaus (coulombeja), V on kondensaattorin yli oleva jännite ja C on sen kapasitanssi (faradeja). Kapasitanssi määräytyy laitteen rakenteen mukaan; suurempi kapasitanssi tarkoittaa enemmän varastoitua varausta samalla jännitteellä.

Seuraus: kondensaattori ei voi muuttaa jännitettään hetkessä. Sisään tai ulos virtaavan varauksen on kuljettava reitillä olevan vastuksen läpi, ja tuo vastus määrää, kuinka nopeasti jännite voi muuttua.

3.11.2.1. RC-aikavakio

Kondensaattorin lataaminen vastuksen läpi tuottaa tasaisen eksponentiaalisen nousun kohti syöttöjännitettä, ei askelmaista nousua. Tämän nousun tunnusomainen aika on RC-aikavakio:

τ = R × C

Yhden τ:n jälkeen kondensaattori on saavuttanut noin 63 % syöttöjännitteestä. 5 τ:n jälkeen se on yli 99 % – käytännössä ”täyteen ladattu”.

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.

Kondensaattori latautuu eksponentiaalista käyrää pitkin. τ = RC on aika, jossa saavutetaan 63 % loppujännitteestä.

Purkautuminen vastuksen läpi noudattaa peilikuvaa: jännite laskee eksponentiaalisesti alkuarvostaan kohti nollaa, pudoten 37 %:iin alkujännitteestä yhden τ:n jälkeen ja alle 1 %:iin 5 τ:n jälkeen.

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.

Kondensaattori purkautuu eksponentiaalista vaimenemista pitkin. τ = RC on aika, jossa pudotaan 37 %:iin alkujännitteestä.

3.11.2.2. Värähtelyn poiston kytkentä

Tulonastan ja maan väliin sijoitettu kondensaattori, jota syötetään sarjavastuksen kautta, muodostaa alipäästösuodattimen. Nopeilla piikeillä ei ole aikaa ladata tai purkaa kondensaattoria tuon vastuksen läpi; nasta pysyy lähellä sitä jännitettä, jossa se oli ennen piikkiä. Hitaat muutokset – tarkoituksellinen painallus – lataavat tai purkavat kondensaattoria, ja lukema seuraa.

R1 nostaa kytkimen yläpuolen Vcc:hen ja tuottaa raa’an kytkinsignaalin, joka värähtelee. R2 ja C alipäästösuodattavat tuon signaalin nastaan:

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.

Laitteistopohjainen värähtelyn poisto: R2 ja C alipäästösuodattavat raa’an kytkinsignaalin ennen kuin se saavuttaa nastan.

Tyypilliset arvot: R1 = 10 (ylösveto), R2 = 10 (sarja), C = 100 nF.

Kun kytkin on auki, virta kulkee Vcc → R1R2 → kondensaattori (sarjassa) ladaten kondensaattorin Vcc:hen aikavakiolla τ_charge = (R1 + R2) × C = 2 ms.

Kun kytkin sulkeutuu, kytkinsolmu kiinnittyy maahan ja kondensaattori purkautuu pelkän R2:n kautta tuohon maahan aikavakiolla τ_discharge = R2 × C = 1 ms.

Molemmat reunat ovat RC-suodatettuja. Koska kondensaattori on omassa solmussaan, R2:n jälkeen kytkimestä katsottuna, se heilahtelee puhtaasti Vcc:n (auki) ja 0 V:n (kiinni) välillä – kummassakaan tapauksessa virtaa ei tarvitse kulkea R1:n läpi tasapainotilassa.

3.11.3. Valinta niiden välillä

  • Ohjelmisto on oletus. Se ei maksa mitään komponentteina, kynnysarvoa on helppo virittää, ja se toimii millä tahansa nastalla, jonka CPU lukee.

  • Laitteisto on osien arvoinen, kun värähtely saavuttaa jotain muuta kuin CPU:n kyselykoodin – keskeytyksen, joka ei saa laueta kahdesti, laitteistopohjaisen laskurin tai oheislaitteen, jolla ei ole omaa suodatinta.

Ohjelmisto- ja laitteistopohjainen värähtelyn poisto elävät myös sovussa: pieni RC-suodatin vaimentaa pahimmat piikit, ja ohjelmistopohjainen värähtelyn poiston ikkuna kattaa loput.