3.11. Ontdenderen (debouncing)

Een schakelaar wordt getekend als een perfect open-of-gesloten contact, maar de contacten van een echte schakelaar springen niet schoon tussen de twee toestanden. Ze denderen – ze maken en verbreken het elektrische contact vele malen binnen enkele milliseconden voordat ze tot rust komen. Een GPIO-ingang die de pin uitleest, ziet dat als een reeks randen; een onzorgvuldige polling-lus telt meerdere “drukken” voor een echte druk, en een interrupt-handler draait meerdere keren per daadwerkelijke druk.

Een geïdealiseerde scoop-trace die een ingangssignaal van een schakelaar toont. Het signaal begint hoog (open schakelaar), zakt laag, kaatst enkele keren heen en weer binnen enkele milliseconden en komt dan laag tot rust (gesloten schakelaar).

Een denderende schakelaar produceert een reeks snelle overgangen voordat ze tot rust komt.

Ontdenderen is de techniek om het denderen weg te filteren zodat elke fysieke druk als één enkele gebeurtenis wordt geregistreerd. Twee benaderingen lossen dit op – software (een timingregel in de firmware) of hardware (een klein filter op de draad). Ze sluiten elkaar niet uit.

3.11.1. Software-ontdenderen

Het idee is om te onthouden wanneer de ingang voor het laatst veranderde en verdere veranderingen binnen een kort venster van dat tijdstip te weigeren. Contactdenderen duurt doorgaans minder dan 10 ms; een echte druk duurt 50 – 100 ms; een venster van 30 – 50 ms vangt alle denderingen op zonder echte drukken te blokkeren.

Lees in een polling-lus de pin, vergelijk met de laatste stabiele waarde en accepteer een verandering pas nadat het ontdendervenster is verstreken:

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)

Pas voor interrupt-gestuurde uitlezingen dezelfde timingregel toe binnen de handler en geef vervolgens de echte druk door aan de hoofdcontext via micropython.schedule() (zie GPIO-ingang):

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)

De ISR filtert denderingen op basis van het tijdstip en zet de callback in de wachtrij; handle_press draait terug in de hoofdcontext, waar allocatie en trage I/O veilig zijn.

3.11.2. Hardware-ontdenderen

Hardware-ontdenderen filtert het denderen elektrisch weg, voordat het de pin ooit bereikt. Het standaardgereedschap is een condensator.

Een condensator is een component met twee aansluitingen die elektrische lading opslaat. Fysiek bestaat het uit twee geleidende platen die op korte afstand van elkaar worden gehouden, gescheiden door een isolator (het diëlektricum).

Een condensator getekend als twee parallelle horizontale platen met een diëlektricum (isolator) ertussen. Een draad verbindt elke plaat met een externe aansluiting -- A bovenaan, B onderaan. Gelijke en tegengestelde ladingen +Q en -Q hopen zich op op de twee platen wanneer er een spanning V over de aansluitingen wordt aangelegd.

Een vlakke-plaatcondensator: twee geleiders gescheiden door een isolerende laag.

Het aanleggen van een spanning over de aansluitingen drijft gelijke en tegengestelde ladingen op de twee platen; de relatie is

Q = C × V

waarbij Q de opgeslagen lading is (coulomb), V de spanning over de condensator is, en C zijn capaciteit is (farad). De capaciteit wordt bepaald door de constructie van het apparaat; meer capaciteit betekent meer opgeslagen lading bij dezelfde spanning.

Het gevolg: een condensator kan zijn spanning niet onmiddellijk veranderen. Lading die in- of uitstroomt moet door de aanwezige weerstand in het pad, en die weerstand bepaalt hoe snel de spanning kan veranderen.

3.11.2.1. RC-tijdconstante

Het opladen van een condensator via een weerstand levert een vloeiende exponentiële stijging naar de voedingsspanning op, geen sprong. De karakteristieke tijd van die stijging is de RC-tijdconstante:

τ = R × C

Na één τ heeft de condensator ongeveer 63 % van de voedingsspanning bereikt. Na 5 τ is het meer dan 99 % – in de praktijk “volledig opgeladen”.

Een grafiek die toont hoe de spanning van een condensator langs een exponentiële curve stijgt van 0 V naar de voedingsrail. De tijd τ = RC is gemarkeerd op de x-as waar de curve 63 % van de voedingsspanning bereikt.

Een condensator laadt op langs een exponentiële curve. τ = RC is de tijd om 63 % van de eindspanning te bereiken.

Het ontladen via een weerstand volgt het spiegelbeeld: de spanning daalt exponentieel vanaf zijn beginwaarde naar nul, en zakt naar 37 % van de beginspanning na één τ, en tot onder 1 % na 5 τ.

Een grafiek die toont hoe de spanning van een condensator langs een exponentiële curve daalt van Vmax naar 0 V. De tijd τ = RC is gemarkeerd op de x-as waar de curve daalt tot 37 % van de beginspanning.

Een condensator ontlaadt langs een exponentiële afname. τ = RC is de tijd om te dalen tot 37 % van de beginspanning.

3.11.2.2. Het ontdenderschakeling

Een condensator tussen een ingangspin en massa, gevoed via een seriële weerstand, vormt een laagdoorlaatfilter. Snelle pieken hebben geen tijd om de condensator via die weerstand op te laden of te ontladen; de pin blijft dicht bij de spanning waarop hij vóór de piek stond. Langzame veranderingen – een doelbewuste druk – laden of ontladen de condensator en de uitlezing volgt.

R1 trekt de hoge zijde van de schakelaar omhoog naar Vcc, wat een ruw schakelaarsignaal oplevert dat dendert. R2 en C laagdoorlaatfilteren dat signaal vervolgens naar de pin:

Een schakelaaringang met hardware-ontdenderen. Vcc verbindt via een 10 kΩ pull-up-weerstand omlaag naar een knooppunt. Dat knooppunt verbindt met massa via de schakelaar op de ene tak, en via een 10 kΩ seriële weerstand met de pin op de andere tak. Een 100 nF condensator tussen de pin en massa voltooit het laagdoorlaatfilter.

Hardware-ontdenderen: R2 en C laagdoorlaatfilteren het ruwe schakelaarsignaal voordat het de pin bereikt.

Typische waarden: R1 = 10 (pull-up), R2 = 10 (serie), C = 100 nF.

Wanneer de schakelaar open is, stroomt er stroom Vcc → R1R2 → condensator (in serie), waardoor de condensator wordt opgeladen tot Vcc met τ_charge = (R1 + R2) × C = 2 ms.

Wanneer de schakelaar sluit, wordt het schakelaarknooppunt naar massa geklemd en de condensator ontlaadt alleen via R2 naar die massa met τ_discharge = R2 × C = 1 ms.

Beide randen worden RC-gefilterd. Omdat de condensator op zijn eigen knooppunt zit, stroomafwaarts van R2 ten opzichte van de schakelaar, schommelt hij schoon tussen Vcc (open) en 0 V (gesloten) – in geen van beide gevallen hoeft er in steady state stroom door R1 te lopen.

3.11.3. Kiezen tussen beide

  • Software is de standaard. Het kost niets aan componenten, de drempelwaarde is eenvoudig af te stellen, en het werkt op elke pin die de CPU uitleest.

  • Hardware is de onderdelen waard wanneer het denderen iets anders bereikt dan de polling-code van de CPU – een interrupt die niet dubbel mag afgaan, een hardwareteller, een randapparaat zonder eigen filter.

Software- en hardware-ontdenderen gaan ook vreedzaam samen: een klein RC-filter onderdrukt de ergste pieken, en een software-ontdendervenster dekt wat overblijft.