PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Frequenzmessung per ICP



Liquidator
11.08.2013, 12:20
Hallo liebe Gemeinde,

heute wende ich mich mit einem erneuten Problem zu euch :)
Es geht um die Frequenzmessung mit einem ATmega8 per ICP mit Siebensegment-Anzeige.

Der Code ist fertig, aber es sind wohl Fehler oder Optimierungen nötig - mir fällt nichts mehr ein. Das Problem ist, dass die Frequenz zu sehr schwankt, so sehr, dass man die Zahlen kaum erkennen kann, was ja eigentlich nicht passieren darf. Sind hauptsächlich Einer-und Zehnerstellen der 4Segmente beteiligt.
Ist der 16Mhz Quarz wirklich so ungenau?

Danke schonmal.
Lg

markusj
11.08.2013, 17:00
Welche Frequenz misst du? Dein gewählter Ansatz ist sehr präzise bei niedrigen Frequenzen, bei höherfrequenten Signalen bekommst du aber schnell einen mehr oder wenig starken Jitter-Effekt. Dagegen könntest du die gemessene Frequenz noch über einen Mittelwert glätten. Eine andere übliche Lösung besteht darin, die Abtastrate zu reduzieren, also nicht ständig neue Werte aufs Display zu legen sondern nur noch alle 0,2s oder so. Und dann bist du schon fast bei einer Lösung für hochfrequente Signale: Innerhalb eines bestimmten Zeitintervalls Flanken zählen.

Grüße,
Markus

Liquidator
11.08.2013, 18:10
Mein Problem ist, dass ich den o.g. "Jittereffekt" mehr oder weniger schon in meinem Messbereich, 1-2000Hz, bekomme - was eigentlich nicht sein dürfte. Weshalb ich auch von einem Programmierfehler meinerseits ausgehe. Es flackern immer die Zehner-und Einerstellen, manchmal gibt es sogar einen 100Hz-Sprung zwischendurch! Für diese niedrigen Frequenzen habe ich, ehrlich gesagt, mehr Genauigkeit von ICP erwartet. Wo ist der Denkfehler?

Da ich möglichst keinen Offset in der Messung haben möchte, würde ich nur ungerne Torzeiten einsetzen... Ist diese Ungenauigkeit üblich?

MfG,
Nik

P.S: Eine hohe Genauigkeit ohne Schwanken erlebe ich bis zu 80Hz, aber da schwankt es stark.

markusj
11.08.2013, 19:00
Ist diese Ungenauigkeit üblich?

Nein, da stimmt was nicht. Ich versuche Mal deinen Quelltext etwas zu kommentieren:

Zeile 83-90: Welchen Zweck haben HighByte und LowByte?
Davon abgesehen ist es falsch den Overflow-Interrupt vorzuziehen. Entweder gab es bis zum Auftreten des Capture-Ereignisses keinen Überlauf und dann darf z nicht inkrementiert werden, oder es kam zeitgleich oder kurz bevor der Capture-Interrupt abgearbeitet wurde zu einem Überlauf und dann machst du nur einen kleinen Fehler. Ziehst du den Capture dagegen vor machst du einen Fehler von bis zu 2^16.
Zeile 94: Die Flags werden nur gelöscht wenn der zugehörige Interrupt ausgeführt wird. Wenn der Code nicht sowieso falsch wäre, müsstest du das Flag also tatsächlich hier manuell löschen.
Zeile 114: Die Berechnung ICR1 - Startzeit funktioniert nur solange du mit 16-Bit rechnest. So führst du bei einem Überlauf eine ungewollte Subtraktion durch.
Zeile 111, 122-123 : Unnötig

Grüße,
Markus

Besserwessi
11.08.2013, 19:20
Auch wenn der gemessene Wert nur geringfügig schwankt, kann da von auch die 100er Stelle betroffen sein, z.B. zwischen 199 Hz und 200 Hz.


An sich kann ich da keinen Fehler mehr erkennen (wohl aber noch Verbesserungspotential). Bei 2000 Hz. hat man noch etwa 8000 Taktzyklen für eine Periode. Das sollte eigentlich noch für 1 Hz Auflösung reichen.

