Taster einlesen
Das Einlesen eines Tastschalters (Tasters), ist auf den ersten Blick trivial. Es können allerdings Probleme auftreten, die genauer betrachtet werden sollten:
Tastschalter (Taster)
Ein Tastschalter schließt einen Kontakt beim Betätigen, beim Loslassen öffnet sich der Kontakt selbstständig wieder, weil eine Feder verbaut ist.

Schaltbild eines Tastschalters (Taster)
Manuelle Eingaben für Mikrocontroller werden immer Tastschaltern gemacht.
Ein normaler Schalter dagegen rastet beim Betätigen in der neuen Stellung ein. Der Kontakt bleibt geschlossen. Erst bei nochmaligem Betätigen wird der Kontakt wiederrum dauerhaft geöffnet:

Schaltbild eines Schalters (Schließer)
Rauschen am offenen Pin
Würde ein Taster einfach nur eine Verbindung zu Masse oder der Betriebsspannung UBB herstellen, so wird das Signal zwar beim Betätigen des Taster im definierten Low- bzw. High-Bereich liegen, beim Loslassen allerdings wirkt das offene Leitungsende in der Praxis oft wie eine Antenne und es können sich infolge des Rauschens zufällige Pegel einstellen:

Rauschen am offenen Pin
Folgender Code bezieht sich auf das obere Bild und funktioniert also hier nicht zuverlässig:
int taster = 8;
void setup() { pinMode(taster, INPUT); // Pin 8 als Eingang konfigurieren } void loop() { int tasterstatus=digitalRead(taster); // Taste einlesen if (tasterstatus == LOW) // Pin auf Masse schalten => LOW, Pin offen => Rauschen
// Verarbeitung, solange Taste gedrückt
else
// Rauschen = NICHT DEFINIERT }
Das Rauschen kann mit Pull-Up oder Pull-Down-Widerständen elimiert werden:
Pull-Up- und Pull-Down-Widerstände
Mit einem Pull-Up-Widerstand wird ein offener Pin auf Betriebsspannung UB "hochgezogen":

Ein Pull-Up-Widerstand zieht den Pin bei offenem Taster auf Betriebsspannung.
Die gleiche Funktion mit umgekehrter Logik kann mit Pull-Down-Widerständen erreicht werden. Hier wird ein offener Pin auf Masse "herunter" gezogen:

Ein Pull-Down-Widerstand zieht einen offenen Pin auf Masse.
Natürlich ist beides mit erhöhtem Schaltungsaufwand verbunden:

Zusätzlicher Schaltungsaufwand durch externen Pull-Up-Widerstand
https://starthardware.org/arduino-und-taster/ (06.01.2022)
Achtung: Der Widerstand darf nicht zu klein sein, sonst fließt bei Tastendruck ein zu großer Strom über ihn. Empfehlung: R = 10kΩ - 100kΩ.
Folgender Code funktioniert:
int taster = 8;
void setup() { pinMode(taster, INPUT); // Pin 8 als Eingang konfigurieren } void loop() { int tasterstatus=digitalRead(taster); // Taste einlesen if (tasterstatus == LOW) // Pin auf Masse schalten => LOW, auf UB schalten => HIGH
// Verarbeitung, solange Taste gedrückt
else
// Verarbeitung, solange Taste NICHT gedrückt }
Interne Pull-Up-Widerstände
Glücklicherweise haben Mikrocontroller oft eingebaute, interne Pull-Up-Widerstande, die lediglich aktiviert werden müssen. Dann reduziert sich der Schaltungsaufwand wieder. Der Taster muss natürlich den Pin auf Masse/Ground schalten:

Reduzierter Schaltungsaufwand durch Verwendung des internen Pull-Up-Widerstands
https://starthardware.org/arduino-und-taster/ (06.01.2022)
In der Setup()-Funktion muss der Pull-Down-Widerstand lediglich aktiviert werden:
int taster = 8;
void setup() {
pinMode(taster, INPUT_PULLUP) // Pin 8 als Eingang mit aktiviertem Pull-Up-Widerstand konfigurieren } void loop() { int tasterstatus=digitalRead(taster); // Taste einlesen if (tasterstatus == LOW) // Bei Betätigung des Tasters wird der Pin auf Masse/Ground geschaltet
// Verarbeitung, solange Taste gedrückt
else
// Verarbeitung, solange Taste nicht gedrückt }
Bemerkung: Im Gegensatz zum ATmega328 auf dem Arduino Uno-Board hat der ESP32 auch zusätzlich intern zuschaltbare Pull-Down-Widerstände.
Obige Programmbeispiele sind geeignet, Aktionen wiederholt durchzuführen, solange Taster gedrückt (oder nicht gedrückt) sind. Es bleiben zwei zu lösende Probleme:
- Wie erreicht man, dass eine Aktion bei Tastendruck nur ein einziges Mal ausgeführt wird?
- Wie kann man Tastschalter software-seitig entprellen?
Taktflankenerkennung
Soll die Aktion Betätigen (oder Loslassen) der Taste einmalig ausgeführt werden, dann muss die Taktflanke erkannt werden.

Steigende und fallende Taktflanke
Dazu vergleicht man in einer Schleife den momentanen Zustand des Eingangs mit dem Zustand davor (Programmzeile 21-25). Die Änderung wird mit der XOR-Funktion erkannt (23). Um festzustellen, ob es sich um einen positiven oder negativen Taktflankenwechsel handelt, wird das Ergebnis der XOR-Funktion mit dem derzeitigen Signalzustand UND-verknüpft (26):
|
Zustand vorher |
Zustand jetzt |
XOR |
jetzt UND XOR |
Beschreibung |
|
0 |
0 |
0 |
|
keine Flanke |
|
0 |
1 |
1 |
1 |
pos. Flanke |
|
1 |
0 |
1 |
0 |
neg. Flanke |
|
1 |
1 |
0 |
|
keine Flanke |
Entprellung
Eine Kontaktfeder wird durch das Drücken des Tastelements auf einen anderen Kontakt gedrückt. Wenn die Kontaktfeder das Kontaktfeld berührt, federt sie jedoch nach. Dies kann dazu führen, dass die Feder wieder vom Feld abhebt und den elektrischen Kontakt kurzzeitig wieder unterbricht. Auch wenn diese Effekte sehr kurz sind, sind sie für einen Mikrocontroller viel zu lang. Für ihn sieht die Situation so aus, dass beim Drücken der Taste eine Folge von: Taste geschlossen, Taste offen, Taste geschlossen, Taste offen Ereignissen am Port sichtbar sind, die sich dann nach einiger Zeit auf den Zustand Taste geschlossen einpendelt:

Kontaktprellen (schematisch)

Kontaktprellen (Messung mit Oszilloskop)
Aus diesem Grund müssen Tasten entprellt werden: Ein 'Tastendruck' wird nicht bei der Erkennung der ersten Flanke akzeptiert, sondern es wird noch eine zeitlang gewartet. Ist nach Ablauf dieser Zeitdauer die Taste immer noch gedrückt, dann wird diese Flanke als Tastendruck akzeptiert und ausgewertet (Zeilen 19-24):
1 |
const int TASTER = 8;
const int LED = 13;
bool ledStatus = LOW;
int jetzt = HIGH;
int vorher = HIGH;
void setup() {
pinMode(TASTER, INPUT_PULLUP);
pinMode(LED, OUTPUT);
}
void loop() {
jetzt = digitalRead(TASTER);
if(jetzt ^ vorher == 1) // Flanke erkannt ^ (bitweise XOR)
{
if((jetzt ^ vorher) & jetzt == 0) // negative Flanke & (bitweise UND)
{
delay(50); // warten, eventuell verlängern
if(digitalRead(TASTER) == LOW) // Taster immer noch LOW
{
ledStatus = !ledStatus; // bitweise invertieren, bzw. negieren => aus HIGH wird LOW und umgekehrt
digitalWrite(LED, ledStatus);
}
}
}
vorher = jetzt;
}
|
|---|
Das muss natürlich nicht manuell selbst programmiert werden. Nutzen Sie einfach die Bibliothek Bounce 2 von Thomas Ouellet Fredericks:
https://github.com/thomasfredericks/Bounce2 (09.01.2021)
Es muss jetzt nur noch gelernt werden, damit umzugehen. Davon handelt die nächste Aufgabe.