Fünf Interrupts beim ESP32 Wroom32 ohne serielle Ausgabe. Beispiel Stoppuhr

Es soll ein Übungsbeispiel mit mehreren Hardware-Interrupts entstehen, eine Stoppuhr mit 5 Sensoren (erstmal mit Tastern simuliert), die jeweils einen Interrupt auslösen. Ziel ist, bei hohen Geschwindigkeiten einen hinreichend weit entfernten Sensor als Stoppzeitgeber auszuwerten.

Da der Uno nur 2 Interrupts anbietet, fiel die Wahl auf einen ESP32 Wroom32 von AZDelivery, bei dem theoretisch jeder GPIO für einen Hardwareinterrupt genutzt werden kann. Praktisch wohl nicht ohne weiteres, wegen anderweitiger boardseitiger Nutzung.

Das aktuelle Problem besteht derzeit darin, dass die serielle Ausgabe bei allen angebotenen und ausprobierten Baudraten den ersten String, wenn überhaupt, verkrüppelt herausbringt. Weitere serielle Ausgaben erfolgen nicht. Allerdings scheint etwas zu funktionieren: Die interne LED reagiert wie erwartet. Sie wird beim Betätigen des ersten Knopfes eingeschaltet und kann mit einem anderen ausgeschaltet werden.
Was kann die Ursache sein, dass die serielle Ausgabe während des Loops nichts ausgibt? Vertragen sich die Interrupts mit der seriellen Ausgabe vielleicht nicht? Oder ist das Board-spezifisch? Oder sollte ein anderes Board in der IDE eingestellt werden?
Verwendet wird die Arduino IDE 2.0.3., eingestelltes Board: ESP32 Dev Module.

Hier der Sketch (bei dem es erstmal nur um die Funktion an sich geht):

// Zeit messen mit 5 Tastern (Simulation mit 5 Gebern, z.B. Lichtschranken)
// ******
// Ziel ist es, in Abhängigkeit von der Geschwindigkeit, die Messung mit dem Taster zu beenden, der hinreichend weit genug vom Einschalttaster entfernt ist.
// Je höher die Geschwindigkeit, desto größer sollte der Messweg für einen brauchbaren Messwert sein.

// Board: ESP32 WROOM32 (AZ-Delivery)

// Struktur anlegen, die die Eigenschaften der Signaleingänge enthält
struct Button {
  const uint8_t PIN;  // Pin am Board
  bool aktiv;         // Zustandsflag des Eingangs
  long t;             // Zeitstempel
};
// Struktur vorbelegen
Button button1 = { 15, false, 0 };
Button button2 = { 2, false, 0 };
Button button3 = { 0, false, 0 };
Button button4 = { 4, false, 0 };
Button button5 = { 5, false, 0 };

// Messtimervariablen
bool messtimerFlag = false;  // Flag ist HIGH während der Messung
bool ausgabeFlag = false;    // Flag ist HIGH, wenn etwas auszugeben ist
long merk = millis();        // zum Merken der aktullen Zeit

// Variablen für Berechnungen
long messstart = 0;  // Messtart
long messende = 0;   // Messende
long messdauer = 0;  // Messdauer
int stoptaster = 0;  // Zustandsvariable, damit in der Ausgabe angegeben werden kann, welcher Eingang die Messung beendet hat

// interne LED
const uint8_t ledPin = 1;  // Ausgabe-PIN der internen LED des Boards

void setup() {
  Serial.begin(115200);  // Serielle Ausgabe starten
  Serial.println("Zeitmesser");

  // Interrupt-Pins initialisieren
  pinMode(button1.PIN, INPUT_PULLUP);
  pinMode(button2.PIN, INPUT_PULLUP);
  pinMode(button3.PIN, INPUT_PULLUP);
  pinMode(button4.PIN, INPUT_PULLUP);
  pinMode(button5.PIN, INPUT_PULLUP);
  // Interrupts initialisieren
  attachInterrupt(button1.PIN, isr1, FALLING);
  attachInterrupt(button2.PIN, isr2, FALLING);
  attachInterrupt(button3.PIN, isr3, FALLING);
  attachInterrupt(button4.PIN, isr4, FALLING);
  attachInterrupt(button5.PIN, isr5, FALLING);

  // Indikator-LED initialisieren
  pinMode(ledPin, OUTPUT);     // Pin auf Ausgabe legen
  digitalWrite(ledPin, HIGH);  // Led AUS
}

