Forum: Mikrocontroller und Digitale Elektronik Frequenzmessung, diesmal aber anders...


von Martin (Gast)


Lesenswert?

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!

von Stefan G. (steg13)


Lesenswert?

ist doch die sinnvollste Lösung!

von Lothar (Gast)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

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.

von Christoph Kessler (db1uq) (Gast)


Lesenswert?

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.

von Martin (Gast)


Lesenswert?

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.

von Martin (Gast)


Lesenswert?

@ Michael:

Messen will ich nicht die Burstfrequenz, sondern die Signalfrequenz. Je 
höher die Auflösung desto besser.

von Christoph Kessler (db1uq) (Gast)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

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 :-)

von Martin (Gast)


Lesenswert?

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;
}

von Martin (Gast)


Lesenswert?

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?

von Michael (Gast)


Lesenswert?

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.

von Martin (Gast)


Lesenswert?

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...




von unsichtbarer WM-Rahul (Gast)


Lesenswert?

>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.

von Martin (Gast)


Lesenswert?

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.

von jÜrgen (Gast)


Lesenswert?

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

von jÜrgen (Gast)


Lesenswert?

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?

von Sonic (Gast)


Lesenswert?

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.

von Martin (Gast)


Lesenswert?

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;
}
}
}

von Michael (Gast)


Lesenswert?

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.

von jÜrgen (Gast)


Lesenswert?

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...

von Sabbelhannes (Gast)


Lesenswert?

@Michael,

immer mal halblang...Wie kommst du auf das schmale Brett ?
Die verarbeitbare Frequenz sinkt dann ebenfalls proportional....

von Martin (Gast)


Lesenswert?

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?

von Martin (Gast)


Lesenswert?

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.

von Martin (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.