Frequenzzähler mittels Interrupt. mal funktioniert er, mal nicht

Guten Tag.

Ich hatte vor ein paar Tagens chon mal eine Frage zum Frequenzzählen mit einem Arduino.
Jetzt habe ich den Code soweit fertig und habe nun den Interrupt an pin 21. An pin 21 liegt testweise GND durch einen Taster getrennt.
Jedes mal, wenn ich den Taster betätige, schaltet GND durch und mein LCD zeigt mir dann auch die (von mir geschätzte) Frequenz an. Zwei mal drücken pro Sekunde: 2 Hertz plus einige Nachkommastellen und so weiter.
Das funktioniert schonmal. Nun habe ich einen anderen Arduino genommen und mir da einen pin wechselweise HIGH und LOW ausgeben lassen. Diese Zustände sollten dann ja auch eindeutig definiert sein, sodass ein "Springen" des Interrupts eigentlich vorbei sein sollte.

void setup(){
pinMode(8, OUTPUT);
digitalWrite(8, HIGH);
}

void loop(){
digitalWrite(8, LOW);
delay(200);
digitalWrite(8, HIGH);
delay(200);
}

Und nun habe ich das Problem, dass eigentlich immer irgendwas mit 70 HZ auf dem Display steht, sobald ich meinen zweiten Arduino als Eingang nehme...

Das ist der Code, der messen soll:

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


volatile unsigned long period = 0;
volatile unsigned long prevtime = 0;
volatile float f = 0;
volatile float delta = 0;
void setup()
{
  Serial.begin(9600);
  lcd.begin(16, 2); 
}

void loop()
{
  period;
  f=1000000/(float)period;
  delta = f*f*0.002;
  detachInterrupt(2);
  lcd.setCursor(0,0);
  lcd.print("Freq=");
  lcd.setCursor(5,0);
  lcd.print(f, 4);
  lcd.setCursor(14,0);
  lcd.print("Hz");
  lcd.setCursor(0,1);
  lcd.print("d_f=");
  lcd.setCursor(5,1);
  lcd.print(delta,3);
  lcd.setCursor(13,1);
  lcd.print("mHz");
  
   Serial.println(period);
  
  attachInterrupt(2, Frequenz, RISING);

}

void Frequenz()
{
  period = micros() - prevtime;
  prevtime = micros();
  
}

Vielleicht kann sich das mal jemand gönnen und mir einen Tipp geben, in welcher Richtung die Fehler sind.

Als Anmerkung: Ich bin noch nicht ganz so lange dabei ;).

Vielen Dank im Voraus und ein angenehmes Wochenende ;).

Du mißt die Frequenz des Tasterprellen.
Grüße Uwe

Die Ausschläge des Prellens kann man da deutlich wahrnehmen. Aber ich habe die ungefähren 70 Hz ausschließlich sobald ich den zweiten arduino als Frequenz nehme, nicht, wenn der Taster anliegt. Und dann auch egal, welchen wert ich bei delay() einfüge.

Das Prellen habe ich vielleicht bei jedem fünften Mal der Tasterbetätigung. Dann erhalte ich auch eine Frequenz von 700 Hz, oder von 1500 Hz. So in der Größenordnung. Total daneben eben.

Aber die gewollten Impulse am Taster werden auch richtig detektiert. Nur eben nicht, wenn ich die beiden Pins der arduinos verbinde.

Der messende arduino ist ein Mega. Der andere ein Uno.

Ich habe keine Ahnung :D.

Hallo,

sollte eigentlich funktionieren. Deshalb die Frage, sind die Massen beider Arduino's miteinander verbunden?
Bzw. wie sind die beide Arduino's generell Spannung's versorgt?

Ich habe die Massen nicht verbunden... Und nun funktioniert das.
Wieso muss ich die Massen verbinden bei zwei Arduinos?
Ich dachte, der pin als Output reagiert autonom und schaltet entweder auf LOW oder auf HIGH.

