MezData-Logo

Entprellen von Tasten mit Assembler

Taster an µC anschließen

Anschluss Taster an µCTimingdiagramm Prellen

Typischerweise werden Taster mit einem PullUp Widerstand an den Eingang eines µC angeschlossen. Dabei fällt besonders bei Anwendungen, die Tastendrücke zählen sollen auf, dass bei einem Tastendruck oft mehr als ein Impuls erzeugt wird. Die Ursache ist das Tastenprellen, Kontaktprellen. Beim Schließen des Kontaktes wird die Verbindung noch mehrmals unterbrochen durch das Nachfedern der Kontake.

Nebenstehend ein Timingdiagramm für das Prellen eines Drehwellen-Schnappschalters der Firma Marquardt der im Münzprüfer eines Kunstautomaten verbaut wurde. Bei diesem Schalter ist das Prellen nach 1,2 ms vorbei und ein konstanter Pegel erreicht. Es gibt mehrere Lösungsansätze dem Prellen zu begegnen.

Hier sollen softwarebasierte Lösungen mit Warteschleifen vorgestellt werden.

Noch ein paar Gedanken zur Größe des PullUp-Widerstands. Sei der µC in einem Fahrradtacho eingesetzt und der Schalter sei der Reedkontakt an der Gabel, der durch einen Magneten an einer Speiche geschlossen wird. Die Batterie sei eine LR2032 3V mit 40mAh Kapazität:

Zähler für Tastendrücke

PAP Entprellen

Quellcode [entprellen.asm]
.include "tn2313def.inc"
  .def tmp= R16
  .def zaehler = R17
  .def wi = R18 ; Warteschleife innen
  .def wa = R19 ; Warteschleife aussen
init: 
  ldi tmp, low(RAMEND) ; Stackpointer initialisieren
  out SPL, tmp
  ldi tmp,$ff
  out PORTB,tmp ; Ausgaenge auf 1 setzen damit nix leuchtet
  out DDRB,tmp  ; PB aus Ausgang
main:
  sbic PIND,0 ; ist PD0 = 1
  rjmp main   ; dann weiter abfragen
  rcall warte ; Prellen abwarten
  inc zaheler ; zaehler++
  mov tmp,zaehler ; zaehler ausgeben
  com tmp     ; invertieren wegen neg. Logik
  out PORTB,tmp
solangeGedrueckt:
  sbis PIND,0 ; ist PD0 = 0
  rjmp solangeGedrueckt ; dann weiter abfragen
  rcall warte ; evtl. Prellen abfragen
  rjmp main
warte: ; rcall 3 Takte
  ldi wa, 5 ; Wartezeit in ms @ 1MHz (+7 Takte Unterprogrammaufruf und ret)
warteaussen: 
  ldi wi,249  ; (249 * 4) + 4 = 1000 Takte
  nop
warteinnen: 
  nop
  dec wi
  brne warteinnen
  dec wa
  brne warteaussen
  ret ; 4 Takte

Die Idee ist einfach -nach dem ersten Impuls so lange warten, bis das Prellen vorbei ist. Dazu muss die Wartezeit warte1 länger als die Prellzeit des Tasters sein. Sollte der Taster beim Loslassen auch prellen muss dafür eine Wartezeit warte2 eingebaut werden. Versuchsweise können die Unterprogrammaufrufe rcall warte auskommentiert werden um die Auswirkung des Prellens zu studieren.

Abfrage, Maskierung und Entprellen mehrerer Tasten

Ein Unterprogramm für eine recht störresistente entprellte Tastaturabfrage. Neu gedürchte Tasten werden in keyenter und losgelassene Tasten in keyexit erfasst..

[keycheck.asm] [keycheck_c.c]
.def keyold = R21        ;Taster die gedrueckt waren
.def keynew = R22        ;Taster die gedrueckt sind
.def keyenter = R23      ;Taster, die neu gedrueckt wurden
.def keyexit = R24       ;Taster, die neu losgelassen wurden
 
keycheck:                ;Unterprogramm zur Tastaturabfrage
  ldi keyenter,0
  ldi keyexit,0
  in keynew,PIND         ;Lade PD
  com keynew             ;invertieren
  andi keynew,0b00001111 ;Maskieren der nicht verwendeten Pins
  cpse keyold,keynew     ;Test ob Veraenderung
  rjmp keyaction         ;Veraenderung
  ret                    ;alles gleich
keyaction:
  rcall wait12ms         ;etwas warten
  in temp,PIND           ;nochmal einlesen
  com temp               ;invertieren bei neg. Logik
  andi temp,0b00001111   ;maskieren der nicht verwendeten Pins
  cpse keynew,temp       ;ist es stabil
  ret                    ;war nix, da nicht stabil
  mov keyenter,keyold
  com keyenter           ;invertieren
  and keyenter,keynew    ;steigende Flanken, !alt & neu
  mov keyexit,keynew
  com keyexit            ;invertieren
  and keyexit,keyold     ;fallende Flanken, alt & !neu
  mov keyold,keynew      ;alt := neu
  ret
unsigned char keyOld = 0;       // alter Tasten-Zustand 
unsigned char keyEnter,keyExit; // gedrueckte und losgelassene Tasten 

void keyCheck(){          // Tastaturabfrage mit Flankendedektion  
  unsigned char keyTest,tmp; 
  keyEnter = 0, keyExit = 0; 
  keyTest = ~PIND & 0b01111111; // Einlesen und zurechtschieben 
  if (keyOld != keyTest){       // hat sich was getan 
    _delay_ms(10);              // Prellen abwarten 
    tmp = ~PIND & 0b01111111;   // nochmal Einlesen und zurechtschieben 
    if (tmp == keyTest){        // ist es stabil? 
      keyEnter = (~keyOld) & keyTest; // steigende Flanke !alt und neu 
      keyExit = keyOld & (~keyTest);  // fallende Flanke alt und !neu 
      keyOld = keyTest; 
    } 
  }