Fragen zu attachInterrupt +S0 Schnittstelle auslesen

Hallo, ich habe mit dem Arduino erfolgreich ein Beispiel nachgebaut, womit ich die S0-Schnittstelle eines Stromzählers auslesen und so die Zählimpulse mit dem Arduino zählen und auswerten kann.

Nun meine wichtige Frage dazu: Ist es möglich mit der Funktion attachInterrupt mehrere unterschiedliche Pins und somit mehrere Zähler entsprechend getrennt auszuwerten?

Bisher klappt das leider nicht und ich erhalte in meinem Beispiel überall die gleichen Werte, obwohl an Pin 3 und 4 kein Zähler angeschlossen ist. Somit ist die Frage, ob der Fehler eine technische Ursache hat oder im Code ein Fehler steckt?

int interval = 5;  //Counter refresh and save interval

int wh_start1;
int wh_start2;
int wh_start3;
int crontimer;


 
void setup() {
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(2), Ext_INT1_ISR1, RISING);
  attachInterrupt(digitalPinToInterrupt(3), Ext_INT1_ISR2, RISING);
  attachInterrupt(digitalPinToInterrupt(4), Ext_INT1_ISR3, RISING);
}

void loop() {
  if(millis()/1000 > crontimer+interval){
    crontimer = millis()/1000;
        
    Serial.print("Start Wh: ");
    Serial.print(wh_start1);
    Serial.print(wh_start2);
    Serial.println(wh_start3);
    Serial.println("================");
  }
}


void Ext_INT1_ISR1()
{
    wh_start1++;
}

void Ext_INT1_ISR2()
{
    wh_start2++;
}

void Ext_INT1_ISR3()
{
    wh_start3++;
}

Nicht jeder Pin geht.
Hängt von deinem Arduino ab.
Näheres findest du in der Referenz.

Im übrigen werden Interrupts selten wirklich gebraucht. Wie lang sind denn deine Impulse?
Im Microsekundenbereich?

Der S0-Impuls ist mindestens 30ms lang.

Wo finde ich die Referenz für die Interrupt-Pins?

Ein loop-Durchlauf ist normalerweise schneller als 1 ms. Brauchst also eine Flankenerkennung.

http://www.arduino.cc/reference kennst du?

Von da bis attachInterrupt() - Arduino-Referenz ist es nur 1 Klick.

Eingangspins die nicht beschaltet sind ziehen Störungen herein. Sie können ein HIGH wie auch ein LOW Potential haben, oder auch jederzeit zufällig wechseln.

Reden wir mal von einem Arduino UNO R3.
Der Controller ist an allen Pins Interuptfähig. Die Pins 2 und 3 haben eine bessere Ausarbeitungsmöglichkeit. Alle anderen Pins können einen Interrupt generieren, die genaue Interruptquelle muß aber nach einem Interruptereignis explizit ausgelesen werden.

Grüße Uwe

1 Like

Hallo,
wie hast Du denn die Eingänge beschaltet ? Wenn die offen in der Luft rumhängen stören die sich eventuell gegenseitig .

Du kannst aber die Eingänge auch ganz normal im Setup mit dem internen Pullup und pinMode auf ein definiertes Potential legen.

Beim Uno kannst Du pin2 und pin3 für eine ISR nutzen. Wenn Du int Variable nutzt dann benötigt der Uno 2 Speicherzugriffe beim auslesen. Genau zwischen beiden könnte ein Interrupt fallen und die bereits halb ausgelesene Variable nochmals ändern. Das kann zu falschen Werten führen. Damit das nicht passiert, kann man vor der Übergabe den Interrupt sperren und direkt anschließend wieder freigeben, oder das ATOMIC Makro verwenden.
Deine Zählvariable außerhalb der ISR verändern willst Du ja vermutlich nicht ansonsten solltest Du die Variable ebenfalls als volatile definieren.

Hallo,
ich hab das mal in Deinen Sketch eingebaut. Die Berechnung millis()/1000 habe ich jetzt mal rausgenommen , ich denke da kann es zu Rundungsfehlern kommen. Ich habe aber nicht weiter drüber nachgedacht :wink:


uint32_t interval = 5000;  //Counter refresh and save interval
uint32_t crontimer;

int wh_start1, i_wh1;
int wh_start2, i_wh2;
int wh_start3;

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), Ext_INT1_ISR1, RISING);
  attachInterrupt(digitalPinToInterrupt(3), Ext_INT1_ISR2, RISING);
  // attachInterrupt(digitalPinToInterrupt(4), Ext_INT1_ISR3, RISING);
}

void loop() {
  if (millis() >= crontimer + interval) {
    crontimer = millis();

   // detachInterrupt(digitalPinToInterrupt(2)); -----so ist es falsch
  //  detachInterrupt(digitalPinToInterrupt(3));

    noInterrupts();
    wh_start1 = i_wh1; // variable schnell umlanden
    wh_start2 = i_wh2;
    interrupts();

   //  attachInterrupt(digitalPinToInterrupt(2), Ext_INT1_ISR1, RISING);
  //   attachInterrupt(digitalPinToInterrupt(3), Ext_INT1_ISR2, RISING);
    
    Serial.print("Start Wh: ");
    Serial.print("  "); Serial.print(wh_start1);
    Serial.print("  "); Serial.println(wh_start2);
    //Serial.println(wh_start3);
    Serial.println("================");

  }
}


void Ext_INT1_ISR1()
{
  i_wh1++;
}

void Ext_INT1_ISR2()
{
  i_wh2++;
}

