Interrupt: Pegeländerung an Interrupt-Pin detektieren

Hallo zusammen,

ich bin ziemlicher Anfänger in der Programmier-Materie und hoffe ihr könnt mir hiebei helfen :slight_smile:

Mein Projekt ist folgendes:

Ich möchte mittels eines Sensors Luftblasen in einer Leitug die mit Wasser gefüllt ist detektieren und zählen. Das Ganze gebe ich dann an ein LCD-Display aus.
Der Sensor hat einen einfachen TTL-Output und verhält sich dabei so:

Luftblase (High-Impuls ca. 2ms);
Wasser (Low)

Ich habe das Detektieren und Zählen der Luftblasen so implementiert und das Ganze läuft auch so wie ich es mir vorgstellt habe :slight_smile:

#define Sensor 2                           //Ultraschallsensor mit dem Name "Sensor" an Pin3          
volatile boolean bLFlag = LOW;                  
int iLCount;                               //Datentyp von iLCount = integer
#include <LiquidCrystal.h>                 //LCD Library laden
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);     //LCD Library mit den verwendeten I/O-Pins initialisieren

unsigned long prev_time;


void setup() {
 lcd.begin(16, 4);                        //Zeilen-Spaltenanzahl des LCD-Displays
 lcd.setCursor(1, 0);
 lcd.print("Blasenzaehler");              //Überschrift
 lcd.setCursor(5, 2);
 lcd.print("[Blasen/s.]");                //Einheit [Blasen pro Sekunde]
 pinMode(Sensor, INPUT);                  //Pin (Pin 3) als Eingang deklarieren
 prev_time=millis();
 attachInterrupt(0, isr_int1, RISING);    //die Interrupt-Routine deklarieren (Interrupt-Pin; wohin soll in Fall eines INT gesprungen werden (immer isr_"Name"); auf was soll getriggert werden)

}

void isr_int1() {                          //Interrupt-Servicerountine
 iLCount++;
 bLFlag = true;
}


void loop() {
   if((millis()-prev_time)>999) 
   {
   lcd.setCursor(0, 2);                  //setzt den Cursor in 3. Zeile und 1. Zeichen
   lcd.print(iLCount);                   //gibt iLCount am Display aus
   bLFlag = false;
   prev_time=millis();
   iLCount=0;
   delay(1000);                         //Verzögerung von 1s bevor Zeile 3 gelöscht wird
   lcd.setCursor(0, 2);                 //setzt den Cursor auf 1. Zeichen der 3. Zeile
   lcd.print("     ");                  //löscht Zeile 3 bis zum 5. Zeichen
 } 
}

Nun habe ich aber das Problem, dass ich gerne erkennen würde wenn der Wasserstand innerhalb der Leitung zu niedrig ist. In diesem Fall würde der Sensor ein dauerhaftes High-Signal liefern.
Den Interrupt-Pin kann ich ja aber lediglich auf eine Pegeländerung “einstellen”. Das heißt, ich könnte zwar detektieren wenn der Output des Sensors auf “High” geht… aber da bleibt er ja dann auch.
Und dies würde ich ja als Luftblase und nicht als “Wasserstand zu niedrig” detektieren.

Deshalb habe ich mir nun folgendes überlegt:

Ich gebe das Signal des Sensors einfach zusätzlich auf den 2.ten Interrupt-Pin und stelle diesen auf “Rising”. Sobald nun ein High-Signal kommt programmiere ich den Interrupt-Pin auf “Falling” um und warte ca. 1s auf die fallende Flanke (die ja, wenn es nur eine Luftblase ist kommen muß). Falls diese nicht kommt heißt das, dass der Output dauerhaft auf High-Pegel steht und der Wasserstand damit zu niedrig ist.

Jetzt kommt aber die Grucks… ich habe keinen Plan wie ich das vernünftig in mein bestehendes Programm einarbeite sodass alles am Schluß funktioniert :confused:

Ich hoffe mir kann von euch einer dabei helfen :slight_smile: :slight_smile: :slight_smile:
Würde mich riesig darüber freuen…

Vielen Dank schonmal im Voraus dafür!!!

Das heißt, ich könnte zwar detektieren wenn der Output des Sensors auf "High" geht........... aber da bleibt er ja dann auch.
Und dies würde ich ja als Luftblase und nicht als "Wasserstand zu niedrig" detektieren.

Den Zeitpunkt merken!
Wenn länger als 2mS High, dann Wassermangel

combie:
Den Zeitpunkt merken!
Wenn länger als 2mS High, dann Wassermangel

Dazu kannst Du die micros() verwenden, die sind in dem Bereich genauer. Wenn Du if (micros() - altMicros >= 2000) {} schreibst, macht auch der Überlauf der micros() nichts.

Ich würde den Interrupt einfach so lassen wie er ist.

da ein Leerstand was langsames und lang andauerndes ist, würde ich den Pin in der loop einfach abfragen.
Wenn längere Zeit high UND bLFlag=false dann ist das Rohr leer.

Zwei Kleinigkeiten:
1.) iLCount sollte auch volatile sein
2.) während man iLCount ausliest sollte man die Interrupts sperren

Also sowas:

noInterrupts();
int temp = iLCount;
iLCount = 0;
interrupts();

lcd.print(tmp);

Vielen Dank für die Rückmeldungen :slight_smile:
Ich habe das Ganze jetzt zwar etwas anders gelöst wie vorgschlagen aber trotzdem vielen Dank!!!

Falls es jemanden interessiert hier der Code

