Durch einen Interrupt aufgerufenes Void wird nicht ordentlich abgearbeitet

Moin allerseits,
ich versuche gerade mit interrupts klar zu kommen und in diesem Zusammenhang brauche ich auch anderen voids (neben void setup und void loop).

In Void loop soll ein Programm durchlaufen. Es soll jedoch jederzeit über einen Taster mit einem Interrupt auf DI 2 (Arduino UNO) unterbrochen werden können. Der Befehl

> attachInterrupt(digitalPinToInterrupt(0), interrupt, CHANGE)

erzeugt an DI 2 den Interrupt. Wenn sich also DI2 zu HIGH oder LOW ändert, wird void interrupt aufgerufen. So weit funktioniert alles wie es soll. Das Problem ist, dass der Code in void interrupt nicht ordentlich von oben nach unten durchlaufen wird sondern eher alles auf einmal. In dem void befinden sich z.b. mehrmals delay(500), wodurch eine LED blinken soll. Das wird völlig ignoriert. Auch im seriellen Monitor erscheint der Text, der sowohl zu beginn des voids als auch am Ende steht, zeitgleich.
Woran liegt das?

Während der Fehlersuche habe ich folgenden Beispielcode gefunden. Ich habe den Eindruck, ich stoße mit ihm auf den gleichen Fehler wie oben beschrieben. Und weil mein eigener Code ein ziemliches Wrack ist, glaube ich, dass sich dieser sehr kompakte Code besser eignet mein Problem zu finden und zu erklären:

// https://funduino.de/25-der-interrupt-befehl-attachinterrupt

const int Taster = 2;          // Der Taster wurde an Pin 2 angeschlossen
const int LED =  13;           // Die LED wurde an Pin 13 angeschlossen


volatile int TasterStatus = 0;         // Variable die den Status des Taster ausliest, wird der sog. "Vektor" 0 zugewiesen
                                      
                                     
                                       // Taster Anschluss an Pin 2 entspricht dem Vektor 0   (hier der Fall)
                                       // Taster Anschluss an Pin 3 entspricht dem Vektor 1

  

void setup() {

  
          pinMode(LED, OUTPUT);         // Hier wird die LED als OUTPUT definiert
          
          
          pinMode(Taster, INPUT);       // Hier wird der Taster als INPUT definiert

  
          attachInterrupt(0, TasterUnterbricht, CHANGE);   // Hier findet die Einbindung unseres Interrupt-Befehls statt
             Serial.begin(9600);
             Serial.println("ende void setup");
                                                         

          
                                             
        }


void loop() {
   Serial.println("void loop");                                        // Hier leer, der eigentliche loop() Teil findet nun in der "void TasterUnterbricht()" statt
}


void TasterUnterbricht() {                 // Sobald die Unterbrechung "TasterUnterbricht" (der Wert am Pin ändert sich [CHANGE])...
  //Serial.println("void TasterUnterbricht");
  TasterStatus = digitalRead(Taster);      // ... wird der TasterStatus neu definiert ("volatile"). Die neue Definition geschieht durch das Auslesen des Tasters an Pin 2. ...
  digitalWrite(LED, TasterStatus);         // ... [digitalWrite(pin,Status)] Nun wird die LED (Pin13) in den zuvor definierten Tasterstatus versetzt.
}

Wenn ich den Code von der oben verlinkten Webseite kopiere funktioniert er. Die LED leuchtet und wenn ich den Taster drücke geht sie aus.

Um den Code besser zu verstehen (wann was abgearbeitet wird) habe ich begonnen Serial.print("...") - Befehle einzufügen. Und dabei stelle ich fest, die (jetzt auskommentierte) Zeile 40 mit //Serial.println("void TasterUnterbricht");
macht alles kaputt. Die LED blinkt auf einmal wie blöd und im Seriellen Monitor rauscht die Zeile "void TasterUnterbricht" mit einigen Unterbrechungen von "void loop" nur so durch.

Woran liegt das? Warum wird nicht wie bisher void TasterUnterbricht ordentlich abgearbeitet? Es muss doch möglich sein, dort ein paar Zeilen Code zu ergänzen?

Serial Print, kann blockieren und hat darum nichts in ISR zu suchen.

Dein Taster prellt!
Und Glückwunsch, du hast das prellen sichtbar gemacht.

Übrigens:
Void ist ein Datentype, es steht für "nicht vorhanden", oder "unbestimmt".
Du meinst Funktion!

Also so:
Warum wird nicht wie bisher, die Funktion TasterUnterbricht ordentlich abgearbeitet?
Dann versteht man das auch!

Für Taster verwendet man generell sehr selten Interrupts. Interrupts sind für Dinge auf die man sehr schnell reagieren muss oder die sehr regelmäßig erledigt werden müssen. Taster dagegen sind extrem langsam.

Du müllst in loop() ständig den seriellen Ausgabe-Puffer zu. Viel schneller als du das je Senden könntest. Bei 9600 Baud braucht ein Zeichen etwa 1ms. Dann machst du im Interrupt nochmal eine serielle Ausgabe. Das blockiert zwangsläufig.