Aber dadurch, dass ich die Massen verbinde, kommt es mir so vor, als würde ich die beiden Arduinos irgendwie synchronisieren.

Jetzt muss ich es nur noch hinbekommen, dass die Messwerte von z.B. 25 Perioden gelesen werden und dann der Mittelwert gebildet wird. Damit einerseits das Display nicht so schwankt (bei 50 Hz schon blöd) und andererseits ist der Mittelwert vollkommen ausreichend.

Vielen Dank, Doc.

Wieso muss ich die Massen verbinden bei zwei Arduinos?

Alle Signale brauchen einen Bezugspotentional!

Signal on: 5V gegen GND
Signal off: 0V gegen GND

Ohne GND, nix Bezugspunkt == nix Signal
Floating...Zufall...Einstreuung

Extrembeispiel:
Wieso kann der Mann überleben, der von einem Hubschrauber auf einer 100.000V Hochspannungsleitung abgesetzt wird.
Ganz einfach: Er hat keinen Kontakt zur Erde(unserem gemeinsamen Bezugspotential)
Hätte er Kontakt zur Erde, würde er aufglimmen und verdampfen.

Hallo,

der Signalpegel des einen Arduinos hing für den anderen Arduino in der Luft ohne Bezug zur seiner Masse. Der Empfangs Arduino hat zwar seine Masse, die muß aber nicht gleich der anderen sein.

Wenn Du die Arduinos ohne USB und nicht an der runden Buchse versorgst, also direkt mit 5V an dem 5V und GND Kontakten der Kontaktleisten, hättest Du vielleicht automatisch und instiktiv beide mit + und Masse gleich versorgt. Dann wäre dir das bestimmt nicht bewußt gewurden.

Merke. Spannung ist nichts weiter wie die Differenz zweier Potentiale. Auf welchen Level die Potentiale liegen, ist erstmal egal. Die Differenz ist die die du misst. Wir gehen immer in Gedanken davon aus das alles Massebezug hat, dem muß aber nicht sein, wie Du gemerkt hast.

Wegen dem Mittelwert. Wenn Du nur manchmal messen möchtest, kannste die 25 Werte sammeln, aufaddieren und teilen. Wenn Du permanent messen möchtest und ständig leichte Schwankungen ausfiltern möchtest, dann kannste die Funktion von gunther probieren.

Siehe hier: Mittelwert bilden... - Deutsch - Arduino Forum

Wegen der Genauigkeit gabs darüber noch eine Diskussion, finde den Link aber nicht. Vielleicht liest der User "MCSO" oder so ähnlich ja mit und ergänzt das?

Hallo,

ich nehme nun die Arbeit am Frequenzzähler wieder auf, erste Klausur ist weg ;).

Ich möchte nun die Netzspannung transformieren, per Brückengleichrichter und Schmitt-Trigger das Signal digital verwertbar machen. Der Trafo, den ich benutze, hat sekundär 6 Volt.

Kann ich jetzt einen Spannungsteiler verwenden, oder ist das ungeschickt wegen der Lastabhängigkeit des Spannungsteilers?

Meine letzte Frage wäre noch, wie ich dem messenden Arduino Masse geben soll. Ich habe nach dem Schmitt-Trigger nur ein Signal. Da weiß ich jetzt nicht, woher ich die zugehörige Masse nehmen soll, damit der Arduino auch eindeutig sein Bezugspotential hat. Einen zweiten Schmitt-Trigger invertieren und den Ausgang als Masse nehmen?

Vielen Dank im Voraus.

h4m0ut

Du willst die Netzfrequenz messen? Dafür reicht ein Trafo, Gleichrichter und Spannungsteiler, die digitalen Eingänge haben schon eine Hysterese (Schmitt-Trigger).

