Fragen zum Thema Interrupt - warum Faktor 4 ?

Hallo,

ich baue noch immer dieses Windsensor-Projekt nach:

http://ossiachersee-wind.info/project.php

Irgendwie komm ich mit den Interrupts nicht ganz klar.
Der Sketch funktioniert grundsätzlich, aber liefert nicht was ich mir erwarte:

In der ISR windpulscallback() zähle ich die Interrupts in einer 2 Sekunden Periode.
Zusätzliche schreibe ich für jeden Interrupt einen * auf die serielle Schnittstelle.

Aus irgendeinem Grund bekomme ich immer Pakete von 4* je Impuls.
Auch die Variable pulses ist immer ein Vielfaches von 4.

Ich könnte es natürlich jetzt einfach durch 4 dividieren, aber ich würde gerne das Problem verstehen.

#include <SPI.h>
#include <Time.h>

#define DELAY 2000



unsigned long last_upload=0;
float current_speed=0.0f;
float accum_speed;

unsigned long sum_pulses=0;
unsigned long sum_delay=0;

unsigned long samples;
float current_direction=0.0f;
unsigned long pulses = 0;

void setup() {
  // start the serial library:
  Serial.begin(9600);
  Serial.println("Hello");

}

void windpulscallback() {
  //  latest_interrupted_pin=PCintPort::arduinoPin;
  //  interrupt_count[latest_interrupted_pin]++;
  pulses++;
  Serial.print("*");
}


void calculateSpeed() {
  unsigned long start;
  unsigned long end;
  unsigned long diff;


 attachInterrupt code as required
  attachInterrupt(digitalPinToInterrupt(2), windpulscallback, RISING);
  Serial.println(String(digitalPinToInterrupt(2)));
  // Messung starten (5 Sekunden)
  pulses=0;
  start=micros();
  delay(DELAY); // in milliseconds
  end=micros();

  // shutdown
  detachInterrupt(digitalPinToInterrupt(2));

  // Berechnung 
  diff=end-start;
  Serial.print("Time(in microsec):");
  Serial.println(diff);
  Serial.print("Pulses:");
  Serial.println(pulses);
  sum_pulses+=pulses;
  sum_delay+=diff;
  
  current_speed=pulses*1000.0f*DELAY/6.0f/diff;
  Serial.print("Speed:");
  Serial.println(current_speed);

}


void loop()
{
  calculateSpeed();
  accum_speed+=current_speed;
  samples++;
}

Serielle Ausgabe:

****Time(in microsec):2000008
Pulses:4
Speed:0.67
0
********Time(in microsec):2000008
Pulses:8
Speed:1.33
0
****Time(in microsec):2000012
Pulses:4
Speed:0.67
0
********Time(in microsec):2000012
Pulses:8
Speed:1.33
0
****Time(in microsec):2000012
Pulses:4
Speed:0.67
0
Time(in microsec):2000012

25.12a.png

Serial gehört nicht in Interrupts!

pulses sollte als volatile deklariert werden. In diesem Fall merkt der Compiler zwar dass sie an mehreren Stellen verwendet wird, aber es gibt Code wo die ISR die einzige Stelle ist wo eine Variable verändert wird. Der Compiler kann dann nicht feststellen wie diese aufgerufen wird.

OK Jetzt bewegen sich die Werte zwar nicht mehr in 4er Paketen, aber trotzdem relativ statisch ...

4,6,7,11,6,7,11,4,4,0,0,...

Unter 4 schaff ich gar nicht

Ich weiß nicht genau nach welchem Prinzip dein Anemo funktioniert ? Meist ist ein Reedkontakt drin, dann passiert sowas:

Außerdem ist es möglich das der Reedkontakt prellt.

Ich habe damals meinen Anemo zerlegt und eine Gabellichtschranke mit Schlitzscheibe aus einem alten Drucker eingebaut. Dann hast du das Problem, das keine Eichung mehr da ist.

Ich hab das Ding jetzt mal zerlegt. (siehe Fotos im Anhang)
Da ist unten im Fuß sowas wie ein schwingender Schalter und oben im Kopf drehen sich zwei Permanentmagneten daran vorbei.
Scheint als würde der Schalter tatsächlich schwingen.

Das müsste sich aber mit einem delay() in der ISR rausfiltern lassen, oder ist das keine schöne Lösung ? :slight_smile:

Ich möchte nicht unbedingt einen neuen Sensor kaufen, weil der auch für meine Beschattungssteuerung verwendet wird und ins bestehende System eingebunden ist.

