Eine Taste per Interrupt einlesen und entprellen

Ein Taster an speziellen Anschlüssen kann per Interrupt das laufende Programm anhalten und sofort ein Unterprogramm per Interrupt ausführen.

Die Arduino Uno und Arduino Nano haben 2 solche Interrupt Pins
Pin 2 für Interrupt 0
Pin 3 für Interrupt 1
Für andere Arduino Versionen gibt es hier die Beschreibung.

Wenn z.B. der Pin2 mit digitalWrite(2, HIGH) über den internen Pull up Widerstand auf 5V gelegt wird und als INPUT definiert wird, ist nur noch ein Schalter oder Taster gegen Masse notwendig.

Der Interrupt 0 wird mit attachInterrupt(0, interruptRoutine, LOW) definiert wenn er auf LOW ist. Das sind also 0V, der Taster ist gedrückt, dann wird die interruptRoutine() ausgeführt.

Tasten haben aber die unangenehme Eigenschaft zu prellen, das heisst sie schliessen nicht immer in Nullzeit, sonder „zappeln“ etwas. Es können mehrere 1010 Zustände entstehen bis der Schalter wirklich dauerhaft geschlossen. Dadurch können mehrere Umschaltungen erkannt werden und der Interrupt wird häufiger angestoßen. Das sollte durch entsprechende Hardware (Stichwort: RC Glied und Schmitt Trigger) oder in der Software gelöst werden.

delay() wird natürlich auch durch den neuen Interrupt unterbrochen und kann nicht zur Entprellung verwendet werden. Hier bietet sich millis() an, der interne Zähler. Das heisst der Interrupt schaltet hier die LED um und merkt sich die Zeit.  Innerhalb der Entprellzeit wird er nun nicht wieder die LED umschalten, selbst wenn mehrere Interrups kommen sollten. Ich habe hier eine Entprellzeit von 20ms gewählt, das ist eine typische Zeit, sollte aber für die verwendete Taste überprüft werden.

Das Programm

// Tasten Interrupt mit Entprellung
//
// Matthias Busse Version 1.0 vom 26.10.2014

int LED=13;
volatile unsigned long alteZeit=0, entprellZeit=20;

void setup() {
  pinMode(LED, OUTPUT);    // LED Pin
  pinMode(2, INPUT);       // Pin 2 ist INT0
  digitalWrite(2, HIGH);   // interner Pull up Widerstand auf 5V
  attachInterrupt(0, interruptRoutine, LOW);
  // Pin 2 (INT 0) geht auf 0V (LOW) dann interruptRoutine aufrufen
}

void loop() {
  delay(1000);             // Dauerschleife
}

void interruptRoutine() {
  if((millis() - alteZeit) > entprellZeit) { 
    // innerhalb der entprellZeit nichts machen
    digitalWrite(LED, !digitalRead(LED)); // LED umschalten
    alteZeit = millis(); // letzte Schaltzeit merken      
  }
}

Die Hardware

ist bei dieser einfachen Schaltung nur ein Taster zwischen Pin 2 und Masse.

Ein Tip

Für zeitkritische Programmteile können die Interrupts ausgeschaltet werden mit noInterrups() und mit interrupts() danach wieder eingeschaltet werden.

von Matthias Busse

6 Gedanken zu „Eine Taste per Interrupt einlesen und entprellen

  1. Pingback: 3 Tasten: Einen Wert einstellen und im EEPROM ablegen | Shelvin – Elektronik ausprobiert und erläutert

      1. Michael_x

        Abhilfe wäre:
        – den alten Zustand selbst merken
        attachInterrupt(0,func,CHANGE);

        Und dann nur die die erste fallende Flanke als Aktionstrigger zulassen
        Wenn der erste Wechsel ausserhalb der Prellzeit LOW -> HIGH ist, nur die Prell-Überwachung starten.

        P.S. Besser finde ich, loop() nicht so zu verlangsamen, so dass man für Taster keinen Interrupt braucht (alle zig Millisekunden abfragen reicht für spontane Reaktion).
        Auch kann man in einer Interrupt-Routine nicht beliebig komplexe Sachen machen: Schon eine LCD Ausgabe verbietet sich.

        Antworten
  2. Benjamin

    delay() würde funktionieren, wenn diese innerhalb der ISR gehalten wird. Kommt natürlich darauf an, ob es ein pending-bit beim Prozessor gibt, was beim Mega2560 der Fall ist. Hier wird bei Eintritt in die ISR das pending-bit prozessorseitig gecleart, wodurch sicher gestellt wird, dass kein anderer Interrupt durchgelassen wird. Am Ende der ISR wird diese Bit wieder prozessorseitig gesetzt, sodass wieder Interrupts erlaubt werden.

    Antworten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.