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.