Die Frage ist auch etwas wo kommt das gemessene Signal her, und wie ist die Wandlung in ein Digitales Signal. Auch da können noch Störungen (z.B. 50 Hz) reinkommen und ggf. einen Jitter bzw. eine scheinbare Frequenzmodulation erzeugen.

- - - Aktualisiert - - -

Das selten Vorziehen des Timer overflow interrupts hat schon seine Berechtigung:

Der ICP Interrupt hat die höhere Priorität (d.h. wird bei gleichzeitig gesetzten Flags zuerst ausgeführt). Es kann also in eher seltenen Fällen vorkommen, dass der ICP Interrupt aufgerufen wird, obwohl der Overflow Interrupt eigentlich vorher dran wäre. Dieser Fall lässt sich daran erkennen, dass das der Overflow interrupt wartet (TOV1-Flag gestetzt) und gleichzeitig der ICP-Wert klein ist (weil die Flanke so kurz nach dem Overflow kam das keine Zeit für die Ausführung war).

markusj
11.08.2013, 19:42
Das selten Vorziehen des Timer overflow interrupts hat schon seine Berechtigung:

Schon klar, "falsch" war vielleicht etwas übertrieben ausgedrückt, aber: Durch unglückliches Timing kann auch der OVF-Flag gesetzt werden nachdem das Capture-Ereignis eingetreten ist. Wenn dann der Overflow vorgezogen wird, kommt es zu einem Fehler in der Größenordnung von 2^16. Der umgekehrte Fall ist weniger Problematisch, die Abweichung durch die höhere Priorität beträgt dann ja nur wenige Takte (Interrupt-Verzögerung).
Es gibt aber mindestens einen Fall in dem du chancenlos bist: Start und Stopp bei TCNT=0. OVF und ICP treten gleichzeitig auf und in dem Fall gibt es einen Fehler von -2^16.

Grüße,
Markus

sternst
11.08.2013, 20:18
aber: Durch unglückliches Timing kann auch der OVF-Flag gesetzt werden nachdem das Capture-Ereignis eingetreten ist. Wenn dann der Overflow vorgezogen wird ...Wird er ja aber nicht, dafür ist schließlich der kleiner-Vergleich da.
OVF gesetzt + ICP ist "klein" -> Overflow vor Capture-Ereignis (mitzählen)
OVF gesetzt + ICP ist "groß" -> Overflow nach Capture-Ereignis (nicht mitzählen)



Der umgekehrte Fall ist weniger Problematisch, die Abweichung durch die höhere Priorität beträgt dann ja nur wenige Takte (Interrupt-Verzögerung).Nein, DANN hat man einen Fehler von 2^16, weil ein Overflow (und damit ein kompletter Timer-Zyklus) nicht mit eingerechnet wird.



Es gibt aber mindestens einen Fall in dem du chancenlos bist: Start und Stopp bei TCNT=0. OVF und ICP treten gleichzeitig auf und in dem Fall gibt es einen Fehler von -2^16.Nein, auch der Fall wird durch das gegebene Schema abgedeckt, weil 0 = "klein".

