Hallo, Sinn dieses Programms ist es eine Frequenzmessung über das Input Capture Pin durchzuführen... Ich verwende für die Messaufgabe einen Atmega168 mit 20MHz Taktfrequenz. Beim Test des Programmes mit einem Frequenz-Generator erhalte Ich bis ca. 2kHz sehr gute Ergebnisse, d.h. dass die ausgegebene Frequenz auf dem Display mit 1Hz Genauigkeit übereinstimmt. Erhöht man die Frequenz bis 5kHz so muss man feststellen, dass Sie bis zu 3Hz abweichen kann. Bei 10kHz ist die Auflösug schon so schlecht, dass man sich mit einer Auflösung von 10 Hz bis 20 Hz abfinden muss. Meine Frage: kann mir jemand weiterhelfen den Code zu verbessern, oder einen Tip geben, um die Messgenauigkeit zu verbessern? Entweder bei 2kHz Auflösung von 0,1Hz oder bei 20kHz Auflösung von 1Hz? Ist das überhaupt möglich? Hintergrund ist Ich möchte Frequenzen bis 150Mhz messen, die über Teiler bis in den kHz-Bereich herunter geteilt werden sollen. Teilungsfaktor bei 2kHz der Messfrequenz wäre dann nach Adam Ries 75 000 macht bei einer Auflösung von 1Hz, wie es bisher ist 75 kHz Gesamtauflösung. Dies möchte Ich gern verbessern. Danke im vorraus
floats vermeiden Mit float hast du so um die 6 signifikante Ziffern. Bei dir könnten es auch nur 5 sein durch die Division. Das hier
1 | while(1) // Endlosschleife im Main |
2 | {
|
3 | |
4 | for( i=0; i<j; i++) // Messschleife für j Messungen |
5 | {
|
6 | summe += erg; // Summieren der gemessenen T's |
7 | }
|
summiert nicht j einzelne Messungen auf, sondern ein und dieselbe Messung j mal. Dein Mittelwert ist also alles andere als ein Mittelwert von j unabhängigen Messungen. Diese Berechnung erg = (overflows*65536) + endtime - starttime; ist nicht richtig. Die Anzahl der Overflows muss anders eingerechnet werden. Je höher deine Frequenz wird, desto kleiner wird das erg hier erg = (overflows*65536) + endtime - starttime; werden. Je kleiner das erg hier aber wird, desto größere Schritte macht die daraus errechnete Frequenz Ab einer bestimmten Grenzfrequenz ist es daher nicht mehr sinnvoll, die Zeitdauer von einer Flanke zur nächsten zu messen, sondern das Messprinzip umzudrehen: Man zählt zb 1 Sekunde lang, wieviele Flanken im zu messenden Signal vorkommen.
Karl heinz Buchegger schrieb: > for( i=0; i<j; i++) // Messschleife für j Messungen > { > summe += erg; // Summieren der gemessenen T's > } > summiert nicht j einzelne Messungen auf, sondern ein und dieselbe > Messung j mal. Aber nur, wenn nicht mittendrin die ISR ausgeführt wurde, und erg sich verändert. Da das irgendwo in der Berechnung passieren kann, können da auch unsinnige float-Werte entstehen. Oliver
Oliver schrieb: > Aber nur, wenn nicht mittendrin die ISR ausgeführt wurde, und erg sich > verändert. Da das irgendwo in der Berechnung passieren kann, können da > auch unsinnige float-Werte entstehen. Hab ich zuerst auch gedacht. In seinem Fall allerdings nicht. Da ist noch eine Absicherung mit einem Flag drinnen, so dass die ISR erst dann wieder zu messen anfängt, wenn die Hauptschleife bestätigt, dass sie sich das Ergebnis geholt hat - das workFlag. Alles in allem wieder mal: Code geklaut ohne ihn analysiert oder verstanden zu haben. Dafür dann naiv ein paar Variablen auf float gewechselt und dann wundert man sich.
Vielen Dank für die schnellen Antworten ja sorry ist mein erster Versuch mit µC zu Rande zukommen . . . >summiert nicht j einzelne Messungen auf, sondern ein und dieselbe >Messung j mal. Dein Mittelwert ist also alles andere als ein Mittelwert >von j unabhängigen Messungen. Könnte man in der for schleife noch eine if-Verzweigung reinbasteln, die auf eine Abfrage des workflag's wartet, bis sie erg aufaddiert, und anderenfalls die Laufvariable -1 rechnet? Oder hab ich einen Denkfehler? >Ab einer bestimmten Grenzfrequenz ist es daher nicht mehr sinnvoll, die >Zeitdauer von einer Flanke zur nächsten zu messen, sondern das >Messprinzip umzudrehen: Man zählt zb 1 Sekunde lang, wieviele Flanken im >zu messenden Signal vorkommen. D.h. Den Zähler zu einem definierten Zeitpunkt starten(Bei Flankenwechsel) d.h dieser zählt dann von 0 an hoch. Die Überläufe zählen. mit der ISR die Flanken zählen. Nach 1 Sekunde stoppen, Endzeit vom Zähler merken und dann die Berechnung ausführen. Bis zu welcher Frequenz kann man so messen??? Und mit welcher Auflösung? Vielen Dank nochmal werds ausprobieren.... CU
Karl heinz Buchegger schrieb: >> Aber nur, wenn nicht mittendrin die ISR ausgeführt wurde, und erg sich >> verändert. Da das irgendwo in der Berechnung passieren kann, können da >> auch unsinnige float-Werte entstehen. > > Hab ich zuerst auch gedacht. > In seinem Fall allerdings nicht. Zumindest der erste oder gar die ersten paar Werte werden beim ersten Start des Programms 0 sein, bis die ISR den ersten berechneten Wert in erg geschrieben hat. Danach greift dann die Synchronisation über das Flag. Oliver
Hallo, Hab mir mal noch ein paar Gedanken gemacht . . . Danke an Karl Heinz Buchegger, dein Tip hat mir weitergeholfen. Habe den Quelltext jetzt soweit verändert, dass Ich gut und gerne bis 300kHz messen kann :-) wie Karl schrieb, 1s lang messen und dabei Flanken zählen. Hätte mal die Bitte, dass sich jemand mal den Code anschaut und mir sagt wo noch Verbesserungspotential ist??? Wäre cool ! PS: Es werden zwei Ausgaben auf dem Display realisiert, eine ohne und mit Korrektur... In einer möglichen späteren Umsetzung wird nur die mit korrektur verwendet. Gruß Erik
Genauer Wert für die Berechnung von 20Mhz / 1024 wäre ja 19531,25 und wenn man das wiederum abzieht von 2^16 rundet er ja auf 46005. Somit hab Ich ja einen Fehler von 0.25 x 19531.25Hz oder nicht ? ? ? Das sind 12,8µs also verkürzt sich das Messintervall um diese Zeit. statt 1 s tatsächlich 0,9999872s ja hab dann noch 1/0.9999872 gerechnet. Hoffe hab mir das richtig gedacht???
Oder einfacher: den Wert multiplizieren mit 0,9999872! Spart das rechenzeit?
Erik Brachmann schrieb: > Genauer Wert für die Berechnung von 20Mhz / 1024 wäre ja 19531,25 und > wenn man das wiederum abzieht von 2^16 rundet er ja auf 46005. Somit hab > Ich ja einen Fehler von 0.25 x 19531.25Hz oder nicht ? ? ? Deine Berechnung klingt zumindest plausibel. Mal sehen, ob wir auf einem anderen Weg zu gleichen Ergebnis kommen. Du setzt Den Zähler so, dass 19531 Timertakte bis zum Überlauf entstehen. Das sind 19531 * 1024 = 19999744 CPU Takte Da 20000000 ziemlich genau 1 Sekunde entsprechen, sind daher 19999744 Zyklen 20000000 1 19999744 x --------------------- 1 * 19999744 x = ------------------ = 0.9999872 Sekunden 20000000 Sieht daher gut aus. Ob das allerdings viel bringt, wage ich mal zu bezweifeln. Hauptsächlich daher, weil dein Quarz mit einiger Sicherheit keine 20000000 Schwingungen in der Sekunde macht. Das können durchaus +/- 100 bis 150 Schwingungen mehr oder weniger sein. Und damit bist du dann schon in der Nähe deiner Korrektur. Aber ist ok. Das einzige was ich tun würde: Den Zahlenwert nicht direkt in den Code rein, sondern als Formel ausdrücken in Abhängigkeit der CPU Frequenz und Verteiler. Auch bin ich kein besonderer Freund des Vorladens im Interrupt. Will man einem Timer einen limitierten Zählbereich aufzwingen, dann gibt es dafür den CTC-Modus.
Bedanke mich für die sehr schnelle Antwort. Werd mir den CTC - Modus als nächstes vornehmen, danke für den Tip. Mein Ziel ist es Frequenzen so hoch und genau wie öglich zu messen. Bin jetzt bei 300kHz +-1Hz. Das ist eigentlich schon gut. Bei meiner Frequenz die Ich messen möchte liegt maximal bei 150MHz, müsste Ich da nur noch 500 runterteilen. Wie Hoch kann Ich maximal messen. Ich denke da ist jetzt bald Schluss, wenn Ich noch andere Aufgaben mit dem Controller machen will, sonst bearbeitet er ja nur Interrupts!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.