void loop() {

  if (button1.aktiv && (!messtimerFlag)) {    // Wenn 1. Eingang und keine Messung im Gang, dann "Messung starten"
    messtimerFlag = true;                     // Messtimerflag setzen
    ausgabeFlag = false;                      // Berechnung und Ausgabe unterdrücken
    Serial.println("1. Eingang ausgelöst ");  // Monitoring
    digitalWrite(ledPin, LOW);                // Monitoring: LED ein
    button1.aktiv = false;                    // Eingang 1 freigeben
    // delay(100);
  }
  if (button2.aktiv && (messtimerFlag)) {   // Wenn 2. Eingang und Messung aktiv, dann prüfen, ob Messung zu beenden ist
    if ((button2.t - button1.t > 10000)) {  // Wenn Zeit überschritten, Messung beenden
      messdauer = button2.t - button1.t;    // Berechnung
      messtimerFlag = false;                // Ende der Messung setzen
      ausgabeFlag = true;                   // Ausgabe setzen
      stoptaster = 2;                       // Monitoring
      digitalWrite(ledPin, HIGH);           // Monitoring: LED aus
    }
    button2.aktiv = false;  // Eingang 2 "freigeben"
  }
  if (button3.aktiv && (messtimerFlag)) {                                     // Wenn 3. Eingang und Messung aktiv, dann prüfen, ob Messung zu beenden ist
    if ((button3.t - button1.t < 10000) && (button3.t - button1.t > 2500)) {  // Wenn Zeitfenster überschritten, Messung beenden
      messdauer = button3.t - button1.t;                                      // Berechnung
      messtimerFlag = false;                                                  // Ende der Messung setzen
      ausgabeFlag = true;                                                     // Ausgabe setzen
      digitalWrite(ledPin, HIGH);                                             // Monitoring: LED aus
      stoptaster = 3;                                                         // Monitoring
    }
    button3.aktiv = false;  // Eingang 3 "freigeben"
  }
  if (button4.aktiv && (messtimerFlag)) {                                    // Wenn 4. Eingang und Messung aktiv, dann prüfen, ob Messung zu beenden ist
    if ((button4.t - button1.t < 2500) && (button2.t - button1.t > 1250)) {  // Wenn Zeitfenster überschritten, Messung beenden
      messdauer = button4.t - button1.t;                                     // Berechnung
      messtimerFlag = false;                                                 // Ende der Messung setzen
      ausgabeFlag = true;                                                    // Ausgabe setzen
      digitalWrite(ledPin, HIGH);                                            // Monitoring: LED aus
      stoptaster = 4;                                                        // Monitoring
    }
    button4.aktiv = false;  // Eingang 4 "freigeben"
  }
  if (button5.aktiv && (messtimerFlag)) {  // Wenn 5. Eingang und Messung aktiv, dann Messung beenden
    messdauer = button5.t - button1.t;     // Berechnung
    messtimerFlag = false;                 // Ende der Messung setzen
    ausgabeFlag = true;                    // Ausgabe setzen
    digitalWrite(ledPin, HIGH);            // Monitoring: LED aus
    stoptaster = 5;                        // Monitoring
    button5.aktiv = false;                 // Eingang 5 freigeben
  }

  // Ausgabe
  if (ausgabeFlag) {
    ausgabeFlag = false;  // Ausgabeflag löschen
    Serial.print("Stop-Taste: ");
    Serial.print(String(stoptaster));
    Serial.print(", Messdauer: ");
    Serial.println(String(messdauer));
    delay(3000);  // 3 Sekunden anhalten
  }
}

