MezData-Logo

Entprellen von Tasten

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.

Zähler für Tastendrücke

PAP Entprellen

Ein Zähler soll die Tastendrücke auf PD0 zählen und binär auf PORTB ausgeben.

Quellcode [entprellen.c]
#include <avr/io.h>     // Definitionen laden 
#include <util/delay.h> // Delay-Bibliothek laden 
 
int main(){         // Hauptprogramm 
  PORTB=0xe0;       // alle LED aus 
  DDRB=0xff;        // PB als Ausgang
  PIND=0x7f;        // PullUps an
  unsigned char zaehler = 0;
  while(1){         // Endlosschleife 
    while(PIND&1<<PD4);  // solange Taste nicht gedrueckt
   // _delay_ms(10);  // Prellen abwarten 
    zaehler++;
    PORTB= 0xe0 | zaehler&0xf; // Ausgeben
    while(!(PIND&1<<PD4)); // solange Taste gedrueckt
  } 
}

Um die Impulse durch das Prellen nicht mit zu zählen wird nach dem ersten Impuls so lange wartet, 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 _delay_ms() auskommentiert werden um die Auswirkung des Prellens zu studieren.

 

 

Abfrage, Maskierung und Entprellen mehrerer Tasten

Quellcode [keycheck_c.c]
#include <avr/io.h>     // Definitionen laden 
#include <util/delay.h> // Delay-Bibliothek laden 
 
#define KEYREADER ~PIND >> 4    // Einlesen, invertieren, maskieren zurecht schieben
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 = KEYREADER;          // Einlesen
  if (keyOld != keyTest){       // hat sich was getan 
    _delay_ms(10);              // Prellen abwarten 
    tmp = KEYREADER;            // noch mal Einlesen
    if (tmp == keyTest){        // ist es stabil? 
      keyEnter = (~keyOld) & keyTest; // steigende Flanke !alt und neu 
      keyExit = keyOld & (~keyTest);  // fallende Flanke alt und !neu 
      keyOld = keyTest; 
    } 
  } 
} 
 
int main(){         // Hauptprogramm 
  PORTB=0xe0;       // alle LED aus 
  DDRB=0xff;        // PB als Ausgang
  PIND=0x7f;        // PullUps an
  unsigned char zaehler = 0;
  while(1){         // Endlosschleife 
    keyCheck();     // Tastatur abfragen
    if(keyEnter&1){ // wenn Taste0 gedrueckt
      zaehler++;
      PORTB= 0xe0 | zaehler&0xf; // Ausgeben
    }
  } 
}

Ein Unterprogramm für eine recht störresistente entprellte Tastaturabfrage. Neu gedürchte Tasten werden in keyenter und losgelassene Tasten in keyexit erfasst. Das Invertieren der Eingänge, Maskieren und Zurechtschieben kann mit KEYREADER eingestellt werden.

Gedanken zur Größe des PullUp-Widerstandes

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:

Tyischer Einbau mit Header-Datei

