Taster mit Interrupt entprellen

Guten Abend Gemeinde,

bin seit ein paar Wochen angefixt vom Arduino und komme nicht mehr weg von dem :smiley:
Habe schon einige Tutorials durch und auch schon einiges “gebastelt” mit den Kindern zusammen.

Jetzt zu meinem Problem. Irgendwie hab ich einen Knoten im Hirn. Versuche seit Stunden auf die Lösung zu kommen. Aber ich komme nicht weiter.

Ich will mit einem Taster die Hintergrundbeleuchtung an einem LCD ein- und ausschalten.
Das klappt mit dem anhängenden Sketch super. Ich dachte nur ich kann das irgendwie in einem Rutsch in der ISR erledigen … Nur wie ???

Ich hoffe ich bekomme einen Schlag auf den Hinterkopf :grin:
Auch falls sonst irgendwelche Fehler im Sketch sind (auch wenn er geht, heißt es ja nicht das er gut ist :wink: ), freue ich mich über Verbesserungsvorschläge.

Danke im voraus! Bin für jeden Tipp dankbar.
Schönes Restwochenende !

// Wire Bibliothek inkludieren für die I2C Kommunikation (SDA, SCL)
#include <Wire.h>
// I2C-LCdisplay Bibliothek inkludieren
#include <LiquidCrystal_I2C.h>

/* ************************* D E F I N I T I O N E N ************************************************************ */

// Definitionen benötigen weniger Speicherplatz, da der Compiler nur den NAMEN mit dem WERT ersetzt.

#define TASTER_LCD_BACKLIGHT 18                 // Taster um die LCD Beleuchtung ein/aus zu schalten

LiquidCrystal_I2C lcd(0x27, 20, 4);             // I2C-LCD Objekt erstellen mit der Adresse 0x27, mit 20 Zeichen und 4 Zeilen


/* ************************* V A R I A B L E N ******************************************************************* */

byte entprellzeit = 200;                         // Entprellzeit des Taster. ACHTUNG BYTE nur bis 255 ms !!!
unsigned long lastTimeLCDtaster = 0;             // Variable für den Timer des Tasters zum entprellen
byte zustandLCDtaster = HIGH;                    // Variable für den LCD Tasterzustand. Volatile um die Variable immer neu zu lesen und nicht aus dem Speicher !

/* ************************* S E T U P *************************************************************************** */

void setup() {

  pinMode(TASTER_LCD_BACKLIGHT, INPUT);                                         // Pin ist ein INPUT für den Taster der Hintergrundbeleuchtung
  attachInterrupt(digitalPinToInterrupt(TASTER_LCD_BACKLIGHT), lcdLight, HIGH); // Interrupt an den Taster INPUT aktivieren und bei HIGH Funktion "lcdLight ausführen

  lcd.init();                                                                   // LCDisplay initialisieren
  lcd.backlight();                                                              // LCDisplay Hintergrundbeleuchtung AN
  lcd.clear();                                                                  // LCDisplay löschen

}
/* ************************* P R O G R A M M A B L A U F ********************************************************* */

void loop() {

  // Taster per Interrupt überwachen für Hintergrundbeleuchtung
  if (zustandLCDtaster == HIGH) {
    lcd.backlight();
  }
  else  {
    lcd.noBacklight();
  }

}

/* ************************* E I G E N E   F U N K T I O N E N *************************************************** */


// LCD Hintergrundbeleuchtung AN/AUS mit entprelltem Taster
void lcdLight()
{
  if ((millis() - lastTimeLCDtaster) > entprellzeit) {
    zustandLCDtaster = !zustandLCDtaster;
    lastTimeLCDtaster = millis();
  }
}

Ich dachte nur ich kann das irgendwie in einem Rutsch in der ISR erledigen ... Nur wie

Das ist mit einer Hürde verbunden.

In einer ISR sind die Interrupts gesperrt. I2C funktioniert bei gesperrten Interrupts nicht.

unsigned long lastTimeLCDtaster = 0;             // Variable für den Timer des Tasters zum entprellen
byte zustandLCDtaster = HIGH;                    // Variable für den LCD Tasterzustand. Volatile um die Variable immer neu zu lesen und nicht aus dem Speicher !

Diese Variablen müssen als volatile gekennzeichnet werden, da sie von dir in einer ISR genutzt werden.

Tipp: Tasten entprellen geht auch ohne Interrupts. (oft so gar viel besser)

Denn: Dein Tasten Interrupt betreibt Dauerfeuer, solange der Taster betätigt wird. Nur ein einzelnes Maschinenkommando wird zwischen den ISR Aufrufen ausgeführt. Die Performance des Restprogramms dürfte auf ca 1% zusammenbrechen.

Danke combie, für den Schlag auf den Hinterkopf !

Hab das jetzt mit dem Interrupt gelassen. Und es läuft so wie es soll.
So schaut´s aus.

