Erste erfahrungen mit Interrrupt

Grüße,

Ich habe ja schon öfters von den Interrupts gehört und was man damit alles tolles machen kann. Selber habe ich sie noch nie gebraucht... bis jetzt. Vielleicht auch nicht, wenn jemand eine bessere Lösung hat bin ich nicht traurig.

Was ich vorhabe.

Ich möchte über den Void Loop Sensordaten abfragen.
Einen Tageslichtsensor
Einen Feuchtesensor
Einen Durchflusssensor
Im Anschluss soll aus den Daten des Durchflusssensors der Gesamtverbrauch in Litern ermittelt werden. Das ganze soll ohne Delay oder mit einem sehr geringem Delay Funktionieren.

Außerdem möchte ich alle 15 min die aktuellen Sensordaten, sowie den verbrauch Exportieren.

Nun habe ich gedacht, man könnte ja ein Interrupt verwenden um alle 15 min eine "void Export" Funktion zu zünden.
Nun habe ich mir den "attachInterrupt" Befehl angeguckt und folgendes gefunden

* **LOW** Interrupt wird getriggert, wenn der Pin LOW ist,
* **CHANGE** Interrupt wird getriggert, wenn der Pin den Wert ändert
* **RISING** Interrupt wird getriggert, wenn der Pin von LOW auf HIGH wechselt,
* **FALLING** Interrupt wird getriggert, wenn der Pin von HIGH auf LOW wechselt.
Die Due-, Zero- und MKR1000-Boards erlauben zusätzlich:
* **HIGH** Interrupt wird getriggert, wenn der Pin HIGH ist.

Ein Zeitlich Bedingtes Triggern ist hier scheinbar nicht vorgesehen.
Nun die Frage:
Geht das Trotzdem?
Geht das anders besser?
Geht das garnicht?

Da braucht man keinen Interrupt. Siehe Beispiel "Blink without Delay".

1 Like

Du kannst auch einen Timer verwenden, der exact dafür gebaut ist.
Einen Interrupt brauchst du nicht dafü.

ich sag mal, es geht anders genauso.
"Blink Without Delay" - ist dein Freund.
Schau dir das mal aus den Beispielen an.

Etwas vereinfacht kannst du damit allerlei "Timer" selber einbauen.
Hier z.B. zwei Einfache Funktionen:

void doSomething() {
  static uint32_t previousMillis = 0;
  if (millis() - previousMillis > 5000) {
    previousMillis = millis();
    Serial.println(F("tu Was"));
  }
}