Besserwessi
11.08.2013, 20:21
Egal wie man es macht, wenn man beim zählen der Überläufe um einen daneben ist, ist der Fehler 2^16 Zyklen. Man sollte es also schon richtig machen - der mögliche Fehler ist zwar selten, aber wenn dann deutlich.
Wie schon richtig erkannt ist das Problem wenn ICP und Überlauf praktisch gleichzeitig auftreten. Das kann beim ICP Wert von 0 passieren, aber ggf. auch bei einem kleinen ICP Wert bis vielleicht 100, durch die paar Zyklen die es braucht eine Befehl zu Ende auszuführen oder was länger dauert den 3. Interrupt von Timer 2 auszuführen. Wenn beide Interrupts anstehen, wird zuerst der ICP Interrupt ausgeführt, auch wenn eigentlich erst der Überlauf dran wäre. Die IF Anweisung fängt genau diese Fälle ab, ohne einen verbleibenden Fehler. Wenn der ICP Wert klein ist, und gleichzeitig noch ein Overlow Interrupt ansteht heißt das, dass die Flanke kurz nach dem Overflow kam der wartende Overflow Interrupt eigentlich vorher hätte kommen müssen. Der Fall mit ICP=0 ist dabei nicht mal ein Sonderfall, sondern fällt auch in das Schema. Bei der etwas unscharfen Bedingung "ICP klein" hat man recht viel Spielraum: ICP und Overflow fast gleichzeitig passiert halt nur bei sehr kleinen ICP Werten (d.h. in der Regel unter etwa 200, oder wie lang Interrupts maximal gesperrt sind) oder bei sehr großen werten halt ICP so um 65000. Die Grenze mit 128 am High-byte ist da relativ beliebig und unkritisch. Der Fall das das Overflow Ereignis nach dem ICP kommt führt dann zu einen sehr großen ICP Wert, dicht an der oberen Grenze. In dem Fall wird dann der Overflow Interrupt auch nicht vorgezogen.

Liquidator
11.08.2013, 22:47
Wow, was für ein Ansturm, vielen Dank für soviel Rückmeldung oO
Fangen wir mal an...


Zeile 83-90: Welchen Zweck haben HighByte und LowByte?
Davon abgesehen ist es falsch den Overflow-Interrupt vorzuziehen. Entweder gab es bis zum Auftreten des Capture-Ereignisses keinen Überlauf und dann darf z nicht inkrementiert werden, oder es kam zeitgleich oder kurz bevor der Capture-Interrupt abgearbeitet wurde zu einem Überlauf und dann machst du nur einen kleinen Fehler. Ziehst du den Capture dagegen vor machst du einen Fehler von bis zu 2^16.


Genau genommen brauche ich zur Überprüfung lediglich das HighByte - doch im Datenblatt stand, dass erst das Lowbyte ausgelesen werden muss, damit das HighByte ins TEMP geschrieben wird. Die ganze Bedingung habe ich mir aus der Wiki abgeschaut, betrifft wohl einen selteneren Fall, dass ein verpasster Interrupt "nachgeholt" wird. Ist die Grenze mit 128 aus dem Beispiel zu klein gesetzt?



Zeile 94: Die Flags werden nur gelöscht wenn der zugehörige Interrupt ausgeführt wird. Wenn der Code nicht sowieso falsch wäre, müsstest du das Flag also tatsächlich hier manuell löschen.


An der Stelle hat das Programm erkannt, dass ein Overflow verpasst wurde - anhand des gesetzten TOV1-Flags und relativ kleiner ICR1H - inkrementiert die Zählvariable und löscht den "bereits ausgeführte" Flag.


Die Berechnung ICR1 - Startzeit funktioniert nur solange du mit 16-Bit rechnest. So führst du bei einem Überlauf eine ungewollte Subtraktion durch.

Das und den 2^16 Fehler verstehe ich leider nicht ganz. Die Differenz wird ihrer Größe wegen in einer unsigned long-Variable gesichert. Wäre es besser wenn ich es in der main() berechne?


Zeile 111, 122-123 : Unnötig

Dürfte stimmen, habe es nur zur Vorsicht getan.

Herrschaften, ich verstehe das Optimierungspotenzial, doch ist mir ersteinmal die Quelle der Ungenauigkeit wichtiger - soll ich beim Vorziehen des Interupts die Grenze nicht auf 128, sondern größer setzen? Wo genau seht ihr jetzt das Problem in dem Code?
Danke nochmal.

MfG,
Nik

Besserwessi
11.08.2013, 23:33
Die Grenze für den Vergleich des Highbytes ist unkritisch. Wenn das Overflow Interrupt Flag gesetzt ist, kommen für das High-Byte vom ICP eigentlich nur Werte von vielleicht 0,1, 254 oder 255 in Frage. Durch eine Verzögerung bei der Interrupt-verarbeitung, also insbesondere durch einen anderen Interrupt oder anderes sperren von Interrupts hat man an beiden Ende ein paar mögliche Werte, aber die Reserve ist schon groß. Die anderen ISRs müssen nur sicher unter etwa 32000 Zyklen bleiben - das ist normalerweise kein nennenswertes Problem, wenn man nicht gerade in der ISR (von timer2) mehrfach Fließkommazahlen benutzt. Die jetzige ISR vom Timer 2 liegt mehr so bei 50-100 Zyklen.