Mit einem Einweg-Gleichrichter (1 Diode) bekommst Du dann 50Hz, mit einem Brückengleichrichter 100Hz. Die maximale Amplitude ist etwa 1,4*Veff, beim 6V Trafo also ca. 8,5V, abzüglich dem Spannungsabfall am Gleichrichter (1 oder 2 mal 0,7V). Die Leerlaufspannung insbesondere kleiner Trafos ist meist höher als angegeben, könnte bei 8 statt 6 Volt liegen - nachmessen!

Dein Signal muß mindestens 0,7*Vcc erreichen, damit der Eingang triggert, darf aber nicht viel höher als Vcc werden.
Eventuell noch eine Diode vom digitalen Eingang nach Vcc, um Spannungsspitzen abzuleiten, oder 10k zum Signal und auf die eingebauten Clamping-Dioden vertrauen.

Wo Du die Spannung am Spannungsteiler abgreifst, sollte Dir inzwischen nach etwas Nachdenken klar werden. Bei einem Multimeter mußt Du doch auch immer 2 Leitungen anschließen...

h4m0ut:
Meine letzte Frage wäre noch, wie ich dem messenden Arduino Masse geben soll. Ich habe nach dem Schmitt-Trigger nur ein Signal.

Auch dein Schmittrigger hat eine Masse. Die verbindest du mit der des Arduinos.

Merke: Spannung kannst du nur zwischen zwei Punkten messen. Deshalb haben Spannungsmessgeräte auch *zwei * Messspitzen.

Wenn im alltäglichen Sprachgebrauch von "5V" die Rede ist, dann meint man immer die Spannung zum Bezugspotential, also zur Masse (GND).

Kann ich jetzt einen Spannungsteiler verwenden, oder ist das ungeschickt wegen der Lastabhängigkeit des Spannungsteilers?

Was hängst Du an den Spannunsteiler?

Beachte daß die Spitzenspannung 1,41 größer als die Effekttivwert der Spannung ist. ein 6V Trafo hat im Leerlauf eine höhere Spannung und der Maximalwert nach Gleichrichtung und Glättung ist noch höher.

Dein im 1. Post gezeigter Sketch hat einen großen Fehler.

Du schaltest dauernd den Interrupt ein und wieder aus für die Zeit der Ausgabe auf das Display und mit der Frequenz des loop()-Durchlaufs. Da kannst Du keine genaue Messungen zusammenbringen.

Außerdem was ist das delta? Das Quadrat der Frequenz mit einem Korrekturfaktor ergibt keinen Sinn.

Viele Grüße Uwe

Guten und Tag und vielen Dank,

okay, wenn ich den Schmitt-Trigger nicht brauche, dann lasse ich ihn weg.

Ich dachte, dass der Arduino vielleicht Schwankungen hat und daher den Wert des Spannungsteilers verändern würde.

Für die Variante mit Schmitt-Trigger dann vorm Schmitt-Trigger Masse nehmen.

Ja, die Wurzel(2) * U_{eff} muss ich berücksichtigen.

Ich werden dann den Interrupt anlassen. Meine nur, irgendwo gelesen zu haben, dass das eventuell ein paar Probleme verursachen kann, wenn der Interrupt eingeschaltet ist während der Ausgabe auf das LCD.

Der Wert für delta wird später ein Fehlerwert der Frequenz. Dieser Fehlerwert resultiert dann aus den verwendeten Bauteilen, Schaltzeiten usw.. Momentan steht da einfacah nur was drin, damit etwas auf dem LCD steht :).

Vielen Dank für die Antworten :).

Mir ist das einsam und alleine stehende "period;" in loop() aufgefallen...
Welch einen Sinn hat das da?

Eine Quelle von Fehlmessungen:
Das Auslesen von period sollte/muss ATOMIC gemacht werden.

Welche Auflösung ist wichtig?
Reicht es nicht, die Impulse pro Sekunde zu zählen?
(ist evtl. einfacher)

Ansonsten gibts ja meist auch noch min, einen Counter, welcher externe Signale zählen kann.
Ganz ohne Interrupts...