#define Sensor 2                           //Ultraschallsensor mit dem Name "Sensor" an Pin2          
volatile boolean bLFlag = LOW;                  
int iLCount;                               //Datentyp von iLCount = integer
#include <LiquidCrystal.h>                 //LCD Library laden
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);     //LCD Library mit den verwendeten I/O-Pins initialisieren
int pin = 3;
int value = 0;
unsigned long prev_time;


void setup() {
 lcd.begin(16, 4);                        //Zeilen-Spaltenanzahl des LCD-Displays
 lcd.setCursor(1, 0);
 lcd.print("Blasenzaehler");              //Überschrift
 lcd.setCursor(5, 2);
 lcd.print("[Blasen/s.]");                //Einheit [Blasen pro Sekunde]
 pinMode(Sensor, INPUT);                  //Pin (Pin 2) als Eingang deklarieren
 pinMode(pin, INPUT);
 prev_time=millis();
 attachInterrupt(0, isr_int1, RISING);    //die Interrupt-Routine deklarieren (Interrupt-Pin; wohin soll in Fall eines INT gesprungen werden (immer isr_"Name"); auf was soll getriggert werden)
 if(value != LOW)
 {
   bLFlag = false;
 }

}

void isr_int1() {                          //Interrupt-Servicerountine
 iLCount++;
 bLFlag = true;
}


void loop() {
   if((millis()-prev_time)>999) 
   {
   lcd.setCursor(0, 2);                  //setzt den Cursor in 3. Zeile und 1. Zeichen
   lcd.print(iLCount);                   //gibt iLCount am Display aus
   bLFlag = false;
   prev_time=millis();
   iLCount=0;
   delay(999);                          //Verzögerung von 1s bevor Zeile 3 gelöscht wird
   lcd.setCursor(0, 2);                 //setzt den Cursor auf 1. Zeichen der 3. Zeile
   lcd.print("     ");                  //löscht Zeile 3 bis zum 5. Zeichen
 } 

 value = digitalRead(pin);
 if(value == HIGH && bLFlag == false)
 {
   lcd.setCursor(0, 2);
   lcd.print("Wasserstand Low "); 
   delay(999);
   lcd.setCursor(0, 2);
   lcd.print("     [Blasen/s.]");
                
 }
}
// Definition
volatile int iLCount;

// Benutzung
int Count;

  noInterrupts();
  Count = ilCount;
  interrupts();

iLCount muss volatile definiert werden. Zugriffe auf iLCount müssen atomar sein (so wie du die Variable in deinem Kode benutzt).

Das habe ich schon in #4 gesagt :slight_smile:

Ich hätte es alles nochmal lesen müssen, sorry. :confused:

Irgendwie konnte ich mir auch nicht vorstellen, dass man so einen Kode schreibt, wenn es schon jemand so deutlich gesagt hat, mein Weltbild ist wohl doch immer noch zu optimistisch.

Gibt es keinen schönen Beispielsketch, wo man den Effekt mal deutlich sehen kann?

Das ist glaube ich nicht reproduzierbar. Dazu muss der Interrupt genau zwischen dem Zugriff auf die zwei Bytes eines Integers kommen.

Na dann gibt es jetzt ein eifaches Beispiel

void setup() {
  Serial.begin(115200);
  Serial.println(F("wait for fail..."));
  // set up Timer 1
  TCCR1A = 0;                         // normal operation
  TCCR1B = bit(WGM12) | bit(CS10);   // CTC, no pre-scaling
  OCR1A =  10999;                    // compare A register value (1000 * clock speed)
  TIMSK1 = bit (OCIE1A);             // interrupt on Compare A Match
}
volatile unsigned long Counter;

ISR(TIMER1_COMPA_vect)
{
  Counter++;
}

void loop() {
  static unsigned long vorher;
  static unsigned long aktuell;

  aktuell = Counter;
  unsigned long differenz = aktuell - vorher;
  if (differenz > 1) {
    Serial.print(F("von "));
    Serial.print(vorher);
    Serial.print(F(" auf "));
    Serial.print(aktuell);
    Serial.print(F(" diff "));
    Serial.println(differenz);
  }
  vorher = aktuell;
}
wait for fail...
von 11263 auf 11519 diff 256
von 11519 auf 11265 diff 4294967042
von 11265 auf 11267 diff 2
von 12287 auf 12543 diff 256
von 12543 auf 12289 diff 4294967042
von 12289 auf 12291 diff 2
von 17663 auf 17919 diff 256
von 17919 auf 17665 diff 4294967042
von 17665 auf 17667 diff 2
von 20991 auf 21247 diff 256
von 21247 auf 20993 diff 4294967042
von 20993 auf 20995 diff 2
von 27903 auf 28159 diff 256
von 28159 auf 27905 diff 4294967042
von 27905 auf 27907 diff 2
von 42495 auf 42751 diff 256
von 42751 auf 42497 diff 4294967042

Schön, aber wo ist da ein fail ?

Scheint mir nur, dass loop manchmal erstaunlich lange braucht :stuck_out_tongue:

allerdings doch erstaunlich, dass es dann meist eine Differenz + - 256 ist,
wobei 4294967042 eigentlich -256 ist.

Nein, Spass beiseite, ein sehr schönes Beispiel!
Jetzt noch interaktiv wechseln zwischen atomic( aktuell = Counter ) und fehlerhaft wie im Beispiel ,
und den Unterschied live bewundern!

Danke!

Die letzte Zeile der Unterschiede (die mit den 2 Abstand) entsteht durch die Verzögerung durch den Ausdruck.
Man kann aber schön sehen, dass das nicht so selten auftritt wie man meinen sollte.