// ISR
void ICACHE_RAM_ATTR isr1() {
  long zeitstempel = millis();
  if (zeitstempel - button1.t > 25) {  // Entprellen: Der letzte Aufruf der ISR muss älter als 25 ms sein
    button1.aktiv = true;              // Zustandsflag von Eingang 1 setzen
    button1.t = zeitstempel;           // Zeitstempel wird gespeichert
  }
}
void ICACHE_RAM_ATTR isr2() {
  long zeitstempel = millis();
  if (zeitstempel - button2.t > 25) {  // Entprellen: Der letzte Aufruf der ISR muss älter als 25 ms sein
    button2.aktiv = true;              // Zustandsflag von Eingang 2 setzen
    button2.t = zeitstempel;           // Zeitstempel wird gespeichert
  }
}
void ICACHE_RAM_ATTR isr3() {
  long zeitstempel = millis();
  if (zeitstempel - button3.t > 25) {  // Entprellen: Der letzte Aufruf der ISR muss älter als 25 ms sein
    button3.aktiv = true;              // Zustandsflag von Eingang 2 setzen
    button3.t = zeitstempel;           // Zeitstempel wird gespeichert
  }
}
void ICACHE_RAM_ATTR isr4() {
  long zeitstempel = millis();
  if (zeitstempel - button4.t > 25) {  // Entprellen: Der letzte Aufruf der ISR muss älter als 25 ms sein
    button4.aktiv = true;              // Zustandsflag von Eingang 2 setzen
    button4.t = zeitstempel;           // Zeitstempel wird gespeichert
  }
}
void ICACHE_RAM_ATTR isr5() {
  long zeitstempel = millis();
  if (zeitstempel - button5.t > 25) {  // Entprellen: Der letzte Aufruf der ISR muss älter als 25 ms sein
    button5.aktiv = true;              // Zustandsflag von Eingang 2 setzen
    button5.t = zeitstempel;           // Zeitstempel wird gespeichert
  }
}

Deutlich mehr!
Tipp: PCINT

Wobei ich mir nicht vorstellen kann, dass hier Interrupts nötig sind.
Gerade auf einem ESP32 mit zwei Kernen und doch sicherlich 80MHz.

Selbst auf einem UNO sollte das kein Problem sein.

Pin 1 ist TX, da hängt keine LED dran.

Unglückliche Pinwahl.

Siehe ESP32 Pinout Reference: Which GPIO pins should you use?

Wenn ich die Pins ändere, tut das Programm etwas :slightly_smiling_face:

das kann man so sagen. in einer ISR hat ein Serial.print nichts zu suchen.

ISR sind für deine Anwendung nicht notwendig.

Da du schon eine Struktur hast, bräuchtest du nur deine Lichtschrankenauswertung als Memberfunktionen der Struktur definieren, die 5 button als Array anlegen und dann kürzt sich dein Code auf ein 5tel runter.

Danke für Deinen Post!

  1. Im steht AZDelivery-Dokument "ESP32 Development Board_V2.pdf" steht in Zusammenhang mit dem Beispielsketch: "Die boardinterne LED des AZ-Delivery ESP32 Development Boards liegt an Pin 1." Und das Beispiel funktioniert. Ist aber wohl nur für den Beispielsketch verwendbar, denn:
  2. Habe auf Deinen Hinweis hin, die LED-Zeilen auskommentiert. Nun scheint die serielle Ausgabe zu funktionieren. Also kollidiert die Ausgabe an die "interne LED" mit TX und der seriellen Ausgabe.

Hatte zwar anhand von Pinouts und Infos aus dem Web die Wahl mit Bedacht gemacht. War aber nicht gut genug. Danke für den Link, der aber auch nur teilweise zum Erfolg geführt hat.
Letztlich durch Probieren konnte ich die Pins 33, 25, 27, 14 und 12 als funktionierend feststellen. 34, 35, 32 und 26 hätten wohl auch funktionieren müssen. Habe ich auch ausprobiert, was aber erfolglos blieb. (Kann man das erklären?)

Mit diesen beiden Änderungen läuft es nun. Danke für Deine konstruktiven Hinweise!

Danke für Deinen Post.