[Include-Files (C)] Der C-konforme Ansatz mit Einbindung in das Projekt ist wesentlich komplizierter. In einer Header-Datei werden die im Hauptprogramm benötigten Variablen und Schnittstellen deklariert. In der .c-Datei wird der Quellcode definiert. Die Headerdatei wird ins Hauptprogramm und in die gleichnahmige Definitionsdatei eingebunden (mit #inlcude). Eine doppelte Deklaration (Fehlermeldung!) wird mit #ifndef vermieden.

Quellcode [checkKeys.h]
#ifndef CHECKKEYS_H_INCLUDETD // vermeide Doppeldeklarationen
#define CHECKKEYS_H_INCLUDETD
 
#ifdef __cplusplus
extern "C" {
#endif
unsigned char keyEnter,keyExit; // gedr. und losgel. Tasten 
 
void checkKeys(); // Maske, neg.Logik 
 
#ifdef __cplusplus
} // extern "C"
#endif
#endif
Quellcode [checkKeys.c]
#include <avr/io.h>     // Definitionen laden 
#include <util/delay.h> // CPU Frequenz einstellen! 
#include "checkKeys.h"
 
void checkKeys(){// Maske, neg.Logik
  static unsigned char keyOld = 0;
  unsigned char keyTest,tmp; 
  keyEnter = 0, keyExit = 0; 
  keyTest =  ~PIND>>4; // Einlesen
  if (keyOld != keyTest){             // hat sich was getan 
    _delay_ms(20);                    // Prellen abwarten 
    tmp = ~PIND>>4;;   // noch mal Einlesen
    if (tmp == keyTest){              // ist es stabil? 
      keyEnter = (~keyOld) & keyTest; // steig. Fl. !alt & neu 
      keyExit = keyOld & (~keyTest);  // fall. Fl. alt & !neu 
      keyOld = keyTest; 
    } 
  } 
}
Quellcode [zEntprellen.ino]
#include <avr/io.h>       // Definitionen laden 
#include <util/delay.h>   // CPU Frequenz einstellen!
 
#include "checkKeys.h"    // Header einbinden
 
int main(){               // Hauptprogramm 
  PORTB=0xe0;       // alle LED aus 
  DDRB=0xff;        // PB als Ausgang
  PIND=0x7f;        // PullUps an
  while(1){               // Endlosschleife 
    checkKeys(); // Maske,NLogik
    if(keyEnter&1){       // Taste gedrueckt?
      PORTB^=1;           // LED an PB0 invertieren
    }
    if(keyEnter&2){       // Taste gedrueckt?
      PORTB^=2;           // LED an PB1 invertieren
    }
  } 
}

Arduino-Projekt: zEntprellen.zip

OOP Lösung

Der Compiler kann auch C++. Zunächst wird die Klasse deklariert. In der .cpp-Datei wird der Quellcode definiert. Im Hauptprogramm wird die Header-Datei eingebunden und ein KeyChecker-Objekt erzeugt.

Quellcode [KeyChecker.h]
class KeyChecker{
  public:
    KeyChecker();
    void checkKeys();
    unsigned char getEnter();
    unsigned char getExit();
  private:
    unsigned char keyOld,keyEnter, keyExit;
};
Quellcode [KeyChecker.cpp]
#include <avr/io.h>     // Definitionen laden
#include <util/delay.h>
#include "KeyChecker.h"
 
KeyChecker::KeyChecker(){}
 
void KeyChecker::checkKeys(){         // Tastaturabfr.
  unsigned char keyTest,tmp; 
  keyEnter = 0, keyExit = 0; 
  keyTest = ~PIND>>4; // Einlesen
  if (keyOld != keyTest){             // hat sich was getan 
    _delay_ms(20);                    // Prellen abwarten 
    tmp = ~PIND>>4;   // noch mal Einlesen
    if (tmp == keyTest){              // ist es stabil? 
      keyEnter = (~keyOld) & keyTest; // steig. Fl. !alt und neu 
      keyExit = keyOld & (~keyTest);  // fall. Fl. alt und !neu 
      keyOld = keyTest; 
    } 
  } 
}
unsigned char KeyChecker::getEnter(){
  return keyEnter;
}
unsigned char KeyChecker::getExit(){
  return keyExit;
}
Quellcode [zEntprellenOOP.ino]
#include <avr/io.h>       // Definitionen laden
#include <util/delay.h>   // CPU Frequenz einstellen!
#include "KeyChecker.h"   // Header einbinden
 
int main(){         // Hauptprogramm 
  PORTB=0xe0;       // alle LED aus 
  DDRB=0xff;        // PB als Ausgang
  PIND=0x7f;        // PullUps an
  KeyChecker kCheck = KeyChecker();
  while(1){                       // Endlosschleife 
    kCheck.checkKeys();           // Tasten abfragen
    if(kCheck.getEnter()&1){      // Taste 0 gedrueckt?
      PINB=1;                     // LED an PB0 invertieren
    }
    if(kCheck.getEnter()&2){      // Taste 1 gedrueckt?
      PINB=2;                     // LED an PB1 invertieren
    }
  } 
}

Arduino-Projekt: zEntprellenOOP.zip

Leuchtpunkt mit Tasten wandern lassen

Leuchtpunkt Zustandsdiagramm

Erstellen Sie ein Progamm, das dieses Verhalten zeigt:

Zum Entprellen der Tasten können Sie die Funktion keycheck() verwenden.