Knapaflæsning og prelbekæmpelse

En mikrokontroller læser en knap som en digital indgang med to niveauer: HIGH eller LOW. En løs indgang må ikke efterlades elektrisk ubestemt; den kan opfange støj og skifte tilfældigt.

Tilslutning med intern pull-up

Den enkle løsning er at aktivere controllerens interne pull-up og forbinde knappen mellem GPIO og GND:

Knap mellem digital indgang og jord med intern pull-up; diagram viser kontaktprel og den filtrerede stabile overgang
Intern pull-up giver en defineret HIGH-hviletilstand. Når knappen trykkes, trækkes indgangen til GND og aflæses som LOW. Kurverne illustrerer softwaremæssig prelbekæmpelse.

Kontaktprel

En mekanisk kontakt lukker ikke nødvendigvis rent én gang. Kontaktfladerne kan ramme hinanden flere gange i løbet af få millisekunder, så GPIO-indgangen hurtigt skifter HIGH/LOW, før den falder til ro. Programmet kan derfor komme til at tælle ét tryk som flere.

Prelbekæmpelse (debounce) kan laves i hardware eller software. Hardware kan fx bruge et RC-filter sammen med en Schmitt-trigger-indgang eller en dedikeret debounce-kreds. Software kan vente, til indgangen har været stabil længe nok, før en ny tilstand accepteres. Nedenfor vises softwaremetoden; et typisk startpunkt er 20–30 ms, men tiden afhænger af knappen og anvendelsen.

Hardwaremæssig prelbekæmpelse: RC-filter dæmper kontaktens hurtige skift, og en 74HC14 Schmitt-trigger former signalet til en ren digital overgang
Hardwaremetode: R og C dæmper kontaktprel, og Schmitt-triggerens hysterese giver en ren digital overgang. Vælg RC-tidskonstant efter kontaktens prel og den ønskede reaktionstid.

Kontrollér databladet for den konkrete 74HC14-variant: forsyningsspænding og tilladt logikniveau afhænger af komponentfamilien. Indgangen må ikke automatisk antages at være 3,3 V-tolerant.

Registrering ved tilstandsskift

Et tryk og et slip kan registreres som hver sin kant, altså som en ændring mellem HIGH og LOW. Med koblingen ovenfor og INPUT_PULLUP er indgangen aktiv-lav:

millis()-eksemplet nedenfor opdager skift ved at sammenligne den nye stabile aflæsning med den tidligere. Det accepterer først kanten efter debounce-perioden og rapporterer derfor ét tryk eller slip, selv om kontakten har prellet.

Man kan også bruge GPIO-interrupts med attachInterrupt() og vælge FALLING, RISING eller CHANGE. Et interrupt på et råt knapsignal kan dog udløses flere gange af kontaktprel; filtrér signalet i hardware først, eller lad interruptet sætte et flag, som programmet debounce-behandler uden for interrupt-rutinen.

Hardware kan altså hjælpe: RC-filteret og Schmitt-triggeren vist ovenfor former prel til en ren digital overgang. Mikrokontrolleren kan derefter registrere den med almindelig polling eller et interrupt. En separat pulskreds er kun nødvendig, hvis kredsløbet også skal levere en kort puls i stedet for et stabilt HIGH/LOW-niveau.

Ikke-blokerende debounce med millis()

Her registreres en tilstand først, når den rå aflæsning har været uændret i debounce-perioden. Koden bruger ikke delay(), så resten af programmets loop kan fortsætte.

const byte buttonPin = 2;
const unsigned long debounceMs = 25;

int rawState = HIGH;
int stableState = HIGH;
unsigned long lastChangeMs = 0;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  int reading = digitalRead(buttonPin);

  if (reading != rawState) {
    rawState = reading;
    lastChangeMs = millis();
  }

  if ((millis() - lastChangeMs >= debounceMs) && reading != stableState) {
    stableState = reading;

    if (stableState == LOW) {
      Serial.println("Trykket");
    } else {
      Serial.println("Sluppet");
    }
  }

  // Resten af programmet kan fortsætte her uden delay().
}

Vigtige detaljer

Kilder