Der Code sieht soweit gut aus, und sollte funktionieren. Den Fehler würde ich eher beim Signal suchen. Um dennoch mögliche Fehler besser zu erkennen wäre es besser nicht gleich in eine Frequenz umzurechnen, sondern erst nur die Zeit auszugeben, ggf. auch nicht auf die LEDs, sondern zum PC schicken.

Liquidator
11.08.2013, 23:57
Das Signal wurde mit einem Oszilloskop untersucht und ist präzise, hier liegt kaum der gesuchte Fehler vor.
Ich schreibe mal die ganze Berechnungsroutine in die main() rein, damit kürzt sich die unsigned long- Variable aus der ISR.

Dann gilt es jetzt die unsigned long Zeit zu analysieren...Was erwartest du da zu sehen? Ich meine, es werden eben größere Sprünge zu sehen sein, doch worauf willst du hinaus?
Bis 2Khz habe ich es mir eigentlich einfacher vorgestellt mit der Messung...

MfG,
Nik

P.S: mal hypothetisch, was könnten für Fehler an einem TTL-Signal +5V auftreten?

markusj
12.08.2013, 00:21
Das und den 2^16 Fehler verstehe ich leider nicht ganz. Die Differenz wird ihrer Größe wegen in einer unsigned long-Variable gesichert. Wäre es besser wenn ich es in der main() berechne?

Vergiss den 2^16-Fehler, da lag ich daneben. Danke Stefan, ich hatte die Logik dahinter nicht ganz durchdrungen.

Was die Subtraktion angeht: Start 0xff00 und Ende 0x00ff entspricht eigentlich einer Zeit von 0x1ff. Wenn du die voneinander subtrahierst bekommst du bei einer 16-Bit-Subtraktion dank abgeschnittener Carry-Bits auch tatsächlich 0x1ff heraus, bei einer 32-Bit-Subtration wäre das Ergebnis aber 0xffff01ff. Lösen lässt sich das Problem, indem du die Subtraktion in Klammern setzt und das Ergebnis auf uint16_t castest.

Grüße,
Markus

sternst
12.08.2013, 05:44
Was die Subtraktion angeht: Start 0xff00 und Ende 0x00ff entspricht eigentlich einer Zeit von 0x1ff. Wenn du die voneinander subtrahierst bekommst du bei einer 16-Bit-Subtraktion dank abgeschnittener Carry-Bits auch tatsächlich 0x1ff heraus, bei einer 32-Bit-Subtration wäre das Ergebnis aber 0xffff01ff. Lösen lässt sich das Problem, indem du die Subtraktion in Klammern setzt und das Ergebnis auf uint16_t castest.Sorry Markus, aber du liegst wieder daneben.
Im Prinzip hast du ja Recht mit der 16-Bit-Arithmetik, aber so einfach ist es nur, wenn die Capture-Länge immer unter einer Timer-Periode liegt, man also keine Overflows mitzählen und einrechnen muss. Wenn man aber mitzählt, dann hat man immer bei Start>Ende einen Overflow gezählt, der nicht hinzugerechnet werden darf. Entweder macht man eine zusätzliche Abfrage und korrigiert die gezählten Overflows ggf, oder man beschränkt das Ende-Start eben NICHT auf 16-Bit, dann fällt dieser zusätzliche Overflow nämlich ganz von alleine raus. Nimm doch einfach mal deine eigenen Beispielwerte und rechne damit die Formel im Code komplett aus, und nicht nur teilweise.

markusj
12.08.2013, 09:54
Sorry Markus, aber du liegst wieder daneben.

Und du hast schon wieder recht, irgendwie war gestern nicht mein Tag ...

mfG,
Markus