EEProm auslesen falls Interrupt ausgelöst - Hilfestellung?!

Hallo allerseits,

ich möchte für ein Projekt eine Variable per Hardwareinterrupt (Taster) heraufzählen und, wenn diese einmal heraufgezählt ist, anhand dieser das EEProm auslesen.

EEProm auslesen sowie Interrupt und Variable funktionieren auch jeder für sich.
Im ganzen Code kommt es, wenn die if- Abfrage für's EEProm-auslesen NICHT auskommentiert ist, nur zur darstellung der erste Buchstaben der seriellen Ausgabe, mit der ich die Abarbeitung des COde nachverfolgen wollte.

Sieht wer, wo hier der Fehler liegt?

Ich würde mich über Lösungshinweise wirklich freuen. Die EEProm- Anweisung sind ja recht übersichtlich, ich finde den Fehler nicht.

Gruß,
Moritz

#include <EEPROM.h>                                                   // EEprom- Bibliothek zum Speichern der Eingaben

const int ledPin = 13;
const int hallPin = 2;
const int wahlPin = 3;

//---------------------------- Variablen für das Eingabe- Interface deklarieren & initialisieren
char wort[9];                                                           // Länge vom Wort (char- Array) muss angegeben werden, auch wenn nicht voll ausgenutzt. Willkürlich gewählt: 10. Eher 9, da erstes Byte die Länge beschreibt.
byte laenge;
byte stelle = 0;
bool lesen = LOW;

void setup() {
  Serial.begin(9600);                                                     // seriellen Port aktivieren
  pinMode(wahlPin, INPUT);                                                // initialisiert wahlPin als Input. Vllt. egal, da interrupt- Pin eh ein Input ist?
  pinMode(wahlPin, INPUT_PULLUP);                                         // aktiviert Pullup- Widerstand am wahlPin. Nicht nötig, wenn hardwareseitig Pullup eingebaut
  attachInterrupt(digitalPinToInterrupt(3), wahl, FALLING);// CHANGE                             // Interrupt, wenn Taster (active- low) an INT1 ≙ Pin3 betätigt
}

void loop() {
  /*  if (lesen == HIGH) {                                                    // falls Interrupt "wahl" ausgelöst, d.h. ausgewählten Speicherplatz einen weiter gedrückt
      Serial.println(laenge);
      laenge = byte(EEPROM.read(stelle));                                   // EEProm lesen an erster Adresse (≙ "stelle"), dies ist "laenge". Folgende 9 Stellen sind zugehöriges "wort".
      if (laenge != 0) {                                                    // Wenn "stelle" nicht belegt (≙ 0), gibt es kein "laenge", dann auch kein "wort".
        for (int i = stelle; i < (stelle + laenge + 1); i++) {              // fängt beim Hochzählen bei ausgewählter Stelle an, zählt bis ausgewählte Stelle plus "laenge", d.h. das ganze gespeicherte Wort
          wort[(i - stelle)] = char(EEPROM.read(i + 1));                    // wenn EEProm(stelle) NICHT Null (≙ es IST etwas vorhanden, es GIBT ein "laenge"): Lese ab Stelle "stelle+1" (an voriger Stelle liegt ja "laenge"!) bis zu "stelle+laenge+1", dass muss dann "wort" sein.
        }
      }
      else {                                                                // Fehlermeldung, falls gewählte Speicherstelle nicht belegt
        wort[1] = 'L';
        wort[2] = 'E';
        wort[3] = 'E';
        wort[4] = 'R';
        for (int i = 4; i < laenge; i++) {                                    // Rest von evtl. noch vorhandenen Buchstaben überschreiben
          wort[i] = '_';
        }
      }
      lesen = LOW;                                                          // Lesen abgeschlossen
    }*/
}


void wahl() {                                                             // Interrupt Serive Routine: Stelle zum EEProm- Lesen auswählen
  if (stelle < 50) stelle += 10;                                          // max. fünf versch. Speicherplätze á ein Byte für Länge und 9 Characters (insges. 50 Speicherstellen). Bei Tastendruck springe 10 Stellen im EEProm weiter
  else stelle = 0;                                                        // zurückspringen, falls weiter als fünf Speicherplätze durchgeklickt
  lesen = HIGH;                                                           // setzt Variable "lesen", damit im Loop EEProm eingelesen wird

  Serial.print("stelle: ");                                               // for debugging purposes, zur Not auskommentieren: Angabe über errechnete Werte
  Serial.println(stelle);
  Serial.print("Zustand von 'lesen': ");                                               // for debugging purposes, zur Not auskommentieren: Angabe über errechnete Werte
  Serial.println(lesen);
}

Serial geht in Interrupts nicht!

Das musst du außerhalb machen:

volatile bool interruptTriggered;

void loop()
{
   if(interruptTriggered)
   {
      interruptTriggered = false;
      
      Serial.print(....);
   }
}

void wahl()
{
   ...

   interruptTriggered = true;
}

Variablen die innerhalb und außerhalb von ISRs verwendet werden müssen dann als volatile deklariert werden

Serielle Ausgaben in einer ISR?
Nein!

"volatile" fehlt da.