void Ext_INT1_ISR3()
{
  wh_start3++;
}

Nachtrag: Sketch auf Grund von Post #9 geändert

Nein!

Das ist was anderes als cli(); / sei();
bzw. der Arduino-Variante

   noInterrupts();
    wh_start1 = i_wh1; // variable schnell umladen
    wh_start2 = i_wh2;
   interrupts();

So gehen die Interrupts nicht verloren.

Ja.
Ich habe aber noch eine weitere Möglichkeit, die in Betracht gezogen werden kann.
Du kannst weitere Pins als interruptpins deklarieren und dann mit eigener Routine auslesen.
Schau Dir im DaBla die Pinbelegung an.
Jeder pin, der pcint gefolgt von einer Zahl beschrieben ist, ist fähig.

Über welchen arduino sprichst du?
Für einen Mega habe einen kompletten Port -> 8 Pins direkt verfügbar gemacht und damit alle meine Zähler ausgelesen.

Vielen Dank! Ich werde das mal ausprobieren.

Es funktioniert mit getrennter Zählung. Sehr gut! :+1:

Aber nur mit ausgeschaltetem PULLUP, dafür Verwendung von 10kOhm Pulldown-Widerständen.

Das Originalbeispiel findet man dazu hier:

Allerdings brauche ich das mit dem EPROM nicht.

Ich spreche von einem normalen UNO R3.

Das mit dem MEGA interessiert mich. Kann man da genaueres erfahren?

Außerdem habe ich in der Referenz gelesen, dass die beim UNO WiFi R2 alle Pins Interrupt fähig sind.

Ich muss ca. 14 Zähler auslesen. Würde das mit einem MEGA funktionieren oder braucht man dafür zwei?

Ich würde das ohne Interrupts machen. Und du kannst 14 Eingänge auch mit dem Uno R3 erfassen. Frage ist eher, was du damit machen willst.

Beim ATmega328 des UNO R3 sind alle Pins Interruptfähig. Der Unterschied zwischen den pins 2 und 3 und den anderen ist die Ausleselogik. Für die Pins 2 und 3 gibt es explizit Register während die anderen in Register zusammengefaßt sind und man die Info daraus extrahieren muß. Ist keine Hexenwerk aber eben nicht von der Controlerinternen Logik so vorgekaut und in mundgerechte Stücke gebracht wie bei Pins 2 und 3.

Grüße Uwe

Hallo @michael_x ,
danke für den Hinweis
darf ich da noch mal für mein Verständnis nachfragen ?
ich war bisher davon ausgegangen das detachInterrupt(digitalPinToInterrupt(2)) den Interrupt auf pin 2 und damit die dazugehörige ISR abschaltet. Nur diese zugehörige ISR könnte ja den Wert der Variablen verändern. Nachteil ein zugehöriger Pegelwechsel an dem Eingang während der Zeit würde unerkannt bleiben, also im Beispiel nicht gezählt.

Aus deinem Post schließe ich nun:
noInterrupts() schaltete alle Interrupts aus, klingt ja erst mal unnötig.
Aber nach dem erneuten Einschalten werden die zugehörigen ISR für die Ereignisse, die während der Zeit aufgetreten sind, nachträglich bearbeitet. Damit ginge kein Interrupt verloren und es würde in dem Beispiel immer gezählt.

Gruß Heinz

noInterrupt() ( = cli() ) sperrt Interrupts, setzt also quasi die Priorität des normalen Sketches hoch.
Wenn in der Zeit ein Interrupt auftritt, wird er eben danach bearbeitet.
Innerhalb einer ISR sind übrigens die Interrupts genauso gesperrt.

detachInterrupt hängt die ISR ab, richtig. Alle Impulse die in dieser Phase kommen, gehen also verloren. Das willst du nicht.

Danke wieder was dazu gelernt.

Jein. Es hängt von der Programmgestaltung ab.

Eine lange Interruptroutine und schnelle Interupts können zu verarbeitungsverlusten führen. Egal ob in der ISR der Interrupt geperrt ist oder interrupts erlaubt sind.

Du mußt nur denken Du sitzt vor dem Fernsehr, siehst ein interessantes Fusßballspiel und Deine Frau will was von Dir. Wenn es nur ein Einmachglas ist das Du aufmachne mußt geht das ja noch, wenn aber der Müll rausgetragen werden muß, der Winterbezug der Gartensitzbank aus dem Speicher geholt werden und Du noch eine weitere Aufgabe bekommst bevor Du mit der letzten fertig bist wird das weder was Gutes mit dem Spiel noch mit der Abarbeitung der Unterbrechiungen.

Grüße Uwe

1 Like

Hallo,
wenn du die Zählerstände der 14 Zähler ebenfalls im EPROM speichern willst, wie in dem Link, dann kannst du dazu eine Struktur anlegen. Die lässt sich dann mit get und put mit einer Zeile auslesen und speichern. Zudem macht die Verwendung von Arrays Sinn.
ich denke auch es sollte ohne Interrupts gehen, allerdings wenn Du z.B noch ein Display mit dran hast sieht das eventuell anders aus. Es hängt davon ab was Du mit den Daten vorhast. Eventuell nimmst Du auch einen ESP und nutzt z.B einen Webserver über Wlahn als Anzeige.Möglichkeiten gibt es da reichlich.

Geht auch mit einem uno.
Ich habe die analogen Pins dazu umgebogen.
Jeder pin, der pcint beschriftet ist, lässt sich dafür verwenden.