Für das Entprellen gibt es fertige Bibliotheken die das für dich erledigen können wenn es nicht selbst tun möchtest. Es schadet aber nichts das einmal per Hand zu machen um das Problem zu verstehen.

Und es gibt keine "voids". Die Dinger heißen Funktionen. Void ist ein Datentyp. Das ist der Rückgabewert der Funktion. In diesem Fall "Leer" oder "Nichts". Funktionen können aber auch Werte zurück liefern. z.B. "int summe(int a, int b)"

1 Like

danke so weit für die Antwort.
Ich habe nun in meinem eigenen Code alle Serial.prints entfernt.
Leider funktioniert er noch immer nicht.

Das Programm soll durchgehend LEDs blinken lassen. Dieser Teil steht bei mir in void loop. Es soll jedoch möglich sein, jederzeit einen Taster zu drücken und dadurch sofort in einen alternativen Programmteil zu wechseln. Dieser soll - wie man es kennt - von oben nach unten abgearbeitet werden und anschließend geht es wieder mit dem Code in void loop weiter.

Kannst du bitte kurz umreißen, wie ich das Programm aufbauen muss? Bisher steht der Code, der durch den Taster per Interrupt ausgelöst wird, in der Funktion (void), auf die in attachtInterrupt verwießen wird. Eben dieser Code wird aber nicht richtig abgearbeitet (z.b. werden jegliche delays ignoriert).

Interruptfunktionen müssen möglichst kurz sein, und vor allem dürfen sie nicht blockieren. Andere Funktionen, die selbst auf Interrupts angewiesen sind, sollen/dürfen in ISR's überhaupt nicht genutzt werden. Da gehört z.B. delay und Serial.print dazu. Sie funktionieren entweder nicht, oder können das ganze System zum Einfrieren bringen.

Überhaupt sind Interruptfunktionen eher nichts für Einsteiger. Wenn man sich da nicht wirklich im Detail auskennt - und da gibt es viele Details - sollte man da die Finger von lassen. Sie machen mehr Probleme als sie lösen.
Zur Auswertung eines Schalters braucht man gar keinen Interrupt.
Viel wichtiger als sich mit Interrutps zu beschäftigen, ist es, sich mit blockadefreier Programmierung zu beschäftigen - also z.B. die delay() loszuwerden ( IDE-Beispiel blinkWithoutDelay ) Dann ergibt sich die Lösung für dein Problem quasi von selbst :wink:

Suche nach "Taster entprellen", bzw. "Debounce" auf Englisch

Auch vielleicht mal kurz lesen:

Das ist innerhalb von Interrupt-Functionen normal. Darin darf man delay() nicht verwenden.

Eigentlich nicht.
delay() werden nicht ignoriert.
Allerdings ist das Versagen schon mit eingebaut, wenn man delay() in ISR verwendet.

Das Arduino delay() benötigt selber Interrupts
In Interrupts sind Interrupts gesperrt.
Darum funktioniert delay() nicht in ISR.

Edit, dieses ist falsch:
--> Die ISR wird nie beendet, das Programm steht.

Doch, mit einem AVR-Arduino. Mal versuchen!

1 Like

Hallo,
wozu dann Interrupt ?
Wenn Du mit dem Button nur einen Ausgang schalten willst muss Du den Taster doch nicht entprellen. Da gibts doch ein Beispiel in der Ide. Für das Blinken schaust Du Dir das Beispiel BlinkWithoutDelay an , und bringst beides zusammen.

Ich danke dir für den Anstoß!

Versuch durchgeführt.
Falsch: delay() in Interrupts wird ignoriert.
Richtig: Es funktioniert nicht wie es soll
Zudem blockiert es so andere Interrupts, z.B. Serial.

Testcode:

const byte pin = 2;

void setup() 
{
  Serial.begin(9600);
  pinMode(LED_BUILTIN,OUTPUT);
  pinMode(pin,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), interrupt, CHANGE);
}

void loop() 
{
  Serial.println(millis());
}

void interrupt()
{
  delay(1000000);
  digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
}

Es hat ein Zeitchen gedauert, bis ich darauf gekommen bin, warum delay() so falsch läuft und doch nicht das Programm total blockiert!

Ganz einfach: micros() springt rückwärts, wenn die Interrupts abgeschaltet sind.
micros() stagniert in einem schmalen (255 Timer Takte) Bereich.
Dann versagt natürlich der "Nachtwächter" in dem delay()

Einer der zentralen Puntke:

start += 1000;

Noch mal Danke, für den Anstoß.


Resultat, mit weniger Worten:

Delay() werden nicht ignoriert!
Die sind dann kaputt, werden aber nicht ignoriert.

1 Like

Danke für die Einsicht!

Einsicht?
Das kann ich nicht!
Immerhin gibt es einen deutlichen Unterschied zwischen "ignorieren" und "funktioniert nicht".


Nein!
Den Beweis/Testcode habe ich mitgeliefert!
delay() wird nicht ignoriert.

Das ist richtig, aber nicht weil delay() ignoriert wird, sondern, weil es nicht funktioniert. Es tut nicht, was man erwartet.

Wenn Einsicht, dann nur an dieser Stelle:

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