Hallo Leser, ich möchte gerne einen kleinen Frequenzzähler programmieren der folgenden Ablauf hat. Die Flanken sollen über T3 eines ATmega128 gezählt werden... - interne/externe HW Zähler auf 0 - 250ms warten - Frequenz aus Zählerstände errechnen Das ganze funktioniert in soweit das ich nun konstante Ergebnisse erhalte, allerdings mit einer Differenz die ich nicht erklären kann, weil sie nicht proportional zur Frequenz ist. Statt 1 000 000 Hz werden 1 002 536 Hz, statt 10 000 000 Hz werden 10 025 184 Hz, statt 20 000 000 Hz werden 20 033 968 Hz angezeigt. Ich vermute nun das das "Tor" wo der T3 zählt nicht exakt für die vorgesehene Messzeit von 250ms öffnet. Wie macht man so etwas? _delay_ms(250) verwende ich nun, da meine Lösung über den Timer0, der alle 10ms aufgerufen wird und bis 25 eine Variable hoch zählt, stark schwankende Ergebnisse erzielt. Wie kann man möglichst genau das Zeitfenster von 250ms erreichen? Es sind noch weitere Timer aktiv, einer für HW PWM ein weiterer für Zeit unkritischer Aktionen sowie Tastenabfragen, ein UART läuft auch noch... Vielen Dank für jeden Tip, schöne Grüße AVRli...
:
Verschoben durch Moderator
Nunja, _delay_ms() wird durch Interrupts unterbrochen, d.h. jeder Interrupt verlängert das Delay um seine Laufzeit. Vielleicht hat es damit zu tun. Du kannst ja mal zum Testen während der Messung die Interrupts sperren.
Ich würde eher in einem Timerinterrupt nutzen im mir eine Torzeit zu erzeugen. Hast du sonst noch interrupts die irgendwie dazwischen funken? Wenn man es genau machen will, deaktiviert man alle störquellen und misst dann ganz in Ruhe.
AVRli schrieb: > Ich vermute nun das das "Tor" wo der T3 zählt nicht exakt für die > vorgesehene Messzeit von 250ms öffnet. Wie macht man so etwas? Mit einem Timer. > _delay_ms(250) verwende ich nun, du treibst den Teufel mit dem Belzebub aus. > da meine Lösung über den Timer0, der > alle 10ms aufgerufen wird und bis 25 eine Variable hoch zählt, stark > schwankende Ergebnisse erzielt. Da hast du dann irgendwas falsch gemacht. Wenn es um exakte Timings geht, dann führt kein Weg an Quarz und Timer vorbei. delay_ms ist dagegen eine Sanduhr, bei der der Sand nass geworden ist. > Wie kann man möglichst genau das Zeitfenster von 250ms erreichen? Zurück an den Start. Alles noch mal auf 'Timer als Torsteuerung' umbauen. > Es sind noch weitere Timer aktiv, einer für HW PWM ein weiterer für Zeit > unkritischer Aktionen sowie Tastenabfragen, ein UART läuft auch noch... Und hoffentlich alle so programmiert, dass sie keine Zeit vertrödeln und nur ganz kurz die Sachen erledigen, die es zu erledigen gilt. > Vielen Dank für jeden Tip, Baus auf Timer zurück und wenn du nicht klar kommst poste dein Programm. Alles andere ist Kaffeesatzleserei und bringt dich nicht weiter. Und PS: Wie meistens ist der Terminus "warten" in µC-Programmen eine gefährlichs Sache. Denn der Ablauf sollte nicht so sein > - interne/externe HW Zähler auf 0 > - 250ms warten > - Frequenz aus Zählerstände errechnen Du hast einen Timer laufen. Der ruft regelmässig eine ISR auf. Nach jeweils 10 Aufrufen ist deine Torzeit um. D.h. deine Strategie muss so aussehen: In der ISR ist es der 10.te Aufruf? wenn ja die aufgelaufenen Zählwerte sichern alles auf 0 zurückstellen d.h. das 'Warten' wird in der ISR erledigt, in dem die ISR mitzählt um den wievielten Aufruf es sich handelt und nach dem jeweils 10.ten Aufruf (denn dann sind 250ms um) die gemessenen Werte aus den Zählern sichert und sie der Frequenzberechnung zuführt. Auf die Art hast du die Sicherheit, dass sich das Tor auch wirklich immer vom Timer-Stand 0 bis zum Timer-Stand 0 10 ISR Aufrufe später öffnet. Es kann höchstens eine kleine Verzögerung geben, wenn gerade eine andere ISR läuft wenn eigentlich die Frequenz-Timer-ISR laufen sollte, da du die aber kurz gehalten hast, bewegt sich der Zeitfehler da im kleinen µs-Bereich. Also weit weg von deinen 250ms. Die 250ms sind dann so exakt, wie es im µC nur möglich ist, auf jeden Fall um Größenordnungen exakter als mit einem _delay_ms 'Warten' in der µC-Programmierung ist meistens eher ein Ich stell mir den Wecker so ein, dass er alle halbe Stunde klingelt. Um 6 Stunden abzumessen, zähle ich einfach wie oft er geklingelt hat. Nach 12 mal klingeln sind 6 Stunden um. Das gibt mir die Freiheit, dass ich in der Zwischenzeit tun und lassen kann was ich will und nicht ständig den Sekundenzeiger beobachten muss. Das muss (im Prinzip) deine Denkweise sein, wenn du irgendwelche Zeitsachen mit einem µC machst. Es läuft so gut wie immer auf ein derartiges oder ein ähnliches Schema raus. Am besten vergisst du fürs erste lieber wieder, dass es eine Funktion _delay_ms gibt. Man kann sie manchmal brauchen, aber es ist nur ganz ganz selten eine gute Lösung. Vor allen Dingen dann, wenn es um längere Zeiten (ich sach mal: alles über 500µs aufwärts) geht. Die Berechtigung für _delay_ms leitet sich IMHO hauptsächlich daher, weil jeder Neuling irgendwo mal anfangen muss und man ihn nicht gleich ins kalte Wasser stossen kann, so dass die Wellen (wie man die Dinge richtig macht) über ihm zusammenschlagen. Daher werden die ersten Lauflichter mit _delay_ms gemacht, auch wenn das aus der Sicht von 2 Monaten später ziemlicher Schwachsinn ist. Aber: an irgendeiner Ecke muss man anfangen zu programmieren und man kann nicht alles gleichzeitig lernen.
Eine Torzeit für einene externen Zähler erzeugt man mit einen Timer/Counter1 Output Compare Match A Output (OC1A). Damit hat die Interruptlatenz keinen Einfluß. Allerding hat die Torzeitmethode einen großen Nachteil, sie wird bei kleinen Frequenzen sehr ungenau. Daher ist die übliche Methode, die Zeit für n Perioden der Eingangsfrequenz zu messen und daraus den Wert zu berechnen. Speziell dazu gibt es den Timer/Counter1 Input Capture Input (ICP1). Damit ist der Timestamp unabhängig von der Interruptlatenz. Die Frequenz muß aber so klein sein, daß man innerhalb einer Periode den Timestamp auslesen kann. Bei hohen Frequenzen schaltet man daher einen festen Vorteiler zu, z.B. 1:256. Der Vorteiler beeinflußt nicht die Genauigkeit.
AVRli schrieb: > ich möchte gerne einen kleinen Frequenzzähler programmieren der > folgenden Ablauf hat. Die Flanken sollen über T3 eines ATmega128 gezählt > werden... Ohne jetzt ins Datenblatt zu sehen und zu hinterfragen, ob es sinnvoll ist, es so zu machen: zähle Deine Impulse mit T3 und erzeuge das genaue Timing der 250ms über T1. T1 erzeugt nach Ablauf der Zeit einen Ausgangsimpuls (output-compare Funktion). Mit diesem wird der input-capture von T3 aktiviert. Suche nach "reziproker Frequenzzähler", wenn Du eine 'vernünftige' Lösung brauchst :-)
B e r n d W. schrieb: > Läuft der AVR überhaupt mit einem Quarz? Der interne RC-Oszillator ist > bei weitem zu ungenau. Er hat doch gar keine Angaben über die gewünschte Genauigkeit gemacht.
Jörg Wunsch schrieb: > Er hat doch gar keine Angaben über die gewünschte Genauigkeit gemacht. Nein aber gefragt: AVRli schrieb: > Das ganze funktioniert in soweit das ich nun konstante Ergebnisse > erhalte, allerdings mit einer Differenz die ich nicht erklären kann, > weil sie nicht proportional zur Frequenz ist. > > Statt 1 000 000 Hz werden 1 002 536 Hz, > statt 10 000 000 Hz werden 10 025 184 Hz, > statt 20 000 000 Hz werden 20 033 968 Hz angezeigt. Er hat also einen Fehler von 0,2 bis 0,3 Prozent, die durchaus am internen Osz. hängen könnte. Wahrscheinlicher aber an seiner delay Geschichte. Richtig ist wie Peter schon geschrieben hat Peter Dannegger schrieb: > Eine Torzeit für einene externen Zähler erzeugt man mit einen > Timer/Counter1 Output Compare Match A Output (OC1A). Damit hat die > Interruptlatenz keinen Einfluß. Ich würde zunächst versuchen über eine kurze Torzeit Impulse zu zählen und falls es zu wenig Impulse sind umschalten auf Periodendauermessung. Der TO kann ja erst mal beides einzeln programmieren und es dann entweder über Schalter oder automatisch umschaltbar machen.
Udo Schmitt schrieb: > Er hat also einen Fehler von 0,2 bis 0,3 Prozent, die durchaus am > internen Osz. hängen könnte. Das ist natürlich richtig, und die Zahlen implizieren, dass er eine Genauigkeit von besser als 1E-3 erwartet (hatte ich vorher nicht gesehen/nicht drüber nachgedacht). Andererseits wäre 1E-6 als Genauigkeit für einen einfachen Quarz auch unrealisitisch. Für den Rest wurden ja genügend Hinweise gegeben.
Jörg Wunsch schrieb: > Andererseits wäre 1E-6 als > Genauigkeit für einen einfachen Quarz auch unrealisitisch. Und wer weiß, wie genau die zum Vergleich verwendete Referenz ist. Wenn man die Grundübungen zur Zeitbasis hinter sich hat, kann man auch gucken, wie andere einen Frequenzzähler bauen, z.B. Beitrag "Frequenzzähler 1Hz - 40MHz" http://www.mikrocontroller.net/articles/Frequenzz%C3%A4hlermodul
Vielen vielen Dank für Eure ausführlichen Antworten! Eine Grippe hat mich lang gesteckt, deshalb konnte ich nicht früher antworten. Takt ist von einem Quarz mit 14,7456 MHz Ich habe auf Timer umgebaut doch leider habe ich nun wieder das Problem das das Anzeigeergebnis nun spring wie verrückt und nicht um eine Stelle sondern +- >100 :-( Zähler (also Flanken) +/- 1-3 denke ich wäre ok Irgendwas mach ich dann wohl noch falsch... :-( Nun habe ich mir mal ein PIN H/L schalten lassen so lange der T0 läuft. Die Zeit ist nicht konstant. von 249,964ms - 249,981ms fINT.bReadGo ist volatile... Die ISR sieht wie folgt aus:
1 | ISR(TIMER0_OVF_vect) { |
2 | static uint8_t swTeilerT0 = 25; |
3 | |
4 | TCNT0 = 255 - 143; |
5 | swTeilerT0--; |
6 | if (swTeilerT0 == 0) { // 250ms |
7 | swTeilerT0 = 25; |
8 | |
9 | TCCR0 &=~ (7 << CS00); // Timer0 CLK OFF |
10 | TIMSK &=~ (1 << TOIE0); // Timer0 OVF INT OFF |
11 | CLR_DEBUG; |
12 | |
13 | TCCR3B &=~ (7 << CS30); // Timer3 CLK OFF |
14 | ETIMSK &=~ (1 << TOIE3); // Timer3 OVF INT ON |
15 | |
16 | fMAIN.bUpdDispQrg = 1; |
17 | fINT.bReadGo = 1; |
18 | }
|
19 | }
|
Im Mainprogramm erfolgt der Start mit...
1 | ...
|
2 | if (fINT.bReadGo) { |
3 | fINT.bReadGo = 0; |
4 | SET_DEBUG; |
5 | TCNT0 = 255 - 143; |
6 | TCCR0 |= (7 << CS00); // Timer0 CLK = CLKcpu / 1024 |
7 | TIMSK |= (1 << TOIE0); // Timer0 OVF INT ON |
8 | }
|
9 | ...
|
Es wird auch nicht besser, wenn man die anderen ISR deaktiviert. IRgendwie komisch... Die Genauigkeit sollte doch schon so zu erreichen sein das die letzte Stelle +/- einen Zähler ausmacht wenn man die gleiche Zeit misst oder? Ich meine damit nicht die Genauigkeit zu Referenz X sondern die der aufeinander fallenden Messungen. Das Ergbnis an sich wird durch Erwärmung usw. wandern aber doch nicht so hin und her hüppeln von Messung zu Messung...? Ist die Timer Sache an sich OK soweit? Gruß AVRli...
AVRli schrieb: > Ist die Timer Sache an sich OK soweit? Natürlich nicht! Wie oben wiederholt ausgeführt wurde, ist T0 ungeeignet und Dein Meßverfahren nicht zeitgemäß.
Mit dem Timer hat man ggf. das Problem, das gerade eine andere ISR (z.B. Timer3.Ovr) aktiv ist. Das kann dann für den Timer0 Interrupt eine Verzögerung von vielleicht 100 Zyklen geben, allerdings nur eher selten. In kleinerer Form gibt es Abweichungen um ein paar Zyklen durch unterschiedlich lange Befehle die unterbrochen werden. Um die beiden Fehler zu umgehen, könnte man z.B. nach 249 ms alle anderen Interrupts, incl. dem Timer3_ovr ausstellen und dann noch mal 1 ms warten. In der Zeit den µC per Sleep in den Standby versetzen - dann ist die Antwortzeit für den Interrupt genau definiert. Auch beim Starten von Timer0 kann ggf. noch was falsch laufen: der Prescaler muss vor dem Start noch zurückgetzt werden, sonst sind da ggf. noch bis zu 1023 Zyken drin. Ein anderes Problem gibt es ggf. mit timer3_Überläufen: Nach dem Stoppen von Timer3 kann immer noch ein Interrupt anliegen, der auch noch gezählt werden muss. Also solle das Ausschalten (der Kommentar ist auch noch falsch) des Interrupts per ETIMSK &=~ (1 << TOIE3); // Timer3 OVF INT ON erst ins Hauptprogramm.
Viktor N. schrieb: > Fuer ein genaues Timing sollte man ein CPLD nehmen.. Ähem..nö. Ein CPLD ist erforderlich, wenn man eine höhere Auflösung und höhere Meßfrequenzen habe will. Das, was es hier in diesem Thread zu lesen gibt, ist mal wieder ne typische Atmel-Fan-Schlaubergerei. AVRli schrieb: > ich möchte gerne einen kleinen Frequenzzähler programmieren der > folgenden Ablauf hat. Die Flanken sollen über T3 eines ATmega128 gezählt > werden... "Die Flanken.." eben. Schon mal der allererste Fehler. Mit einem normalen Mikrocontroller ohne asynchronen Vorteiler kann man keine Flanken zählen, weil alle Signale, die an den Portpins ankommen, auf den systeminternen Takt synchronisiert werden. Das ist ein klassisches Sampling - also erstmal ein bissel Abtast-Theorie sich anlesen. Was soll man auch mit einem Frequenzzähler, der grundsätzlich nicht zwischen echten Signalen und Alias (Aliassen, Alii..?) unterscheiden kann? AVRli schrieb: > - interne/externe HW Zähler auf 0 > - 250ms warten > - Frequenz aus Zählerstände errechnen Im Prinzip ist das ja nicht wirklich falsch, aber es bedarf einer kleinen Ergänzung: Erforderlich sind 2 Zähler, einer für das zu messende Signal und ein zweiter für das Referenzsignal - und (GANZ WICHTIG) beide müssen gleichzeitig oder zumindest mit konstantem Versatz gestartet und gestoppt werden - entweder synchron zum Referenzsignal (das ergibt dann den klassischen Zähler) oder synchron zum Eingangssignal (das ergibt dann den sogen. Reziprokzähler). Dein Ablauf sähe dann etwa so aus: 1. beide Zähler rücksetzen 2. Meßtor öffnen (entweder synchron zum Reftakt oder synchron zum Eingangssignal) 3. so ungefähr 250 ms lang warten (kann auch länger oder kürzer sein) 4. Meßtor wieder schließen (synchron wie bei Pkt.2) 5. beide Zähler auslesen und ins Verhältnis setzen W.S.
So unmöglich ist die Idee nicht den µC zu nutzen um die Torzeit bereitzustellen. Der Timer (in der SW hier Timer0)im µC ist dabei der eine der Zähler, nämlich der für den Ref. Takt. Es gibt nur in der Regel einen besseren Weg. Wenn man die Wahl hat zwischen einem Reziprokzähler und einem klassischen Zähler mit fester Torzeit, dann macht der klassische Zähler nur Sinn, wenn die Signalfrequenz höher als die Taktfrequenz des µC ist - sonst gibt der Reziprokzähler in der Regel die besseren Ergebnisse. Eine höhere Frequenz als die halbe Taktfrequenz kann zumindest der Mega128 nicht direkt zählen und bräuchte entsprechend einen externen Zähler und eine Torschaltung dazu. Die Steuerung des externen Tors kann man dann über einen PWM Pin des µC machen, was die Sache mit dem Timing um einiges vereinfachen würde. Wenn es sein muss, könnte man die Tor Zeit auch per Timer und Interrupt erzeugen, das ist aber nicht so einfach - das eigentliche freischalten des Zählers ist dabei nicht das Problem - das ist auch auch in Software auf den Zyklus genau möglich, nur halt nicht so einfach. Das klassische Zählen mit dem AVR macht eigentlich nur Sinn, wenn es darum geht ohne extra HW eine Frequenz zwischen etwa 1/50 und 1/3 der Taktfrequenz zu erfassen. Drunter ist der Reziprokzähler möglich und besser, drüber geht nicht richtig ohne extra HW.
Ulrich schrieb: > Auch beim Starten von Timer0 kann ggf. noch was falsch laufen: der > Prescaler muss vor dem Start noch zurückgetzt werden, sonst sind da ggf. > noch bis zu 1023 Zyken drin. Bevor ich nun weiter suche warum es springt, würde mich interessieren wie man das anstellt? Reicht ein abschalten der CLK und erneutes einschalten nicht? Gruß AVRli...
Wie es jetzt genau beim Mega128 hab ich nicht nachgesehen, aber bei vielen AVRs (z.B. Mega88, Mega32) reicht es nicht den Takt anzuhalten um den Prescaler zurückzusetzen. Dafür ist dann ein extra Bit in einem weiteren Register vorgesehen.
Ulrich schrieb: > Dafür ist dann ein extra Bit in einem > weiteren Register vorgesehen. Das war's! Vielen Dank, nun läuft die Sache Rund mit Timer ohne _delay_ms und es sind 250ms :-D Prima! Grüße AVRli...
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.