// Wire Bibliothek inkludieren für die I2C Kommunikation (SDA, SCL)
#include <Wire.h>
// I2C-LCdisplay Bibliothek inkludieren
#include <LiquidCrystal_I2C.h>

/* ************************* D E F I N I T I O N E N ************************************************************ */
// Definitionen benötigen weniger Speicherplatz, da der Compiler nur den NAMEN mit dem WERT ersetzt.

#define TASTER_LCD_BACKLIGHT 18         // Taster um die LCD Beleuchtung ein/aus zu schalten

/* ************************* O B J E K T E *********************************************************************** */

LiquidCrystal_I2C lcd(0x27, 20, 4);     // I2C-LCD Objekt erstellen mit der Adresse 0x27, mit 20 Zeichen und 4 Zeilen

/* ************************* V A R I A B L E N ******************************************************************* */

byte entprellzeit = 200;                // Entprellzeit des Tasters. ACHTUNG BYTE nur bis 255 ms !!!
unsigned long lastTimeLCDtaster = 0;    // Variable für den Timer des Tasters zum entprellen
byte zustandLCDtaster = LOW;            // Variable für den LCD Tasterzustand.
byte merkerLCD = HIGH;                  // Variable, um den den Zustand des Backlight zu speichern.

/* ************************* S E T U P *************************************************************************** */

void setup() {

  pinMode(TASTER_LCD_BACKLIGHT, INPUT);   // Pin ist ein INPUT für den Taster der Hintergrundbeleuchtung
  lcd.init();                             // LCDisplay initialisieren
  lcd.backlight();                        // LCDisplay Hintergrundbeleuchtung AN
  lcd.clear();                            // LCDisplay löschen

}
/* ************************* P R O G R A M M A B L A U F ********************************************************* */

void loop() {

  tasterLCDbacklight();

}

/* ************************* E I G E N E   F U N K T I O N E N *************************************************** */


// LCD Hintergrundbeleuchtung AN/AUS mit entprelltem Taster
void tasterLCDbacklight()
{
  zustandLCDtaster = digitalRead(TASTER_LCD_BACKLIGHT);

  // wird Taster gerade gedrückt, WENN JA UND vergangene Zeit > entprellzeit dann -->
  if (zustandLCDtaster == HIGH && (millis() - lastTimeLCDtaster) > entprellzeit)  {

    lastTimeLCDtaster = millis();   // Zeitpunkt neu speichern

    if (merkerLCD == HIGH) {        // WENN LCD Backlight AN war
      lcd.noBacklight();            // dann mach LCD Backlight AUS
      merkerLCD = LOW;              // Zustand AUS speichern
    }
    else if (merkerLCD == LOW) {    // WENN LCD Backlight AUS war
      lcd.backlight();              // dann mach LCD Backlight AN
      merkerLCD = HIGH;             // Zustand AN speichern
    }
  }
}

Schön, dass es läuft!
(So ganz will mir das noch nicht, mit dem entprellen, in den Kopf…)

Danke combie, für den Schlag auf den Hinterkopf !

:grin:

combie: (So ganz will mir das noch nicht, mit dem entprellen, in den Kopf..)

So wirklich 'Entprellen' ist das aus meiner Sicht auch nicht. Nachdem das erstmal HIGH erkannt wurde, wird der Taster 200ms lang ignoriert, so dass das Prellen nicht zu einer Mehrfach-Auslösung des Umschaltens führt. Wenn man mehr als 200ms auf dem Taster draufbleibt, müsste er aber wieder erkannt werden, und das Backlight wieder ausschalten. Und nach weiteren 200ms wieder an ....

Meiner Meinung nach müsste für eine saubere 'Taster'-Funktion erst wieder LOW erkannt werden, bevor erneut umgeschaltet werden kann.

Was passiert denn, wenn Du auf dem Taster draufbleibst?

200mS sind lang; da reichen auch 5-10mS max 50mS. Grüße Uwe

MicroBahner: Was passiert denn, wenn Du auf dem Taster draufbleibst?

OK ... zu früh gefreut. Auf dem Taster mal drauf zu bleiben bin ich nicht gekommen. Stimmt! Display geht AN .. AUS .. AN .. AUS :smiling_imp:

Dann muss ich mich nochmal dran machen/wagen.

MicroBahner: Meiner Meinung nach müsste für eine saubere 'Taster'-Funktion erst wieder LOW erkannt werden, bevor erneut umgeschaltet werden kann.

Mal überlegen, ob mir dazu irgendwas einfällt ... bin noch am rätseln :-)

dfence: Mal überlegen, ob mir dazu irgendwas einfällt ... bin noch am rätseln :-)

Du setzt dir ein Flag, wenn Du die Displaybeleuchtung umgeschaltet hast und blockierst damit ein weiteres Umschalten. Erst wenn der Tastereingang wieder auf LOW ist, wird das Flag zurückgesetzt.

