LCD zur Anzeige des Kartennames usw. nach x Sekunden abschalten

Hallo Fan-Gemeinde des TonUINO.

Meinem TonUINO-Aufbau habe ich ein 4x20 I2C LC-Display spendiert.
Dieses soll im Einschaltmoment (=Strom anschließen) für x Sekunden eine Art Willkommenstext anzeigen. Anschließend sollen alle Zeichen verschwinden und die Hintergrundbeleuchtung abgeschaltet werden.

Erst mit Vorhalten einer neuen/bekannten RFID-Karte soll es erneut erleuchten. Wieder für x Sekunden, ehe es wieder gelöscht und seine Hintergrundbeleuchtung abgeschaltet wird.

Nun hab ich es mit dem Interrupt Timer1 versucht und obwohl der Interrupt sekündlich ausgelöst wird, funktioniert das Löschen und Abschalten nicht.

Bisher komm ich nicht dahinter, was ich an den wenigen Zeilen Code wohl verkehrt gemacht habe und erbitte daher Hilfe :slight_smile:
Vielen Dank!!

Hier meine Interrupt Service Routine:

//Interrupt service routine of timer1
ISR(TIMER1_OVF_vect) {
  TCNT1 = 3036;
  
  if (timer_10sec >= lcd_timeout) {
    //Clear LCD and turn of its backlight
    lcd.clear();
    lcd.noBacklight();

timer_10sec = 1;
  }
  else
  {
    timer_10sec++;
  }
}

Für den gesamten Programm-Code habe ich die Timer1-relevanten Variablen wie folgt angelegt:

// Values for interrupt Timer1
uint8_t timer_10sec = 1;
const uint8_t lcd_timeout = 10;

Nach intensivem Probieren habe ich den Eindruck, dass meine if-Anweisung nicht korrekt ausgewertet wird…

Du musst die globalen Variablen die mit einer ISR geshared werden volatile deklarieren. Also:

volatile uint8_t timer_10sec = 1;
volatile uint8_t lcd_timeout = 10;

Eventuell hilft das schon. Einen anderen Fehler sehe ich so ad hoc auch nicht.

Hm, das hat leider nicht genügt. Weder nach Programmstart, noch nach Auflegen einer RFID-Karte erlischt das Display.
„Schlimmer“ noch: Nach exakt 10 Sekunden geht auf dem Arduino-Board die rote L-LED aus und mein TonUINO reagiert auf keine neuen Karten. Es scheint als würde sich der Arduino aufhängen…

Implementiere das ganze doch mal ohne Timer und ISR. Sprich ne Funktion die du im Loop kontinuierlich aufrufst und die dann mit millis() merken und Differenz ausrechen guckt ob 10s um sind. ISRs haben sehr viele Eigenheiten die normale Funktionen nicht haben. Vielleicht läufst du da in irgend eine Falle.

i2c ist nicht gerade das schnellste protokoll. wenn du das in der isr machst, blockierst du dir damit den ganzen controller während der ausführung. eigentlich setzt man in der isr nur statusvariablen und macht die zeitfressenden sachen (z.b. displayansteuerung) dann in der mainloop.

Sehr guter Punkt. So wird es auch von Arduino empfohlen:

About Interrupt Service Routines

ISRs are special kinds of functions that have some unique limitations most other functions do not have. An ISR cannot have any parameters, and they shouldn’t return anything.

Generally, an ISR should be as short and fast as possible. If your sketch uses multiple ISRs, only one can run at a time, other interrupts will be executed after the current one finishes in an order that depends on the priority they have. millis() relies on interrupts to count, so it will never increment inside an ISR. Since delay() requires interrupts to work, it will not work if called inside an ISR. micros() works initially but will start behaving erratically after 1-2 ms. delayMicroseconds() does not use any counter, so it will work as normal.

Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile .

Vielen Dank für die zahlreichen guten Hinweise! :slight_smile:

Tatsächlich hatte ich in der ISR zunächst keine direkte Ansteuerung des I2C-Displays, sondern nur eine Status-Boolean gesetzt und die Auswertung in der Main-Loop vorgenommen. Leider ohne Erfolg, weil die If-Abfrage in der ISR einfach nicht so funktionieren wollte, wie ich es benötige. Der oben angeführte Code war mein letzter Stand.

Ich bin letztlich dem Ratschlag millis() einzusetzen gefolgt und nun funktioniert alles wie gewünscht. Danke.
:+1:

1 Like

schön das es jetzt geht, aber…

nur durch nutzung von millis() statt der isr / delay kann es ja nicht auf einmal funktionieren. da muss dann ja schon irgendwo ein anderer logikfehler gewesen sein.

der timer overflow kam auch wirklich jedes mal? woher weißt du das? ich toggle da dann immer gern einen pin und hänge mein oszi dran. dann hab ich auch gleich die frequenz.

Ich könnte mir vorstellen, dass die status variable für das abschalten des displays vielleicht nicht als volatile definiert war.

Hallo,
ich nutze folgenden Abschaltmechanismus:
zu Beginn führe ich eine Variable zum Zeitspeichern ein:

unsigned long timestore;  // Variable Speicher für Systemzeit.

nach jedem Oled.print (oder sonsiger Display ausgabe) wird die aktuelle Zeit gespeichert

  timestore = millis(); //Die aktuelle Systemzeit wird in den TimeStore gebracht

Ganz am Ende in der voidloop prüfe ich, ob seit dem letzten Speichern (also auch Anzeigen auf dem Display) schon X millisekunden vergangen sind. Falls ja, cleare ich das display.

  void loop() {
  
    
  do {

//Display
  if (millis() - timestore> 3000) // ...wenn die aktuelle Zeit minus die Gespeicherte Zeit > 3 sek sind...
    {
     oled.clear();  // wird display gecleared
    }
  //Display Ende

Ich hoffe das war halbwegs versätndlich.

Ja, das war vermutlich mein Fehler.