delay() geht ISRs auch nicht, da in einer ISR keine anderen Interrupts mehr gehen und damit micros() nicht richtig inkrementiert, was von delay() zum Zählen verwendet wird. Was gehen würde ist delayMicroseconds().

Wenn du nicht weißt was du mit Interrupts umgehen musst, dann verzichte darauf. Mach die Tor-Zeit mit millis()/BlinkWithoutDelay und Frage den Eingang per Hand ab. Dann kannst du ihn auch entprellen.

Ok, es handelt sich um einen Reed Kontakt basierenden Anemo.

Was du vor hast, ist innerhalb einer ISR nicht möglich. :o Bzw wird nicht funktionieren.

Es gibt zwei Wege zum Entprellen via. Hardware oder Software.

Hardwaremässig: Würde ich einen 100nF parallel zum Pull up Widerstand einbauen, und testen.

Wobei zu testen wäre, wo dann die obere Grenzfrequenz liegen würde. Das zu berechnen überlasse ich anderen....

Edit: Die Bilder des Anemo zeigen 2 Magnete auf der Achse, im Kontext zu meinem Link erzeugt der Geber 4 Impulse pro Umdrehung. Ich war etwas aus dem Konzept geraten, weil normal 2 Impulse pro Umdrehung generiert werden.

Wenn du also immer 4 Impulse pro Umdrehung zählst, hast du kein Prellproblem.

Meine Interpretation der Verwendung von Interrupts in dem Windsensor-Sketch:

  • Aktiviere Interrupts in einer Funktion die regelmäßig in der Loop() aufgerufen wird
  • Starte Messung für x Sekunden
  • Zähle wie oft HIGH-Signal am Interrupt-Pin anliegt
  • De-aktiviere Interrupts

Das ist aus meiner Sicht zwar kein besonders gutes Beispiel um Interrupts zu erklären, aber das kann auch meine Interpretation sein :)

Inwiefern funktioniert in der Hinsicht delayMicroseconds() anders als delay() ?

Das mit dem Kondensator ist auch einen Versuch wert, aber sowas kann ich frühestens am Montag kaufen - bis dahin ist noch Zeit für softwaretechnische Versuchsansätze :)

Die Bilder des Anemo zeigen 2 Magnete auf der Achse, im Kontext zu meinem Link erzeugt der Geber 4 Impulse pro Umdrehung. Ich war etwas aus dem Konzept geraten, weil normal 2 Impulse pro Umdrehung generiert werden.

Wenn du also immer 4 Impulse pro Umdrehung zählst, hast du kein Prellproblem.

Wie fragt man sowas sinngemäß “von Hand” ab ?

In der Loop() die Status=HIGH Situationen raufzählen und durch millis() dividieren ?

@rudirabbit
Verstehe ich nicht - bei einer Umdrehung kommen die Magneten zweimal vorbei → 2 Impulse
Wie kommst du auf 4 ?
Nachdem ich das Serial.println() aus der ISR rausgenommen hab bin ich auf Werten wie 6,7,11, etc.
Also keine Vielfachen mehr …

Verstehe ich nicht - bei einer Umdrehung kommen die Magneten zweimal vorbei -> 2 Impulse Wie kommst du auf 4 ?

Upps stimmt, mein Denkfehler.

Nachdem ich das Serial.println() aus der ISR rausgenommen hab bin ich auf Werten wie 6,7,11, etc. Also keine Vielfachen mehr ...

Und das quasi zufällig ? dann prellt das ganze doch.

Von Hand abfragen (polling) geht im z.b mit millis(), wie Serenifly schon beschrieben hat. Den Zustandsänderungen (z.b von 0->1) des Pins über eine definierte Zeit die du über millis() berechnest abfragen und dann die Variable incrementieren.

Ja, ganz zufällig - und ich schaff "von Hand" keinen Wert unter 4, egal wie langsam und vorsichtig ich drehe :)

D.h. ich kanns einfach über das Zählen der Zustandsänderungen und in Beziehung setzen zu den millis() machen Das Zählen läuft dann aber ganz normal in der Loop(), was bedeutet dass die Durchlaufzeit einer Loop() meine Frequenz nach oben begrenzt (aber wohl nicht der Rede wert in meinem Beispiel :))

oder ...

und da fehlt mir noch ein kleiner Tip: mit delayMicroseconds() und Interrupts Inwiefern funktioniert delayMicroseconds() anders als delay() so dass es in Verbindung mit den Interrupts verwendet werden kann ?

lG Gawan

Du kannst dir den Code anschauen: wiring.c