Erstmal danke an alle Helfenden für die Tipps ! Ich mach jetzt schon Stunden rum ... gestern und heute ... und ich krieg es einfach nicht gebacken. Ich dreh mich irgendwie im Kreis und bekommen das nicht sauber zum Laufen.

Weiß nicht mehr weiter. Schmeiß´ jetzt dann den Rechner an die Wand :-)))))))))))

So … man muss nur hartnäckig bleiben :smiley:
Ich glaube, ich hab es hinbekommen. Zwar nicht komplett mit IF-Anweisungen (da war ich kurz vor dem Nervenzusammenbruch), aber mit einer Mischung aus IF- und Switch/Case.

Eine Bitte hätte ich an die Könner hier … mal drüber schauen, ob das so korrekt ist und mich zusammenstauchen falls nicht :wink:
Es geht eigentlich nur um die Funktion ganz unten.

Danke an alle Beteiligten für die Tipps und Hilfestellung gegeben haben !

// Wire Bibliothek inkludieren für die I2C Kommunikation (SDA, SCL)
#include <Wire.h>
// I2C-LCdisplay Bibliothek inkludieren
#include <LiquidCrystal_I2C.h>

/* ************************* D E F I N I T I O N E N ************************************************************ */

// Definitionen benötigen weniger Speicherplatz, da der Compiler nur den NAMEN mit dem WERT ersetzt.

#define TASTER_LCD_BACKLIGHT 18         // Taster um die LCD Beleuchtung ein/aus zu schalten

/* ************************* O B J E K T E *********************************************************************** */

LiquidCrystal_I2C lcd(0x27, 20, 4);     // I2C-LCD Objekt erstellen mit der Adresse 0x27, mit 20 Zeichen und 4 Zeilen

/* ************************* V A R I A B L E N ******************************************************************* */

byte entprellzeit = 50;                         // Entprellzeit des Taster. ACHTUNG BYTE nur bis 255 ms !!!
unsigned long lastTimeLCDtaster = 0;            // Variable für den Timer des Tasters zum Entprellen
byte LCDswitch = LOW;                           // Variable, ob der Taster gerade gedrückt wird, oder nicht
byte LCDmerker = LOW;                           // Variable, um den den Zustand des Backlight zu speichern AN/AUS
byte LCDstatusNow;                              // Variable für den aktuellen Zustand des Backlight
byte LCDstatusLast = LOW;                       // Variable für den letzten Zustand des Backlight

/* ************************* S E T U P *************************************************************************** */

void setup() {

  pinMode(TASTER_LCD_BACKLIGHT, INPUT);   // Pin ist ein INPUT für den Taster der Hintergrundbeleuchtung
  lcd.init();                             // LCDisplay initialisieren
  lcd.backlight();                        // LCDisplay Hintergrundbeleuchtung AN
  lcd.clear();                            // LCDisplay löschen

}
/* ************************* P R O G R A M M A B L A U F ********************************************************* */

void loop() {

  tasterLCDbacklight();

}

/* ************************* E I G E N E   F U N K T I O N E N *************************************************** */


// LCD Hintergrundbeleuchtung AN/AUS mit entprelltem Taster
void tasterLCDbacklight()
{
  // Tasterabfrage und in Variable LCDswitch speichern ob HIGH oder LOW
  LCDswitch = digitalRead(TASTER_LCD_BACKLIGHT);

  // Prüfen, ob der Taster HIGH ist. Egal ob durch absichtliches drücken oder prellen.
  // Wenn der Tasterzustand sich zum vorherigen geändert hat DANN -->
  if (LCDswitch != LCDstatusLast) {
    // setze jedesmal den Timer zurück, wenn von LOW nach HIGH gewechselt wird
    lastTimeLCDtaster = millis();
  }

  // erst WENN die vergangene Zeit seit letztem LOW/HIGH > der Entprellzeit ist DANN -->
  if ((millis() - lastTimeLCDtaster) > entprellzeit) {

    // WENN der Tasterzustand sich geändert hat DANN -->
    if (LCDswitch != LCDstatusNow) {
      LCDstatusNow = LCDswitch;

      // "invertiere" LCDmerker NUR WENN das Backlight HIGH ist
      if (LCDstatusNow == HIGH) {
        LCDmerker = !LCDmerker;
      }
    }
  }

  // Switch/Case Anweisung
  switch (LCDmerker) {
    // Wenn Backlight AN ist
    case HIGH:
      // Backlight AUS
      lcd.noBacklight();
      break;
    // Wenn Backlight AUS ist
    case LOW:
      // Backlight AN
      lcd.backlight();
      break;
  }

  // letzten LCD Backlight Zustand speichern
  LCDstatusLast = LCDswitch;
}