3.11. Uklanjanje podrhtavanja (debouncing)

Sklopka se crta kao savršeni otvoren-ili-zatvoren kontakt, ali kontakti stvarne sklopke ne prelaze čisto između tih dvaju stanja. Oni podrhtavaju – uspostavljaju i prekidaju električni kontakt mnogo puta unutar nekoliko milisekundi prije nego što se smire. GPIO ulaz koji čita pin to vidi kao niz rubova; nepažljiva petlja prozivanja broji nekoliko „pritisaka” za jedan stvarni pritisak, a rukovatelj prekida izvršava se nekoliko puta po stvarnom pritisku.

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

Sklopka koja podrhtava proizvodi niz brzih prijelaza prije nego što se smiri.

Uklanjanje podrhtavanja je postupak filtriranja podrhtavanja tako da se svaki fizički pritisak zabilježi kao jedan događaj. Tomu pristupaju dva rješenja – softverski (vremensko pravilo u ugrađenom programu (firmware)) ili hardverski (mali filtar na žici). Ona se međusobno ne isključuju.

3.11.1. Softversko uklanjanje podrhtavanja

Zamisao je zapamtiti kada se ulaz posljednji put promijenio i odbaciti daljnje promjene unutar kratkog prozora od te vremenske oznake. Podrhtavanje kontakta obično traje manje od 10 ms; stvarni pritisak traje 50 – 100 ms; prozor od 30 – 50 ms hvata sva podrhtavanja bez blokiranja stvarnih pritisaka.

U petlji prozivanja pročitajte pin, usporedite ga s posljednjom stabilnom vrijednošću i prihvatite promjenu tek nakon što istekne prozor za uklanjanje podrhtavanja:

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)

Za čitanja vođena prekidima primijenite isto vremensko pravilo unutar rukovatelja, a zatim stvarni pritisak predajte glavnom kontekstu putem micropython.schedule() (vidi GPIO ulaz):

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 filtrira podrhtavanja prema vremenskoj oznaci i stavlja povratni poziv u red; handle_press izvršava se natrag u glavnom kontekstu, gdje su dodjela memorije i spori U/I sigurni.

3.11.2. Hardversko uklanjanje podrhtavanja

Hardversko uklanjanje podrhtavanja filtrira podrhtavanje električki, prije nego što uopće dosegne pin. Standardni alat je kondenzator.

Kondenzator je komponenta s dva priključka koja pohranjuje električni naboj. Fizički, to su dvije vodljive ploče postavljene na malu udaljenost jedna od druge, odvojene izolatorom (dielektrikom).

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.

Pločasti kondenzator: dva vodiča odvojena izolacijskim slojem.

Primjena napona na njegove priključke tjera jednake i suprotne naboje na dvije ploče; odnos glasi

Q = C × V

gdje je Q pohranjeni naboj (kuloni), V je napon na kondenzatoru, a C je njegov kapacitet (faradi). Kapacitet je određen konstrukcijom uređaja; veći kapacitet znači više pohranjenog naboja pri istom naponu.

Posljedica: kondenzator ne može trenutno promijeniti svoj napon. Naboj koji teče unutra ili van mora proći kroz bilo koji otpor na putu, a taj otpor određuje koliko se brzo napon može mijenjati.

3.11.2.1. RC vremenska konstanta

Punjenje kondenzatora kroz otpornik proizvodi glatki eksponencijalni porast prema naponu napajanja, a ne skok. Karakteristično vrijeme tog porasta je RC vremenska konstanta:

τ = R × C

Nakon jednog τ, kondenzator je dosegnuo oko 63 % napona napajanja. Nakon 5 τ, prelazi 99 % – u praksi „potpuno napunjen”.

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.

Kondenzator se puni uzduž eksponencijalne krivulje. τ = RC je vrijeme potrebno da se dosegne 63 % konačnog napona.

Pražnjenje kroz otpornik slijedi zrcalnu sliku: napon eksponencijalno pada od svoje početne vrijednosti prema nuli, padajući na 37 % početnog napona nakon jednog τ, te na ispod 1 % nakon 5 τ.

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.

Kondenzator se prazni uzduž eksponencijalnog opadanja. τ = RC je vrijeme potrebno da padne na 37 % početnog napona.

3.11.2.2. Sklop za uklanjanje podrhtavanja

Kondenzator između ulaznog pina i mase, napajan kroz serijski otpornik, tvori niskopropusni filtar. Brzi šiljci nemaju vremena napuniti ili isprazniti kondenzator kroz taj otpornik; pin ostaje blizu napona na kojem je bio prije šiljka. Spore promjene – namjeran pritisak – pune ili prazne kondenzator i očitanje ih prati.

R1 povlači visoku stranu sklopke prema Vcc, proizvodeći sirovi signal sklopke koji podrhtava. R2 i C zatim niskopropusno filtriraju taj signal u pin:

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.

Hardversko uklanjanje podrhtavanja: R2 i C niskopropusno filtriraju sirovi signal sklopke prije nego što dosegne pin.

Tipične vrijednosti: R1 = 10 (pull-up), R2 = 10 (serijski), C = 100 nF.

Kada je sklopka otvorena, struja teče Vcc → R1R2 → kondenzator (u seriji), puneći kondenzator na Vcc s τ_charge = (R1 + R2) × C = 2 ms.

Kada se sklopka zatvori, čvor sklopke spaja se na masu i kondenzator se prazni samo kroz R2 na tu masu s τ_discharge = R2 × C = 1 ms.

Oba ruba su RC-filtrirana. Budući da kondenzator sjedi na vlastitom čvoru, nizvodno od R2 od sklopke, čisto se njiše između Vcc (otvoreno) i 0 V (zatvoreno) – nikakva struja ne mora teći kroz R1 u stacionarnom stanju ni u jednom slučaju.

3.11.3. Odabir između njih

  • Softver je zadani izbor. Ne košta ništa u komponentama, prag se lako podešava, a radi na bilo kojem pinu koji CPU čita.

  • Hardver je vrijedan dijelova kada podrhtavanje dosegne nešto drugo osim CPU-ovog koda prozivanja – prekid koji se ne smije dvostruko aktivirati, hardverski brojač, periferiju bez vlastitog filtra.

Softversko i hardversko uklanjanje podrhtavanja također supostoje mirno: mali RC filtar potiskuje najgore šiljke, a softverski prozor za uklanjanje podrhtavanja pokriva ono što preostane.