Taster prellen.
Auslesen per ISR ist da eher nicht sinnvoll.

Ja, Taster per Interrupt auslesen ist generell nicht sinnvoll. Wieso nicht einfach Pollen und dann gleich Entprellen?

Moritz_uno:
Sieht wer, wo hier der Fehler liegt?

Serial.print ist keine interruptsichere Funktion.

Wenn Du innerhalb einer Interruptbehandlung mit Serial.print den seriellen Ausgangspuffer zum Überlaufen bringst, tritt ein Dead-Lock in Deiner Software auf und das System wartet für immer auf irgendwas, das nicht passieren wird: Die Interruptroutine wartet darauf, dass im seriellen Ausgangspuffer Platz entsteht, weil Zeichen aus dem Puffer gesendet werden, aber es werden keine Zeichen aus dem Puffer gesendet, weil die aktive Interruptroutine das (interruptgesteuerte) Senden von Zeichen aus dem seriellen Ausgangspuffer blockiert.

Wahrscheinlich tritt durch Kontaktprellen ein so häufiger Aufruf des Interrupts auf, dass mehr als 63 Zeichen in kürzester Zeit zu senden wären, was den Puffer überlaufen läßt.

Aber der Fehler liegt natürlich allgemein bereits daran, dass Du schlurflangsame Signale wie das Drücken eines mechanischen Tasters mit einem Interrupt auswerten möchtest. Das macht man ja auch nicht. Langsame Signale werden gepollt. Üblicherweise.

Hi!

Ich danke, das hat die Lage verbessert! Natürlich ist hierbei Pollen in der loop auch machbar. Ich will keinen Tastendruck verpassen, und in der loop sind delays, daher der Gedanke mit dem Interrupt.
Das "Serial.print" ist nichts funktional wichtiges, ich wollte nur den Ablauf damit nachvollziehen können.

Der Taster ist mit einem 100nF Kerko entprellt.
In die Debounce- Library habe ich schonmal reingeschaut, aber die ist doch sehr mächtig. Gibt es außer delay() noch einen simplen Weg, einen Tatser softwareseitig zu entprellen?

Serial.print ist keine interruptsichere Funktion

  • Woher weiß man das? Ich habe die Arduino Reference hierzu konsultiert, da bin ich über nichts zur Interruptsicherheit von Serial.print gestolpert.

Aber vielen Dank, das hilft schon mal.
Gruß,
Moritz

Moritz_uno:
Ich will keinen Tastendruck verpassen, und in der loop sind delays, daher der Gedanke mit dem Interrupt.

Aha, und wenn Du gleich zwei grundlegende Fehler in einem Programm machst, und zwar:

  1. in einem interaktiven Programm die Programmausführung mit "delay()" blockieren
    und
  2. ein langsames und prellendes Signal auf aberwitzig komplizierte Art mit Interrupts auswerten
    soll dabei irgendwas vernünftiges herauskommen?

Zwei dicke Fehler = 1 richtiges Programm?

Und wenn's dann nicht funktioniert, baut man noch ein, zwei, drei Fehler ein, bis es funktioniert?

Das klingt für micht leider nicht nach einem Plan, der funktionieren kann.

Grundregel Nummer eins: In einem interaktiven Programm, in dem auf Knopfdruck irgendwas passieren soll, ist die Verwendung von "delay()" (= Programmausführung für eine vorgegebene Zeit anhalten) absolut verboten.

Grundregel Nummer zwei: Mikrocontroller sind sehr schnell (16 MHz Systemtakt) gegenüber einem Menschen, der eine Taste drückt. Der Mensch schafft wohl nicht mal 10 Tastendrücke pro Sekunde. Daher ist das Pollen von Tastendrücken das Mittel der Wahl: Es ist programmtechnisch sehr einfach umsetzbar (ein Bruchteil des Aufwands gegenüber Interruptprogrammierung) und es ist immer ausreichend schnell bei manuell bedienten mechanischen Tastern und Schaltern.

Woher weiß man das? Ich habe die Arduino Reference hierzu konsultiert, da bin ich über nichts zur Interruptsicherheit von Serial.print gestolpert.

Ich weiß es, weil ich mir angesehen habe, was es tut, und wie es das tut.
Der Quellcode ist kein Geheimnis und liegt auch dir vor.

Moritz_uno:
Ich habe die Arduino Reference hierzu konsultiert, da bin ich über nichts zur Interruptsicherheit von Serial.print gestolpert.

Generally, an ISR should be as short and fast as possible. [...] will start behaving erratically after 1-2 ms [...] For more information on interrupts, see Nick Gammon's notes.

Und dort:

When writing an Interrupt Service Routine (ISR):
Keep it short
Don't use delay ()
Don't do serial prints
...

Zehn Zeilen weiter als klassisches Beispiel, wofür ISR nicht gedacht sind: Tasten auswerten.

Wenn man nicht weiss, was genau passiert, sollte man sich an die Grundregel halten: keine fremde Funktion in einer ISR aufrufen.

Und das Dumme an Serial.print ist: Wenn man nur ganz wenige Zeichen selten genug ausgibt, geht es leider gut :wink: