3.11. Avstudsning

En strömställare ritas som en perfekt öppen-eller-sluten kontakt, men kontakterna i en verklig strömställare växlar inte rent mellan de två tillstånden. De skallrar – skapar och bryter elektrisk kontakt många gånger inom några få millisekunder innan de stabiliseras. En GPIO-ingång som läser stiftet uppfattar detta som en skur av kanter; en oförsiktig avsökningsslinga räknar flera ”tryckningar” för en enda verklig tryckning, och en avbrottshanterare körs flera gånger per faktisk tryckning.

En idealiserad oscilloskopkurva som visar en ingångssignal från en strömställare. Signalen börjar hög (öppen strömställare), faller låg, studsar fram och tillbaka flera gånger inom några millisekunder och stabiliseras sedan låg (sluten strömställare).

En studsande strömställare ger en skur av snabba övergångar innan den stabiliseras.

Avstudsning är metoden att filtrera bort skallret så att varje fysisk tryckning registreras som en enda händelse. Två tillvägagångssätt löser detta – mjukvara (en tidsregel i den fasta programvaran) eller hårdvara (ett litet filter på ledningen). De utesluter inte varandra.

3.11.1. Avstudsning i mjukvara

Tanken är att komma ihåg när ingången senast ändrades och avvisa ytterligare ändringar inom ett kort fönster efter den tidsstämpeln. Kontaktstuds varar typiskt under 10 ms; en verklig tryckning tar 50 – 100 ms; ett fönster på 30 – 50 ms fångar alla studsar utan att blockera verkliga tryckningar.

I en avsökningsslinga: läs stiftet, jämför med det senaste stabila värdet och godta en ändring först efter att avstudsningsfönstret har löpt ut:

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)

För avbrottsdrivna läsningar tillämpar du samma tidsregel inuti hanteraren och lämnar sedan över den verkliga tryckningen till huvudkontexten via micropython.schedule() (se GPIO-ingång):

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-rutinen filtrerar studsar efter tidsstämpel och köar återanropet; handle_press körs tillbaka i huvudkontexten, där allokering och långsam I/O är säkra.

3.11.2. Avstudsning i hårdvara

Avstudsning i hårdvara filtrerar bort skallret elektriskt, innan det ens når stiftet. Standardverktyget är en kondensator.

En kondensator är en komponent med två anslutningar som lagrar elektrisk laddning. Fysiskt är den två ledande plattor som hålls ett kort avstånd från varandra, åtskilda av en isolator (dielektrikumet).

En kondensator ritad som två parallella horisontella plattor med ett dielektrikum (isolator) mellan sig. En ledare förbinder varje platta till en yttre anslutning -- A upptill, B nedtill. Lika stora och motsatta laddningar +Q och -Q ansamlas på de två plattorna när en spänning V läggs över anslutningarna.

En plattkondensator: två ledare åtskilda av ett isolerande skikt.

Att lägga en spänning över dess anslutningar driver lika stora och motsatta laddningar till de två plattorna; sambandet är

Q = C × V

där Q är den lagrade laddningen (coulomb), V är spänningen över kondensatorn och C är dess kapacitans (farad). Kapacitansen är bestämd av komponentens konstruktion; mer kapacitans betyder mer lagrad laddning vid samma spänning.

Konsekvensen: en kondensator kan inte ändra sin spänning omedelbart. Laddning som flödar in eller ut måste passera genom vilket motstånd som än finns i banan, och det motståndet avgör hur snabbt spänningen kan ändras.

3.11.2.1. RC-tidskonstanten

Att ladda en kondensator genom ett motstånd ger en mjuk exponentiell stigning mot matningsspänningen, inte ett steg. Den karaktäristiska tiden för den stigningen är RC-tidskonstanten:

τ = R × C

Efter en τ har kondensatorn nått ungefär 63 % av matningsspänningen. Efter 5 τ är den över 99 % – ”fulladdad” i praktiken.

En graf som visar en kondensators spänning stiga längs en exponentiell kurva från 0 V mot matningsskenan. Tiden τ = RC är markerad på x-axeln där kurvan når 63 % av matningsspänningen.

En kondensator laddas längs en exponentiell kurva. τ = RC är tiden för att nå 63 % av slutspänningen.

Urladdning genom ett motstånd följer spegelbilden: spänningen faller exponentiellt från sitt initialvärde mot noll och sjunker till 37 % av startspänningen efter en τ, och till under 1 % efter 5 τ.

En graf som visar en kondensators spänning falla längs en exponentiell kurva från Vmax mot 0 V. Tiden τ = RC är markerad på x-axeln där kurvan sjunker till 37 % av startspänningen.

En kondensator laddas ur längs ett exponentiellt avtagande. τ = RC är tiden för att falla till 37 % av startspänningen.

3.11.2.2. Avstudsningskretsen

En kondensator mellan ett ingångsstift och jord, matad genom ett seriemotstånd, bildar ett lågpassfilter. Snabba spikar hinner inte ladda eller ladda ur kondensatorn genom det motståndet; stiftet stannar nära den spänning det hade före spiken. Långsamma ändringar – en avsiktlig tryckning – laddar eller laddar ur kondensatorn och avläsningen följer med.

R1 drar strömställarens högsida upp till Vcc och ger en rå strömställarsignal som studsar. R2 och C lågpassfiltrerar sedan den signalen in i stiftet:

En strömställaringång med avstudsning i hårdvara. Vcc ansluter genom ett 10 kΩ pull-up-motstånd ned till en förgrening. Den förgreningen ansluter till jord genom strömställaren på en gren, och genom ett 10 kΩ seriemotstånd till stiftet på den andra grenen. En kondensator på 100 nF mellan stiftet och jord fullbordar lågpassfiltret.

Avstudsning i hårdvara: R2 och C lågpassfiltrerar den råa strömställarsignalen innan den når stiftet.

Typiska värden: R1 = 10 (pull-up), R2 = 10 (serie), C = 100 nF.

När strömställaren är öppen flödar ström Vcc → R1R2 → kondensator (i serie) och laddar kondensatorn till Vcc med τ_charge = (R1 + R2) × C = 2 ms.

När strömställaren sluts klamras strömställarnoden till jord och kondensatorn laddas ur genom enbart R2 till den jorden med τ_discharge = R2 × C = 1 ms.

Båda kanterna är RC-filtrerade. Eftersom kondensatorn sitter på sin egen nod, nedströms om R2 från strömställaren, svänger den rent mellan Vcc (öppen) och 0 V (sluten) – ingen ström behöver flöda genom R1 i stationärt tillstånd i något av fallen.

3.11.3. Att välja mellan dem

  • Mjukvara är förvalet. Den kostar inget i komponenter, tröskelvärdet är lätt att finjustera och den fungerar på vilket stift som helst som CPU:n läser.

  • Hårdvara är värd komponenterna när studsen når något annat än CPU:ns avsökningskod – ett avbrott som inte får utlösas dubbelt, en hårdvaruräknare, en kringutrustning utan eget filter.

Avstudsning i mjukvara och hårdvara samexisterar också fredligt: ett litet RC-filter dämpar de värsta spikarna, och ett mjukvaruavstudsningsfönster täcker det som återstår.