Hardwareinterrupts, um die es in meinem BEISPIEL-Sketch geht, gibts für den UNO nur zwei (https://www.arduino.cc/reference/de/language/functions/external-interrupts/attachinterrupt/).

Danke für Deinen Post.

Stimmt. Deswegen stehts auch im LOOP und nicht in den ISR.

Da liegst Du leider nicht richtig. Es geht hier um ein BEISPIEL, wie mehrere Hardwareinterrupts zusammenarbeiten und insbesondere darum, dabei die serielle Ausgabe zum Laufen zu bringen.
Das ist ja Dank @agmue geklärt.

:slightly_smiling_face:

Ich mag es kaum glauben!

Bitte zeige mal einen Link zur Hardware.

Wenn es so ist, könnte Dein Board speziell sein und es zu Problemen mit Beispielen aus dem Netz kommen. Programme von mir wären bei Dir vermutlich nicht lauffähig. Eventuell solltest Du diesem Punkt etwas Zeit widmen. Ich würde es tun, denn Übungsbeispiele mit exotischer oder unklarer Hardware finde ich nicht so zielführend.

Ich verwende ESP32-Core 2.0.6 und Arduino-IDE 1.8.19 "ESP32 Dev Module". Meine Pinbelegung mit GPIO1 == TXD 0:


:thinking:

Ist das nicht Hardware abhängig, ist doch ein ESP32

Nach Deinem Beispiel nutzt Du die Interrupts doch lediglich dazu, die Taster zu entprellen. Mag sein, dass Du das extra beispielhaft so machst. Ist auch Ok.

Dennoch hat @noiasca damit recht, dass man dafür keine Interrupts braucht und deine Konstruktion eigentlich überflüssig ist.

Und beim Atmega 328p kann man, zusätzlich zu den zwei Hardwareinterrupts, auch die Pin Change Interrupts nutzten:

In addition to our two (2) external interrupts, twenty-three (23) pins can be programmed to trigger an interrupt if there pin changes state.

https://www.arxterra.com/11-atmega328p-external-interrupts/

Die Pins 34, 35, 36 und 39 sin nur Input Pins ohne PullUp, und Down, dazu haben die Sonderwünsche, mach dich mal.l schlau

Das ist nicht gewünscht!
Warum ist mir auch egal....
(ich bin sowieso zu blöd für diese Welt)

Immerhin kann ihm jetzt seine gute Note abholen.

Wohl kaum. Du setzt wahrscheinlich nur zu viel voraus. Und deine, zuweilen einsilbigen, Kommentare versteht so mancher wohl nicht als Stichwort zur selbständigen Informationsbeschaffung, sondern ist eher abgeschreckt, weil der Kontext fehlt…

Naja, jede Jeck is anders…

2 Likes

Hmmm....
Der Kontext wurde nicht von mir vorgegeben oder vorausgesetzt.
Der kam vom TO: Ein Arduino UNO hat nur 2 Interrupts.

Soll ich das wirklich nachplappern?
bzw. vorbeten?

Ja, nee...
Ich muss davon ausgehen, dass ihn PCINT nicht interessiert, und wir nicht erfahren dürfen:

  1. Wozu überhaupt Interrupts
  2. Was an PCINT falsch ist.

Aber alles gut.
Mein Leben geht auch ohne diese Information weiter.

Gern: https://www.az-delivery.de/en/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/das-zehnte-tuerchen
Das Pinout-Bild wird von hier geladen: https://drive.google.com/file/d/1do7tb6tHbTQNlPX8fVwQQnwxOe8gZbed/view?usp=sharing
Da ist die LED an GPIO01 mit Verbindung zu TX sogar extra gekennzeichnet.

Ja sorry, Du bist rehabilitiert!

Eigentlich bin ich ja vorsichtig, möchte diese Konstellation aber, solange mir niemand das Gegenteil beweist, als Fehlkonstruktion von AZ-Delivery werten.

Du weißt es nun und kannst Fehler vermeiden, ich habe was gelernt, Danke für die Info :slightly_smiling_face:

Du könntest das Thema als gelöst markieren, wenn es für Dich gelöst ist.

Ich finde, das hast Du gut auf den Punkt gebracht.

Mach' ich.

Danke!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.