Nulldurchgänge der Netzspannung kann man auch mit einem Optokoppler übertragen.
http://www.dextrel.net/diyzerocrosser.htm

Ich denke dein Grundproblem ist, dass der Interrupt fast nie aktiv ist.

du machst attachInterrupt, dann werden drei kurze Befehle ausgeführt, dann wird für die (lange dauernde) Ausgabe wieder detached.

ich würde es anders lösen:
erst mehrere Pulse (z.B. 8 ) sammeln (z.B. in ein Array schreiben, oder aufaddieren).
Erst wenn 8 Pulse gezählt wurden, dann die Auswertung starten.

das könnte z.B. so aussehen:

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


const int Pmax = 8;
volatile unsigned long timestamp[Pmax+1];
volatile int Pcount = 0;

void setup()
{
  Serial.begin(9600);
  lcd.begin(16, 2);
}

void loop()
{
  
  if (Pcount > Pmax){  // erst, wenn 8 Pulse gezählt sind, Auswertung starten.
    detachInterrupt(2);
    unsigned long period = 0;
    for (int i=0; i>Pmax-1;i++){   // aussummieren der einzelnen Pulszeiten
      period += timestamp[i] - timestamp[i+1];  // hier könnte man auch noch eine überprüfung auf sinnvolle Werte machen
    }
    period = period / 7;    // Mittelwert bilden
    Pcount = 0;
  
    float f=1000000.0/(float)period;
    float delta = f*f*0.002;
    lcd.setCursor(0,0);
    lcd.print("Freq=");
    lcd.setCursor(5,0);
    lcd.print(f, 4);
    lcd.setCursor(14,0);
    lcd.print("Hz");
    lcd.setCursor(0,1);
    lcd.print("d_f=");
    lcd.setCursor(5,1);
    lcd.print(delta,3);
    lcd.setCursor(13,1);
    lcd.print("mHz"); 
    Serial.println(period);  
    attachInterrupt(2, Frequenz, RISING);
  } // end if Pulseauswertung
}// end loop()


void Frequenz(){
  timestamp[Pcount++] = micros();
}

hat auch den Vorteil, dass die ISR kürzer wird.

Ist es nicht einfacher zu zählen wie oft die Interruptroutine aufgerufen wurde und zB beim 8 mal erst die Zeit zu nehmen.

Dann einfach vor der Weiterverarbeitung die Zeit durch 8 teilen.

Grüße Uwe

uwefed:
Ist es nicht einfacher zu zählen wie oft die Interruptroutine aufgerufen wurde und zB beim 8 mal erst die Zeit zu nehmen.

Dann einfach vor der Weiterverarbeitung die Zeit durch 8 teilen.

Grüße Uwe

ja, es ist einfacher.
Aber dann kann ich keine Fehlerbehandlung einbauen. Wenn per Interrupt auf Flanken reagiert wird, besteht immer die Gefahr, dass auch auf Störer getriggert wird. Wenn ich die einzelnen Zeiten kenne, kann ich nicht sinnvolle Zeiten ignorieren.

ok, das ist ein Argoment.
Grüße Uwe

uwefed:
Dein im 1. Post gezeigter Sketch hat einen großen Fehler.

Du schaltest dauernd den Interrupt ein und wieder aus für die Zeit der Ausgabe

guntherb:
Ich denke dein Grundproblem ist, dass der Interrupt fast nie aktiv ist.

Während was zu zählen kommen könnte, muss immer die ISR attached sein, sonst macht alles keinen Sinn.

Die ISR dient dazu, dass parallel LCD Anzeigen und alles andere ablaufen kann, sogar mit delay()
In der ISR wird dann gezählt, und meinetwegen noch eine Plausiprüfung gemacht.

Wenn du keine delays verwendest und nur 50 Hz erkennen willst, kann sich das Zählen natürlich auf 0/1 beschränken, aber einen Grund für detachInterrupt gibt es nicht.