Hallo zusammen! Ich versuche über den Input Capture Pin eine Frequenz zu messen. Der Code im Anhang gibt aber unterschiedliche Ergebnisse aus, wobei manchmal auch das richtige dabei ist. Ich benutze einen Atmega16 mit 8MHz intern und Prescaler 256 im CTC 4 Mode. Bei einer Frequenz von 50 Hz an ICP1 lasse ich den Timer bis 3125 laufen, was 100ms entspricht. Somit findet maximal ein Compare Interrupt pro Messung statt. Wo liegt mein Fehler?
1 | |
Ich denke nicht das das stimmt. Wenn du schon darauf aus bist, festzustellen ob eine Overflow (alias Compare) stattgefunden hat, warum nimmst du dann nicht einfach deine Compare Variable dafür her? Deine Berechnung (und auch die Anzeige updaten) solltest du nur dann machen, wenn eine Messung komplett vollständig abgelaufen ist. Setz dir doch einfach eine 2.te globale Variable, wenn du den 2.ten Puls detektierst. In der Hauptschleife fragst du ständig diese Variable ab und nur wenn die anzeigt, dass neue Messwerte vorliegen, wird die Berechnung durchgeführt und die Anzeige erneuert. Jetzt ist es so, dass deine Berechnung manchmal einen alten B-Wert mit einem neuen A-Wert verrechnet.
Also ungefähr so:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
Das hat dann auch den Vorteil, dass du keine Warteschleifen mehr brauchst und die Hauptschleife nur dann durch die teure Berechnung und den LCD Code geht, wenn es auch wirklich was zu tun gibt.
> mit 8MHz intern
sonderlich genau wirds damit aber so und so nicht werden.
Gast
#492308
Seh ich das richtig, Du misst (schreibt man das so, oder mit 'ß' ??) einfacah nur die Zeit zwischen 2 Flanken mit dem Compare Interrupt? Dann begreife ich die Timereinstellung für die 100ms nicht.
Gast
#492312
Ich verstehe nicht ganz, warum Du "compare" nach der zweiten Flanke löschst, noch bevor Du die Messung auswertest. Ich denke, Du benötigst den Wert für die Berechnung. Dann solltest Du ihn erst nach der Berechnung löschen. Ansonsten schließe ich mich Karl Heinz an: Warte zwei Flanken ab, setze ein Jobflag in der Capture-ISR und werte dieses Flag im Hauptprogramm aus. BTW: SIGNAL ist veraltet. Installiere Dir bei Gelegenheit mal eine aktuelle WINAVR-Version bzw. wenn Du das schon getan haben solltest, verwende ISR anstelle von SIGNAL.
Gast
#492338
@Mister mit Kanister: Nein, er misst die Zeit mit dem *Capture*-Interrupt. Der Compare-Interrupt zählt nur 100ms-Blöcke. Das erleichtert hinterher die Umrechnung in Sekunden bzw. in Hz. Man kann aber auch die Overflows zählen. Im Ergebnis kommts aufs gleiche raus, man muss nur ein bisschen mehr mit krummen Zahlen rechnen...
> Ich hab die Änderungen jetzt mal übernommen. Der Wert stimmt > jetzt. Das heißt wärend meiner delay funktion fürs Display > kommen falsche Werte für ICR1 zu stande? Stell dir einfach mal vor, dein ursprüngliches Programm steht zur Zeit gerade in der Berechnung: Erg = ((compare*3125) - ic_zp_A + ic_zp_B); Es rechnet munter dahin compare * 3125 und Zack, jetzt kommt ein Input Capture Interrupt. Dadurch wird ic_zp_A auf einen neuen Wert gesetzt. Der Interrupt ist fertig und es geht zurück zu der Berechnung. Die rechnet jetzt weiter Vorhergehendes Ergebnis der Multiplikation minus ic_zp_A Aber das ist schon der neue Wert für ic_zp_A! Eine neue Messung hat begonnen. Und weiter plus ic_zp_B das ist aber der alte Wert von ic_zp_B, also der der vorhergehenden Messung. Die Werte von ic_zp_A und ic_zp_B haben nichts miteinander zu tun. Und trotzdem werden sie miteinander verrechnet. Da kann nichts vernünftiges rauskommen. Am besten das Display immer ohne delay funtion anschauen? Wie meinst du das genau mit der "compare" Variablen. Muss ich überhaupt schauen ob ein Overflow stattgefunden hat? > Am besten das Display immer ohne delay funtion anschauen? Am besten ohne delay programmieren. Das Schema, das ich dir gezeigt habe ist universell verwendbar. Da brauchts keinen delay. > Muss ich überhaupt schauen ob ein Overflow stattgefunden hat? Solange es unmöglich ist, dass 2 Overflows in einem Messzyklus passieren, brauchst du es nicht berücksichtigen. Übrigens: Johnny hat recht. Das mit dem Compare würde so nicht 100% funktionieren. Selbes Problem gibt es übrigens mit den beiden Timer Messwerten. Wenn der nächste 1-Flanke Interrupt kommt, noch bevor die Hauptschleife die Berechnung zu Ende gebracht hat, hast du wieder eine falsche Anzeige. Man könnte das so umgehen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
Schau dir auch an, was ich mit dem i gemacht habe. Das vermeidet unnötige Berechnungen.
Gast
#492373
Den Overflow brauchst Du ja wirklich nicht. Ich würde einfach die Zeit zwischen 2 Captueres messen. Dann hast Du ja eine Periode. Da bei Dir F_CPU >> F-mess wird das ganze sicher ziemlich genau. Frequenz = F_CPU / Timerticks_zwischen_2_Captueres Ich würde der Übersicht halber bei der Deklaration noch unsigned int verwenden.
Falls aber für den unwahrscheinlichen Fall der Timer bei der 1.Flanke kurz vor OCR1 ist und die zweit Flanke erst nach dem compare Ereignis auftritt, bekomm ich doch falsche Werte? Das ist hier wahrscheinlich selten der Fall, aber wenn zusätzlich kleinere Frequenzen auftreten wird's problematisch, oder?
Gast
#492415
>Falls aber für den unwahrscheinlichen Fall der Timer bei der 1.Flanke >kurz vor OCR1 ist und die zweit Flanke erst nach dem compare Ereignis >auftritt, bekomm ich doch falsche Werte? 1. ICR wird erst bei der nächsten Flanke verändert, solange hat man Zeit, das Register auszulesen. 2. bei einem ICP ohne die Begrenzung durch OCR1 hat man ja den Vorteil, dass der Zahlenraum aufgrund der 16Bit begrenzt ist. Dadurch kommt es bei einer Subtraktion automatisch zum richtigen Ergebnis, solange es nur einen Überlauf gab. In deinem Fall muß zum zweiten Wert OCR1 (oder war es die Differenz zu 0xFFFF?) hinzuaddiert werden, wenn er kleiner als der erste ist. Er befindet sich dann ja im"unsichtbaren" Intervall des Timers. Es müsste OCR1 zu addieren sein...
Es scheint zu funktionieren! Vielen Dank an alle! Was kann ich tun um die Genauigkeit zu erhöhen, denn jetzt habe ich 2Hz Abweichung zu meinem Funktionsgenerator?
Gast
#492490
>2Hz Abweichung Bei welcher Frequenz? Um Karl Heinz zu zitieren: >> mit 8MHz intern >sonderlich genau wirds damit aber so und so nicht werden. Nimm einen externen Quarz. (ich weiß jetzt nicht, ob du dich diesbezüglich schon geäussert hast).
Die 2Hz Abweichung sind bei 50 Hz. Ich muss Signale aufnehmen können (Drehzahlsensor) von (o,5 Umdr. pro Sec.) 360 Hz bis (3000 Umdr. pro Sec.) 36kHz. Bei einem schnelleren Quarz läuf doch der Counter schneller hoch. Warum wird dadurch mein Ergebnis genauer? Oder meinst du keinen schnelleren sondern einen externen da der genauer ist?
Sagen wir mal so. Wenn du mit einer Sanduhr einen 100 Meter Läufer stoppst, wirst du auch jedesmal ein anderes Ergebnis kriegen. Dein 8Mhz interner Takt macht alles Mögliche, nur nicht möglichst exakt 8 Mhz. Und das tut er im Sommer anders als im Winter (da andere Temperatur, Luftdruck, Wasserstand etc.)
> 3000 Umdr. pro Sec
Bist du dir da sicher?
Das sind 180.000,0 U/min
Nicht mal Turbinen drehen so hoch (AFAIK).
Falls das noch nicht klar geworden ist: Im AVR ist kein Quarz. Da sitzt ein normaler R-C Schwingkreis. Der schwingt so lala, wie's ihm gerade gefällt. Für Dinge bei denen es aufs Timing nicht ankommt ist das kein Problem.
Gast
#492548
Du hast ja auch den Prescaler vom Timer auf 256 stehen. Du brauchst keinen schnelleren CPU-Takt, sondern einen schnelleren Timer-Takt. Und den gibts, wenn Du den Prescaler runtersetzt.
Pardon, natürlich 3000 Umd. pro min sind dann aber trotzdem 36kHz am Controller. Also mit Prescaler = 256 (31,25kHz) wird der 16-bit-Timer in 2,098s hochgezählt. Ohne Prescaler mit 8MHz in 8,195ms hochgezählt. Das heißt die Skala ist feiner unterteilt und damit genauer. Dann kann man den Zeitpunkt wenn eine High-Flanke kommt genauer bestimmen. Bei einem 8-bit-Timer sind ist dann die Schrittdauer (bei 8MHz - 125ns pro Schritt) gleichgroß wie bei einem 16-bit-Timer? Also ist der 16bit-Timer nicht genauer? Nur beim 8-bit-Timer hab ich event. mehr overflows? Habt ihr mir ein paar Tipps wie ich für meine Anwendung die sinnvollste Einstellung wähle?
Gast
#492645
> Nur beim 8-bit-Timer hab ich event. mehr overflows?
Jau, beim 8-Bit-Timer haste bei gleicher Prescaler-Einstellung im selben
Zeitraum 256-mal mehr Overflows... Bei Deiner aktuellen Einstellung mit
Prescaler auf 256 wird der Timer alle 32 µs um eins erhöht. Die 32 µs
sind dann gleichzeitig Deine maximale Auflösung, wenn Du immer eine
Periode misst. Bei 50 Hz ist das eine Abweichung von 1,6 Promille
(Hicks!). Welche Einstellung für Deine Anwendung die beste ist, kann man
nur sagen, wenn man weiß, in welchem Bereich die zu messenden Frequenzen
liegen sollen und welche Anforderungen Du generell an die Auflösung
stellst. Dass Du aber mit den o.g. Einstellungen Abweichungen von 4 %
hast, ist für mich nicht ganz nachvollziehbar, es sei denn, in Deinem
Programm sind noch irgendwelche Schwachstellen drin...
> Dass Du aber mit den o.g. Einstellungen Abweichungen von 4 % > hast, ist für mich nicht ganz nachvollziehbar Er benutzt den internen Taktgeber. <4% bei einem RC-Oszillator ist doch nicht so schlecht.
Kalibriert (d. h. bei Tamb = 25 °C und Vcc = 5 V) sollte der < 1 % in der Abweichung liegen.
Gast
#492965
@Karl Heinz:
> Er benutzt den internen Taktgeber.
Upps, hatte ich jetzt übersehen...
Hab jetzt einen externen 4MHz Quarz in Betrieb und umgestellt auf den Overflow Interrupt ohne Prescaler. Das liefert mir ziemlich exakte Werte für hohe Frequenzen (5kHz, danach macht das Display nicht mehr mit), alles was aber kleiner als ca. 62 Hz ist, wird nicht mehr erkannt. Ist da noch ein Fehler im Code, z.B. wird die overflow Variable richtig gezählt. Bei so niedrigen Frequenzen finden ja mehrere Überläufe statt.
1 | |
2 | |
zw_Erg wird beim ersten Overflow Überlaufen. In einen unsigned int passen keine höheren Zahlen als 65536. Mach da mal unsigned long draus. Lass dir auch mal zw_Erg ausgeben. Bei der Berechnung von Erg dividierst du relativ grosse Zahlen durch sehr kleine Zahlen in float -> nicht gut. Rechne es lieber so: Erg = 4000000 / zw_Erg; (Ich hab einfach nur den Doppelbruch aufgelöst).
Karl heinz Buchegger wrote:
>1 | |
2 | |
3 | |
> > zw_Erg wird beim ersten Overflow Überlaufen. > In einen unsigned int passen keine höheren Zahlen > als 65536. Mach da mal unsigned long draus. Da wollte ich ja noch was dazu schreiben. Bin unterbrochen worden. Damit unsigned long aber auch greift, muss klarerweise auch die Multiplikation unsigned long sein:
1 | |
Wenn ich zw_Erg bei niedrigen Frequenzen anzeigen lasse ist es negativ!? Erg hingegen stimmt!? Warum den das? Ab einer Freqeunz von 32kHz wird auch nichts sinnvolles mehr angezeigt. Da ist zw_Erg dann auf einem sehr kleinen Wert ~120, könnte es sein dass dann einfach nicht mehr geht?
> Wenn ich zw_Erg bei niedrigen Frequenzen anzeigen lasse ist es negativ!? > Erg hingegen stimmt!? Warum den das? Weil du die falsche Ausgabefunktion benutzt. Eine unsigned Zahl kann per Definition nicht negativ sein. Wenn du allerdings das Bitmuster in eine Funktion stopfst, die von signed ausgeht dann interpretiert diese eine sehr grosse Zahl als negativ, weil das am weitesten links stehende Bit als Vorzeichenbit auffasst. Ist es gesetzt, dann ist die Zahl negativ. > Da ist zw_Erg dann auf einem sehr kleinen Wert ~120, könnte es sein > dass dann einfach nicht mehr geht? Nein. 4000000 dividiert durch ~120 ergibt immer noch was sinnvolles. Da die Grenze die du nennst ungefähr 32kHz ist, wirst du wieder dasselbe Problem haben. Beschäftige dich mal damimt, welche Zahlenbereiche die einzelnen Datentypen abdecken können: int -32768 bis 32767 unsigned int 0 bis 65536 long -2147483648 bis 2147483648 unsigned long 0 bis 4294967296 Wann immer du in einer Berechnung über diese Grenzen drüber kommst, bist du in Schwierigkeiten. Auch bei Zwischenergebnissen, auch dann wenn du die falsche Ausgabefunktion nimmst. Und float ist oft keine gute Wahl. Besonders dann, wenn in einer Berechnung viele Größenordnungen Unterschied zwischen den beteiligten Zahlen herrschen.
Hab deine Tipps umgesetzt. Unter anderem "ultoa" anstelle von "itoa", volatile vor "compare" ist wahrscheinlich auch besser und aus den int variablen hab ich unsigned long gemacht, siehe Anhang. Wenn ich aber kein delay_ms() vor mein clearLcd() schreib kann ich das Display nicht ablesen. Muss das so sein, oder kann man das umgehen. Ansonsten stimmen die Werte. Vielen Dank für die Geduld, wenns wirklich läuft stell ich's dann in die Codesammlung.
Gehs noch mal in Gedanken durch: Wann musst du das Display löschen (wenns schon unbedingt löschen sein muss). Doch wohl nur dann, wenn ein neuer Wert ausgegeben werden soll. Dann wäre es aber gut, wenn die Reihenfolge wäre * zuerst Display löschen * dann neuen Wert hinschreiben und nicht umgekehrt Studier noch mal mein Posting vom 6.2 13:53 und schau dir dort die Reihenfolge an: Zuerst wird der neue anzuzeigende Wert komplett (wenn auch falsch) berechnet und als String zurecht gelegt. Das Display zeigt in dieser Zeit immer noch den alten Wert an. Dann wird das Display gelöscht, der Cursor positioniert und der neu errechnete Wert hingeschrieben. Auf diese Weise ist die Zeit in der das Display nichts anzeigt auf ein Minimum gedrückt. Und da das ganze nur dann passiert, wenn (dank UpdateDisplay) ein neuer Messwert vorliegt, flackert das Display auch nur ganz kurz wenn ein neuer Wert auf das Display gezaubert wird. Das geht aber so schnell, dass man es kaum sehen wird.
So jetzt passt's. Schwere Geburt. Vielen Dank!
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.