void blink() {
  static uint32_t previousMillis = 0;
  if (millis() - previousMillis > 500) {
    previousMillis = millis();
    if (digitalRead(13) == LOW) digitalWrite(13, HIGH); else digitalWrite(13, LOW);
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(13, OUTPUT);
}

void loop() {
  doSomething();
  blink();
}

ist immer das gleiche Muster.

  1. Du brauchst eine Variable die einen Zeitstempel halten kann
  2. Du brauchst eine Bedingung die den Ablauf der Zeit überprüft
  3. Du aktualsierst den gemerkten Zeitstempel wenn du was durchführst.

Viel mehr ist es nicht.
3 Schritte die dir künftig Zeitsteuerungen ohne delay() ermöglichen.

Man kann auch die eigentliche Funktion von der Zeitsteuerung trennen

const unsigned long Zyklus = 15*60*1000UL;  // 15 Minuten
void loop() {
  static uint32_t previousDoSomething = 0;
  if (millis() - previousDoSomething > Zyklus) {
    DoSomething ();  // wird einmal je Zyklus aufgerufen
    previousDoSomething = millis();
  }
}

Interrupts sind dafür nicht gedacht.

Was hast Du denn da?
Einen Flowmeter, der eine Impulslänge im Bereich von Millisekunden hat?
Dann reicht auch definitiv ein einfacher Abruf während der Laufzeit.
Erfasse bei jedem Umlauf den Zustand und reagiere darauf, wenn dieser sich in eine bestimmte Richtung geändert hat.

In einer kleinen Funktion fragst Du dann die Zeit ab und reagierst darauf, wenn die Torzeit abgelaufen ist, mit der Ermittlung des Wertes im Counter und setzt das gegen die vergangene Zeit.
Dann ist zu überlegen, ob der Counter zurückgesetzt wird oder ob der Counterstand gemerkt wird für die Ermittlung nach der nächsten Torzeit.

Abhängig davon

  • was dein Programm sonst noch so macht und wie lange das dauert
    und
  • mit welcher Maximalfrequenz die Impulse vom Durchfluss-Sensor kommen
    macht es unter Umständen Sinn die Impulse des Durchfluss-Sensors per Interrupt zu zählen.

Wenn du eine wirklich hohe Zahl an Impulsen pro Sekunde gezählt werden muss
und dein Programm 200 bis 800 millisekunden mit anderen Dingen beschäftigt ist
und man dieses "beschäftig sein" auf Teufel komm raus nicht schneller abarbeiten kann
dann verwendet man einen Interrupt.

Das was du void loop() nennst ist die function "loop()"
nur loop nicht void loop

Die Buchstabenfolge "void" (VAU O I DE ) gehört nicht zum Namen der function.

Diese function heißt "loop()"
Wenn du das "void loop" nennst weist du dich als Anfänger aus der noch nicht mal die elementarsten Dinge verstanden hat.

Das englische Wort "void" bedeutet übersetzt "nichts"
Du würdest also von der "nichts-loop" sprechen. Macht keinen Sinn.

Das void ist aber trotzdem zwingend notwendig weil das festlegt was diese function als Rückgabewert zurückgibt.

Nämlich genau das was das Wort besagt "nichts"
Für den Compiler ist es zwingend erforderlich, dass er mitgeteilt bekommt was diese function als Rückgabewert hat. Auch dann wenn es "nichts" ist.

Auch dann nicht (unbedingt), denn jeder(?) AVR hat Eingänge die direkt auf einen der Hardware Timer/Counter gehen.
Soweit mir bekannt, kann z.B. der Tiny85 so bis 64MHz Impulse zählen.

Interrupts würden nur gebraucht, um Counter Überläufe zu bemerken/zählen.

Das ist wohl etwas hoch gegriffen. Die Nullen sollten da mindestens weg.

Es geht um das Grundprinzip und darum die Richtung zu verdeutlichen. Wenn loop() zu langsam ist (für die jeweilige Aufgabe) dann ....

Oder willst du damit sagen selbst wenn loop() nur 2 Millisekunden Durchlaufzeit hat dann muss man auf jeden Fall schon einen Interrupt verwenden?
Das wäre dann eine verwirrende Aussage: Selbst wenn function loop() schon recht schnell ist
( 2 Millisekunden Durchlaufzeit)
aber eben nicht hyperschnell (= Durchlaufzeit von loop() weniger als 0,1 Millisekunden )
dann musst du einen interrupt nehmen.
Nein ! Eben nicht! Hängt doch von den Gesamtumständen ab!
Mal angenommen Temperatur wird mit onewire gemessen und nicht-blockierende Abfrage klappt nicht aus was für bescheuerten Gründen auch immer,
dann braucht die Abfrage des onewire-sensors nun mal an die 600 - 750 Millisekunden.

Die Entscheidung hast Du dann aber selbst getroffen, indem Du auf Teufel komm raus 12bit lesen willst.

Nonblocking geht immer.
Wenn die Abfrage des Sensors via lib, ob die Konv bereits fertig ist, nicht funktioniert, ist die 1sekunden Pause noch immer ein probates Mittel.

Danke, das war genau das was ich gebraucht habe.
ich habe es allerdings etwas vereinfacht und alles in den Loop reingeschrieben, das das für meine Zwecke gut funktioniert.

Du hast deinen Code jetzt nicht gepostet. Mag sein dass es für einen ultramini-Code "einfacher" ist alles in loop reinzuklatschen.

Sobald man mehr als zwei Programmteile hat die eine sinnvolle Untereinheit ergeben
dann macht es immer Sinn dafür eigene functions zu definieren.
Möglicherweise hast du das definieren von functions noch nicht so richtig drauf.
Dann besteht die Schwierigkeit darin.

Wenn man functions verstanden hat, dann wird alles sehr viel einfacher wenn man functions verwendet.

vgs

Ja das Stimmt das mit den Functions verstehe ich noch nicht wirklich. Da ich nur Sporadisch mit Arduino arbeite und es Bislang nur sehr einfache Codes waren kam ich damit nicht in Berührung.

Aber mal ne Andere Frage ich habe hier meinen Code


if (currentMillis - previousMillis >= interval) {
  // save the last time you blinked the LED
  previousMillis = currentMillis;

  
int licht = analogRead(A4);
int feuchte1 = analogRead(A3);
int feuchte2 = analogRead(A2);
int feuchte3 = analogRead(A1) ;
ExportCounter = ExportCounter + 1;

if (licht > 510) {
  digitalWrite(A0, HIGH); //TESTLINE DAYNIGHT
}
else         {
  digitalWrite(A0, LOW); //TESTLINE DAYNIGHT
}
dataString = String(ExportCounter) + "," + (licht) + "," + (feuchte1) + "," +(feuchte2) + "," +(feuchte3) + "," + (digitalRead (A0)) + "," + (waterconsumption); //Create Datastring for CSV File
Serial.println(dataString);
Serial.println(analogRead (A3));
Serial.println(analogRead (A2));
Serial.println(analogRead (A1));
Serial.print("--------------------------------------------------\n");
datalog = SD.open("Datalog.txt", FILE_WRITE);
datalog.println(dataString);
datalog.close();

und in dem String der die Var feuchte 1-3 verwendet steht was anderes wie in den Println wo ich direkt analog read printe. Weist du wie das kommt?

Muss es nicht String(licht) ... String(feuchte1) etc. heissen?
Ich mach sowas nicht, wäre aber mein erster Gedanke.

Was dann auch dazu führt, Dir sprintf, bzw. den großen Bruder snprintf dringend zu empfehlen.

Wer zwei mal das gleiche fragt, bekommt auch zwei verschiedene Antworten.
Insbesondere wenn sich zwischendurch was geändert hat.

Ich wieß nicht was du erwartest, aber ich erwarte dass sich Analogwerte auch mal ändern.

naja das ding ist ich habe zum testen die ausgängge A1 A2 A3 auf 5V gesteckt im sprintstring habe ich einen wert von 500 und im printline einen von2

danach habe ich die Ausgänge A1 A2 A3 auf GND gesteckt im printstring habe ich einen wert von 500 und im Printline einen von 1000.

und das ist ja so erstmal komisch (wenn auch nicht lustig)

Doch!
Ich kann da nur lachen!
(nicht analysieren)

Code untestbar, Schaltplan und Board geheim.
Also: KA, was du da tust.

Bedenke:
Meist wird nur das gezeigt, wo der Fehler nicht ist.
Denn wäre er da, wo du gesucht hast, hättest du ihn sicherlich gefunden.

Nöö, das ist schon ok....

String(ExportCounter) erzeugt eine Instanz
Diese implementiert das +
Was allerdings über ist, ist die Klammer um licht herum.

Ok - weiss i bescheid.
THX.