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
Pingback: 3 Tasten: Einen Wert einstellen und im EEPROM ablegen | Shelvin – Elektronik ausprobiert und erläutert
Wenn der Taster nach den 20ms losgelassen wird und dabei einmal prellt dürfte die LED nochmals umgeschalten werden, oder?
Da hast Du recht.
Bei nocheinmal LOW nach mindestens 20ms wird der Interrupt wieder aufgerufen.
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.
Abhilfe wäre nicht LOW sondern FALLING
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.