delayMicrosconds() macht eine Schleife in inline Assembler. Das funktioniert also für sich ohne von anderem Code abhängig zu sein. delay() fragt dagegen micros() ab. delayMicroseconds() hat aber einen unsigned int als Parameter! Also lange Zeiten sind da nicht so so ohne weiteres möglich.

Das Zählen läuft dann aber ganz normal in der Loop(), was bedeutet dass die Durchlaufzeit einer Loop() meine Frequenz nach oben begrenzt (aber wohl nicht der Rede wert in meinem Beispi

Uh nein. Du kannst mit millis() abfragen wann zwei Sekunden vorbei sind. Indem du den Anfang der Messung abspeicherst und dann wie üblich if(millis() - previousMillis > interval) machst

Serenifly hat das Beispielscript BlinkWithoutDelay erwähnt, nach diesem Prinzip funktioniert es.

Wobei ich der Meinung bin, das der Weg über den ext. Interrupt schon der Schönere ist. Wenn es aber prellt, ist dies suboptimal - schon deswegen weil die ISR mehrmals unnötig angesprungen wird.

Wenn du einen 100nf Kondensator besorgen kannst, versuche es mal damit. Dann kannst du bei dem IRQ Code bleiben.

BTW: Hast du keine alten Platinen aus dem Elektronikschrott rumliegen? Mit dem Fundus in meinem Dachboden könnte ich die Straße damit pflastern:) Alles was mir in die Hände kommt, wird zerlegt, und das was noch zu gebrauchen ist, eingelagert. Und dieser Kondensator wird sehr häufig eingebaut.

Hardware-Entprellung + Interrupts wäre wahrscheinlich das schönste

Aber generell muss man dann wissen was man in Interrupts tun darf und was nicht: 1.) ISRs kurz halten. Keinen blockierenden Code 2.) Kein Code der auf Funktionen beruht die selbst Interrupts brauchen. delay() geht nicht. millis() und micros() inkrementieren nicht (einmal für einen Zeitstempel geht es). Kein Serial! 3,) Variablen die innerhalb und außerhalb von ISRs verwendet werden müssen als "volatile" deklariert werden.

OT: Wäre interessant ob dies mit dem Due einfacher wäre. Der hat PIO Glitch und Debouncing Filter Register die man setzen kann.

Ich bin da leider ziemlich schlecht ausgestattet. Mit dem Thema Arduino beschäftige ich mich seit wenigen Monaten und Elektrotechnik inkl. der zugehörigen Hardware war bisher nicht Teil meines Lebens :) Im Informatik-Studium hatten wir nur ein Semester halbherziges Rumstecken ohne allzuviel KnowHow-Transfer.

Gawan:
Im Informatik-Studium hatten wir nur ein Semester halbherziges Rumstecken ohne allzuviel KnowHow-Transfer.

Also du hast Informatik Studiert oder bist gerade dabei ?

Dann verstehe ich nicht wirklich, das du dich nicht reindenken kannst wie ein polling funktioniert. :o

OK - mit den ISR’s hat man bei einer OS-basierenden Entwicklung direkt wenig zu tun.
Deshalb kann ich nachvollziehen, das du hier gravierende Fehler machst.

Ich wollte dir nicht zu nahe treten, bitte nicht falsch verstehen.
Wundere mich nur etwas.

Das mit ISRs ist auch nicht sooo allgemein. Das wird auf verschiedenen Prozessor-Architekturen anders implementiert. z.B. dass sich ISRs nicht gegenseitig unterbrechen können ist nicht unbedingt überall so.

Ich hatte einen Kurs hardwarenahe Programmierung und das hat mir in Bezug an den Arduino auch nichts gebracht. Da wurde x86 behandelt. Und das ohne viel Praxisbezug. Da hatte ich was anderes erwartet. Einer meiner größten Vorteile ist vielleicht dass ich 8051/52 in Assembler programmiert habe. Dadurch bekommt man eine ganz andere Denkweise in Bezug auf Low-Level Probleme.

Ich hab mein Studium vor 10 Jahren beendet und hab nicht mehr wirklich viel mit den Basics zu tun.

Mein Programmierkenntnisse gehen soweit dass ich grundsätzlich das Problem verstehe und die möglichen Lösungen bewerten kann, ausprogrammieren lass ichs dann andere, bessere.

Und mit Hardware hatte ich noch nie zu tun. Ich bin schon stolz wenn ich selber die LED mit (zusätzlichem) Bewegungsmelder montiere daheim in der Garage :) Daher ist der ganze elektrotechnische Bereich für mich zwar total spannend, aber einfach auch komplett neu.

Mit Interrupt hatte ich zuletzt bei meinem 286er zu tun dessen Soundkarte konfiguriert werden wollte :)