Hallo, Hab die Suchfunktion schon benutzt, konnt aber bisher nichts passendes zu meinem Problem finden. mich quält folgendes: Ich muss eine Frequenz am AVR messen. Zu einem zufälligen Zeitpunkt kommt ein Frequenzburst mit einer Frequenz von ca. 500kHz und ca. 50µs Dauer an dem AVR an. Das Problem ist zusätzlich noch, dass die Dauer nicht genau 50µs ist, das kann variieren. Das Signal liegt als TTL Pegel vor. Ich betreibe einen Atmega8 mit 4 MHz. Programmiern tu ich mit GCC. Jetzt meine Frage, wie kann man dieses Signal am genauesten/exaktesten messen? Ich stelle mir das so vor: Kommt ein Flankenwechsel an z.B. bei INT0 wird ein Interrupt ausgelöst. Der Interrupt Startet den Timer und zählt ein Signalwechsel. Pro Periode wird dann ein Interrupt ausgelöst und der Zähler um 1 erhöht. Dann muss der AVR erkennen wenn kein Signal mehr anliegt und den Timer stoppen und den Zählerstand und Timerstand in die Frequenz umrechnen. Hat jemand eine bessere Idee oder ein paar Vorschläge? Ich will nicht um ein fertiges Programm betteln, ein guter Lösungsansatz oder Bestätigung wäre schon sehr hilfreich. Besten Dank!
Hallo, sehr genau wird das nicht. Wenn Du, sagen wir mal mindestens zehn Impulse hast (20µs), dann miss die Dauer dieses Bursts, also zehn Impulse und lasse in dieser Zeit einen Timer mit vollem Takt mitlaufen. Der Rest ist Mathematik. Für das nötige 1/x wäre eine MathLIB sinnvoll. Sehr genau wird das aber auch nicht. Genauer wird es, wenn Du mehr Perioden hast und den AVR- und damit den Timertakt so hoch wie möglich kriegst. AVRs machen z.Z. max. 20MHz. Viel Spaß Lothar
Was genau willst Du denn messen ? Die Dauer des Paketes ? eine einzelne Periode ? jede einzelne Periode ? Wie hoch muß die Auflösung sein ? Wie auch immer, besorge Dir schon einmal einen Quarz mit 16MHz.
500kHz und 50 usec wären 25 Perioden. Wenn wir die Dauer von z.B. 20 Perioden messen und dann den Kehrwert/20 bilden, fallen nur Impulspakete <20 aus.
Hi, also wegen der Genauigkeit: Wenn ich sagen wir mal günstig angenommen 20 Impulse einfange, dann wäre meine Messungenauigkeit +/- ein Zählerstand im Timer, vorausgesetzt das Timing stimmt und der Code ist effektiv. Ein Zählschritt @ 4 MHz dauert ca. 0,25µs. Bei also angenommen 40µs Signal mit 20 Impulsen wäre das dann ca. 0,625% Messungenauikgeit. Also bei meinem Signal würde er im schlimmsten Fall 500kHz +/- 3kHz anzeigen. Das würde mir an Genauigkeit reichen. Macht es überhaupt Sinn den Flankenwechsel per Interrupt auszuwerten? Ich bräuchte einerseits ein Interrupt der meinen Timer Startet und dann ein Interrupt für das Hochzählen. Mein Signal kann ja aber nur ein ext. Interrupt auslösen.
@ Michael: Messen will ich nicht die Burstfrequenz, sondern die Signalfrequenz. Je höher die Auflösung desto besser.
Mit einer höheren Zählfrequenz wirds genauer. Ein externer 16er-Teiler mit einem Takt von bis zu 160 MHz, 74AC161 o.ä. könnte an einem 20 MHz-AVR funktionieren. Start und Stop dieses Zählers ( Clock enable oder externes UND-Gatter) werden von der ersten Pulspaketflanke und der Flanke des 20. Impulses abgeleitet. Anschließend hat der AVR Zeit, die 4 Bit des externen Zählers und seiner internen folgenden Zählstufen in die Frequenz umzurechnen.
Die beste Lösung wäre mit ICP1 von Timer 1 zu erreichen. Nach dem 1. Interrupt bleibt man in der ISR und arbeitet weitere Flanken ab, bis die erwartete Pulsdauer überschritten wird (timeout). (Dickere CPUs würden T1-Werte nebenbei einfach per DMA ablegen.) Um den 1. Interrupt nicht zu verpassen, kann man den AVR schlafen legen und durch diesen Interrupt aufwecken, sodaß die Reaktionszeit minimal wird. Nimm aber bitte 16MHz :-)
Also das mit dem CPU Takt das werde ich mir zu Herzen nehmen. Einen externen Zähler denke ich brauche ich nicht. Trotzdem Danke für den Tip, Christoph. Ich hab mal hier ein Lösungsansatz der mir INT0 Interrupt funktionieren sollte. Hab das ganze nur mal rudimentär zusammengeschustert. @ Michael: Taktfrequenz wird noch erhöhrt. Das mit dem ICP1 gefällt mir ganz gut, dann könnte ich denke ich den Timer0 zum zählen, und den Timer1 als Timout verwenden, dann könnte ich automatisch die maximale Länge des Burstes erfassen und mein Ergebnis so genau wie möglich halten. Was meinst Du aber mit schlafen legen? Brauch ich doch nicht?!? Hier erstmal die 1. Version: #includes_und_defines_usw ISR(INT0_vect) // Flankenwechsel an INT0 { if (TCCR1B != 0x01) // Falls Timer nicht gestartet dann starten TCCR1B = 0x01; // starte Timer 1 (clk = F_CPU) freqcounter++; // eine Flanke hochzählen } int main(void) { uint16_t freqcounter=0; uint16_t frequenz; sei(); // Interrupts Enable MCUCR |= (1 << INT0) | (1 << ISC01) | (1 << ISC00); // Interrupt Freigeben für INT0, rising edge while(1) { if (freqcounter == 20) { TCCR1B = 0x00; // stoppe Timer MCUCR &= ~(1 << INT0); // Interrupt INT0 Disable, damit Zählerstand nicht mehr erhöht wird frequenz = (uint16_t)(20/( (long)TCNT1/(long)F_CPU) ); // berechne Frequenz in kHz } // Ausgabe Frequenz auf z.B. Display } return 0; }
Ich hab mal der Einfachheit wegen ein Frequenzgenerator angeschlossen. Bei 100kHz funktioniert es ohne Probleme mit der MEssung, doch bereits ab 150kHz stimmt der Wert nicht mehr. Kann es sein, dass die ISR zu viele Taktzyklen belegt und somit der nächste Flankenwechsel/Interrupt zu "früh" eintrifft, bevor die alte ISR abgearbeitet ist?
Schön, daß Du Dein Programm selbst getestet hast; fertige Lösungen wolltest Du ja nicht haben, und auf die würde es hinauslaufen, wenn jemand die Funktion Deiner Zeilen bestätigen soll. C-Programmierung geht hier nicht, es muß ASM sein ! Dabei ist es geschickt, möglichst alle Variablen in Registern zu halten und nur Daten zu erfassen. Die Auswertung kommt dann später, wenn alle Impulse erkannt sind. Und wenn man ganz schnell auf INTs reagieren muß, kann es nützlich sein, die notwendigen Register zu retten, dann den Prozessor schlafen zu legen, und beim Aufwachen per INT gleich mit der Verarbeitung einzusetzen, ohne PUSHs oder POPs.
Hi Michael, das mit fertiger Lösung ist in der Tat so. Bevor ich mir ne fertige Lösung anschaue versuch ich selbst darauf zu kommen. Ich denke das macht einen guten Programmierer aus. Wenn man sich intensiv mit den Problemen auseinandersetzt bekommt man mit der Zeit das richtige "Gespür". Abtippen kann jeder und das Programm funktioniert wohlmöglich auch, aber lernen tut man da nur begrenzt. Es war schon so gemeint dass ich gerne Hilfe annehme. Wenn dabei mein Programm komplett umgestrickt wird dann hab ich da auch nichts dagegen. Nur den 1. Ansatz den wollte ich halt selbst machen. Wenn jemand eine Idee oder Verbesserung dazu hat gerne her damit. Bin darüber auch sehr dankbar. Mit ASM hab ich mich noch nicht auseinandergesetzt. Ich denke das müsste doch auch mit effizientem C-Code zu machen sein!?!? Mittlerweile hab ich eine 2. Version "entwickelt": Timer0 wird mit dem externen Signal getaktet. Timer1 fängt an die CPU Takte zu zählen. Wenn Timer0 auf z.B. 50 steht wird der Timer1 - Stand anhand von den 50 Impulsen in die Frequenz umgerechnet. Der Vorteil ist, dass der Zähler automatisch hochzählt und ich mir die ISR sparen kann. Funktioniert bis 400kHz gut, darüber hinaus versagt die Abfrage ob schon 50 Impulse eingegangen sind. Mal eine prinzipielle Frage: Wie ist die theoretisch messbare Frequenz wenn man DIREKT auf ein Eingangspin am Atmel geht? Habe mal was von halber CPU Frequenz gelesen...
>Ich denke das macht einen guten Programmierer aus. Ein guter Programmierer versteht auch bzw. kann analysieren und bewerten, was andere Leute ihm "vorsetzen". >Wenn man sich intensiv mit den Problemen >auseinandersetzt bekommt man mit der Zeit das richtige "Gespür". ... oder manverrent sich in etwas völlig sinnfreies... >Habe mal was von halber CPU Frequenz gelesen... Das dürfte auch per ICP noch nicht gehen. Zumindest kann per OC die halbe Prozessorfrequenz ausgegeben werden.
Ich hab ja geschrieben dass ich mir gerne helfen lasse. Ich wollte nur nicht Anfangs hergehen und sagen: "Leute, ich will ne Frequenz messen, gebt mal bitte alles an Programmen und Vorschlägen was Ihr habt" Wie gesagt ich lass mein Programm gern verbessern oder greife auch auf ein anderes Konzept zurück.
wie siehts denn mit deinen messleitungen aus? der schwachsinn kann auch leicht durch fremdeinflüsse kommen... Prescaler richtig eingestellt? Höhere Frequenz zum messen benutzen. ASM programmieren. Es kann auch sein, wo ich gerade im Datasheet lese: welchen timer benutzt du? beim timer 1 kann das problem darin bestehen, das er 16-bit hat - der µC braucht zum lesen und ins register schreiben 2x so lang als beim 8-bit timer... vllt. geht es sich ja mit den zeiten so aus, das du den 8-bit timer verwendest... vllt. hat ich geholfen... jÜrgen
wie ich nochmals nachgelesen habe, das du bei deinem zweiten versuch timer0 mit einem externen clock ansprichst, hab ich mal im DS nachgelesen und gesehen, das hier max. f/2,5 empfohlen sind... hast du das eingehalten?
Als Timeout empfiehlt sich auch der Watchdog, dann muss kein zusätzlicher Timer verbraten werden. Ist bei dem ankommenden Burst sichergestellt dass das Potential am Anfang und Ende gleich sind? Oder ist das z.B. am Anfang Low und am Ende High, dann solltest du die Flanken zählen, den Abstand zwischen den Flanken messen und in einer Variable kumulieren, dann kannst du die Burstdauer, die Anzahl Impulse und die Frequenz des Bursts ausrechnen.
Also ich habe mein Atmega8L @ 8 MHz laufen. Die Messleitungen dürften, soweit ich das beurteilen kann, sauber sein. Bei dem Singal ist am Anfang LOW und am Ende auch LOW. Die eingehende Frequenz ist mal ca. 1 MHz zum Testen. Der Prescaler ist 1, läuft also mit vollem CPU Takt für max. Genauigkeit. Timer 0 Zählt meine Impulse, Timer 1 misst die Zeit die vergeht. Nach 50 Impulsen sollte die Messung stoppen. Hab hier mal meine Funktion angehängt. Das Problem ist, bei höheren Frequenzen stoppt der Timer nicht nach 50 Impulsen, sondern erst nach z.B. 63 Impulsen die Messung. Ich kann kaum glauben dass die IF Abfrage und das Stoppen des Timers so lange Dauert dass mein Signal << F_CPU noch über 10 Perioden lang anliegt. void frequenzmessung(void) { uint16_t frequenz; TCNT0 = 0; TCNT1 = 0; // Timerstände resetten // External clock source on T0 pin. Clock on rising edge. TCCR0 |= (1 <<CS02) | (1 <<CS01) | (1 <<CS00); // Starte Timer1. Full CPU Clock. TCCR1B |= (1 << CS10); while(1) { if (TCNT0 > 49) { // Stoppe Timer0 TCCR0 &= ~(1 <<CS02) | ~(1 <<CS01) | ~(1 <<CS00); // Stoppe Timer1 TCCR1B &= ~(1 << CS10); frequenz = (uint16_t)((TCNT0*8000)/TCNT1); // berechne Frequenz in kHz // Ausgabe Frequenz, Timerstände zur Kontrolle auf z.B. Display break; } } }
Das ist jetzt ein gutes Beispiel dafür, daß eine Taktfrequenz auch zu hoch sein kann. Wenn einige Blödmänner hier 16MHz vorgeschlagen haben, ist das völlig daneben. Du brauchst einen Takt von genau 6,34920MHz (50*8/63) !!! So erhält man dann einen Zählerstand von exakt 50. Man sieht, daß genaue Meßanforderungen auch genaue µP-Taktfrequenzen erfordern, so krumm diese auch sein mögen.
ja, der Takt des Timers kann so gewählt werden, den µC selber würde ich dennoch mit 8 od. 16MHz betreiben, damit die Auswertung schnell genug abgearbeitet werden kann...
@Michael, immer mal halblang...Wie kommst du auf das schmale Brett ? Die verarbeitbare Frequenz sinkt dann ebenfalls proportional....
Also 8MHz scheint mir auch als bestmögliche Frequenz. Hat vielleicht jemand eine Verbesserung zu meinem Programm? Oder soll ich auf ein ganz anderes Konzept zurückgreifen? Soll ich lieber in der ISR zählen, oder den Zähler mit externem Takt laufen lassen, oder die Capture Funktion benutzen. Mit was wurden die besten Ergebnisse erziehlt?
Ich habe mal die Zeilen: TCCR0 |= (1 <<CS02) | (1 <<CS01) | (1 <<CS00); TCCR1B |= (1 << CS10); durch TCCR0 |= 0x07; TCCR1B |= 0x0001; ersetzt (und beim Rücksetzen entsprechend auch). Scheint gewaltig Zeit zu sparen. Jetzt Zählt er bis ca. 1 MHz ABER: Die Ausgabe ist manchmal 1015 kHz, manchmal 1025 kHz, manchmal 1021 kHz oder irgendwo dazwischen. Also ein genaues Ergebnis schickt er nicht raus, dabei ist meine angelegte Frequenz ziemlich stabil. Je kleiner die zu messende Frequenz ist, desto ungenauer wird das Ergebnis.
Hat jemand vielleicht doch ein Beispiel? Angeblich sollen ja Frequenzen bis zur halben CPU Frequenz messbar sein...
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.