Forum: Mikrocontroller und Digitale Elektronik Frequenzmessung


von alex (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe ein Problem mit einem Projekt. Ich sollte eine Frequenz messen 
(zwischen 100Hz bis 5000Hz). Ich habe es bis jetzt so versucht:
bei einer steigenden Flanke wird ein Zähler gestartet (200kHz) und wird 
bei jeder steigenden Flanke wieder zurückgesetzt. Der Zählerstand wird 
an einem Display ausgegeben (die Mathematik um dies wieder in eine 
Frequenz umzurechnen brauch ich nicht, da ich einige Abtastwerte 
speichere und diese dann Vergleiche...es sollte übrigens ein Stimmgerät 
werden).

Nun mein Problem, ich habe bei eine Frequenz von 440Hz einen Abtastwert 
von 454 berechnet. Ich komme aber nur auf einen Wert von 423.
Der Schaltplan liegt im Anhang :)
Mein Code sieht bisher so aus:

volatile unsigned long wert = 0;
volatile unsigned long wert1 = 0;

SIGNAL(SIG_OUTPUT_COMPARE0)
{
  wert++;
}

SIGNAL(SIG_INTERRUPT1)
{
  wert1=wert;
  wert=0;
}

int main(void)
{
  DDRD=0x00;

  TCCR0 = 0b00001001;
  TIMSK = 0x02;
  OCR0=12;

  MCUCR = 0x0C;
  GICR = 0x80;

  sei();
  lcd_init();

  while(1)
  {
    lcd_pos(0,0);
    printf("Wert: %8d",wert1);
  }
}

Ich hoffe mir kann jemand helfen!

von MaWin (Gast)


Lesenswert?

Mir leuchtet nicht ein, warum dein Komparator bei einem um 0 
symmetrischen Audiosignal nicht mit 0V vergleicht, sondern mit halbem 
Maximum.

> es sollte übrigens ein Stimmgerät werden).

Dazu reicht das Verfahren natürlich vorne und hinten nicht.
Schlag nach was eine FFT ist.

Zudem ist den LM358 als Komparator mit vollem output swing nicht mehr so 
richtig tauglich für hohe Töne.

von alex (Gast)


Angehängte Dateien:

Lesenswert?

Mit der analogen Schaltung habe ich kein Problem, diese funktioniert 
ohne Probleme! Nur die Messung mit dem Atmega16.

Mit dem Komparator "filtere" ich die Obertöne heraus, da die 
Nulldurchgänge nicht die Grundfrequenz ist.

Im Anhang siehst du warum ich es mit 90% des maximal wertes vergleiche.

von Ingo (Gast)


Lesenswert?

Grundsätzlich mast du es ja richtig, nur das mit dem Timer durchblicke 
ich nicht. Irgendwas stimmt da nicht.

Also erstmal kannst du den Timer ruhig auf Vollgas laufen lassen, dass 
erhöht deine Auflösung. Dann triggerst du bei steigender Flanke die 
Messung indem du dir dein aktuellen Zählerstand merkst. Bei der nächsten 
steigenden Flanke guckst du dir wieder den Zählerstand an, nebenbei 
zählst du noch die Timerüberläufe mit. Danach berechnest du die 
Timerincremente:

Incremente = Überläufe * (2^Bits Timer)- neuer Wert+ alter Wert

mit den Incrementen bestimmst du die Frequenz:

F_Timer/Incremente * (gewünschte Auflösung [10 für 10tel Hz, 100 für 
100tel Hz])

fertig ist die Laube...
Es ist sinnvoll über ein Paar Perioden zu mittel bevor man die Frequenz 
ausrechnet...


Aber dein Komparator sieht in der Tat etwas seltsam aus.

Ingo

von Ingo (Gast)


Lesenswert?

alex schrieb:
> Im Anhang siehst du warum ich es mit 90% des maximal wertes vergleiche.
Äh, tust du nicht, du vergleichst mit 99%, denn

1,2/1,21 = 0,9917


Ingo

von Ingo (Gast)


Lesenswert?

Oszillographier doch mal am Komparatorausgang, dass ist ja der 
interessante Teil!

von alex (Gast)


Lesenswert?

> Äh, tust du nicht, du vergleichst mit 99%, denn

Sorry mein Fehler!

Danke Ingo, ich werde es einmal so versuchen.

Leider habe ich erst am Montag wieder ein Oszilloskop in der Hand 
(Schüler ^^).
Aber das Ausgangssignal am Ausgang des Komparators kommen "Nadeln" mit 
der Grundfrequenz heraus.

von Ingo (Gast)


Lesenswert?

alex schrieb:
> Aber das Ausgangssignal am Ausgang des Komparators kommen "Nadeln" mit
> der Grundfrequenz heraus.
Wenn dem so ist, ist da ja gut. Jedoch wäre mal interessant wie es 
aussieht. Was noch auffällt, dass der Komparator absolut keine Hysterese 
hat, ohne die es jedoch relativ schnell zu Schwingungen führt, gerade 
beim Umschalten.

Ingo

von alex (Gast)


Lesenswert?

Ich werde gleich am Montag, falls ich dazu komme, das Signal aufnehmen!
Ingo:

Ich sollte bei jeder Steigenden Flanke ein Interrupt auslösen und dann 
die Überläufe, den End- bzw den Anfangsstand speichern.
Ich glaube, für dies muss ich mich noch ein wenig einlesen, bin eben 
noch Neuling ;-)

von Ingo (Gast)


Lesenswert?

alex schrieb:
> Ich sollte bei jeder Steigenden Flanke ein Interrupt auslösen und dann
> die Überläufe, den End- bzw den Anfangsstand speichern.
> Ich glaube, für dies muss ich mich noch ein wenig einlesen, bin eben
> noch Neuling ;-)
So ist es, aber man wächst mit seinen Aufgaben... Is aber recht trivial


Ingo

von Dirk J. (dirk-cebu)


Lesenswert?

alex schrieb:
> Ich sollte eine Frequenz messen...

Und warum nimmst Du nicht ein fertiges Frequenzmeßgerät?

von Ingo (Gast)


Lesenswert?

Dirk J. schrieb:
> Und warum nimmst Du nicht ein fertiges Frequenzmeßgerät?

alex schrieb:
> Leider habe ich erst am Montag wieder ein Oszilloskop in der Hand
> (Schüler ^^)

alex schrieb:
> es sollte übrigens ein Stimmgerät
> werden


Noch Fragen???


Ingo

von MWS (Gast)


Lesenswert?

alex schrieb:
> wird ein Zähler gestartet (200kHz)
>   OCR0=12;

Du weist, dass Die Formel für die Aufrufrate bei CTC
lautet ? Bzw. dass der Timer0 damit 13 Takte für 'ne Runde braucht.

Deine Taktfrequenz wäre dann bei 200kHz x 13 = 2,6MHz, sehr 
ungewöhnlicher Takt. Außerdem mit dem hier:

> TCCR0 = 0b00001001;
> OCR0=12;
> TIMSK = 0x02

hast Du eben nur 13 Takte zur Verfügung um in der ISR einen volatile 
Long zu inkrementieren, das ist völlig illusorisch. Tatsächlich benötigt 
das um die 60 Takte. Womit wiederum Dein vermeintliches Ergebnis:

> Ich komme aber nur auf einen Wert von 423.

Makulatur ist. Also zurück auf Start und ziehe keine 1000 Mark ein.

Außerdem würd' ich mir das Das Konfigurieren der Register per Binär- 
oder Hex-Konstanten abgewöhnen, das ist ein Unding.

von alex (Gast)


Lesenswert?

Ich habe das OCR Register nun auf 60 geändert (200kHz wenn ich mich 
nicht täusche) jedoch habe ich immer noch fast den gleichen Fehler.

von Ulrich (Gast)


Lesenswert?

Die 60 Zyklen für die ISR waren nur ein Schätzwert, das können auch noch 
ein paar mehr sein. 200 kHz könnte schon etwas zu schnell sein für den 
µC.

Die logischere Lösung hat Ingo oben schon genannt: einfach einen Timer 
laufen lassen, und die Timerregister auslesen. Das Hochzählen einer 
Variable in einer Timer ISR ist fast immer eine suboptimale Lösung zur 
Zeitmessung. Das Hochzählen der "Zeit" ist eigentlich genau das was die 
timer Hardware macht. Falls es zu schnell geht, gibt es dafür den 
Vorteiler.

Das wird vor allem dann einfacher, wenn man timer 1 nimmt, der hat mit 
16 Bit genügend Auflösung und man muss sich nicht groß um Überläufe von 
Hand kümmern - das ist nämlich nicht ganz so trivial wie es scheint.

von MWS (Gast)


Lesenswert?

Ulrich schrieb:
> Die 60 Zyklen für die ISR waren nur ein Schätzwert

Es waren 58 Zyklen ab Resetvektor. Je nach gerade ausgeführtem Befehl 
dauert es 1,2 oder 3 Zyklen, bis dieser zu Ende ausgeführt wurde.

> wenn man timer 1 nimmt, der hat mit 16 Bit genügend Auflösung

Und man kann den Input Capture nutzen, der das Ganze schon viel 
entspannter werden lässt.

alex schrieb:
> Ich habe das OCR Register nun auf 60 geändert (200kHz wenn ich mich
> nicht täusche) jedoch habe ich immer noch fast den gleichen Fehler.

Bei 60 (61 Durchläufe) hast Du immer noch ~100% Prozessorlast, erst wenn 
Du drüber gehst, wird der Timer das zeitbestimmende Element und Du wirst 
Unterschiede merken.

Wie hoch ist denn die Taktfrequenz ?

Außerdem, die ISR ist auch deshalb so lang, weil der Compiler 4 
Prozessorregister für den Long benutzt, er diese sichern muss und auch 
weil dann 4 Byte aus dem SRam geholt und dorthin wieder gesichert werden 
müssen.

Wenn's schon der Ansatz mit Timer0 sein soll, dann nimm ein Word, das 
geht schneller und reicht auch.

von Erich (Gast)


Lesenswert?

Ja, MWS (Gast) hat recht !
Frequenzmessungen macht man mit dem "Input Capture Mode" ,
ich hoffe mal dieser uC hat sowas auch.

GOOGLE: Frequenzmessung "input capure mode"

von alex (Gast)


Lesenswert?

MWS und Erich, hätte ich den ICP Pin am ATMEGA verwenden sollen? oder 
geht das mit jedem beliebigen?

von STK500-Besitzer (Gast)


Lesenswert?

alex schrieb:
> MWS und Erich, hätte ich den ICP Pin am ATMEGA verwenden sollen? oder
> geht das mit jedem beliebigen

Das geht nur mit dem.

von alex (Gast)


Lesenswert?

Also müsste ich den Schaltplan ändern und ein neues Layout erstellen - 
neuer Print! Gibt es keine andere Lösung?

von MWS (Gast)


Lesenswert?

alex schrieb:
> hätte ich den ICP Pin am ATMEGA verwenden sollen? oder

Alternativ kann der Input Capture über den Analog Comparator getriggert 
werden, der AC kann wiederum über den Multiplexer gefüttert werden. Da 
hast Du dann zwar mehr Pins zur Auswahl, auf Deinen Int1-Pin kommst Du 
damit immer noch nicht.

> ein neues Layout erstellen - neuer Print! Gibt es keine andere Lösung?

Doch da fiele mir schon was ein, musst halt ausprobieren, ob das geht. 
Im Datenblatt findest Du unter "Alternate Functions of Port D" den ICP1 
auf Pin6. In manchen Fällen "überfährt" eine alternative Portfunktion 
die originale Funktion vollständig, in anderen Fällen bleiben sie 
zumindest teilweise intakt.

Bei den ext. Int's ist's so, dass man die auf Ausgang schalten und damit 
den Interrupt per Software auslösen kann. Das könnte hier auch gehen, 
wenn der ICP1-Pin nicht anders verwendet wird. Dann schaltest Du den auf 
Ausgang und triggerst ihn in der Int1-ISR. Das kostet Dich nicht viel 
Rechenzeit und solange keine weiteren ISR's (außer der Capture-ISR) 
aktiv sind, wird das zwar 'ne kleine Verzögerung erzeugen, aber immer 
annähernd dieselbe.

Du hast natürlich eine Unsicherheit zwischen 1-3 Takten, die es dauert 
um aus dem aktuellen Befehl raus in eine ISR zu springen, das 
verschlechtert Deine Auflösung.

Damit genau das nicht passiert, nimmt man eigentlich Input Capture, aber 
kannst es ja mal als Notlösung versuchen.

Du hast immer noch nicht beantwortet nach was ich gefragt hab', nämlich 
die Taktfrequenz, aber gehen wir mal aus, dass Dein Code mit 200kHz 
hochzählen konnte, mal 60 Takte hättest Du einen 12MHz Quarz dran.

Bei 440Hz entspräche eine vollständige Periode dann 27272 Takten, bei 
einem Prescaler von 1 wäre das gleichzeitig das Ergebnis der Capture. 
Bei einer Unsicherheit von 1 bis 3 Zyklen käme dann bei exakt gleicher 
Eingangsfrequenz von 440 Hz ein Zählwert von 27270 bis 27272 raus, was 
440,04 bis 440,01 Hz als Ergebnis entspricht, also sollte der Fehler aus 
dieser Int1-Lösung für Deinen Zweck unerheblich sein.

Bei 10kHz ist der Fehler Prinzip-bedingt größer, da ist das Ergebnis 
dann 10000 bis 9983 Hz. Hinzu käme noch +-2 Takte wenn der Trigger 
ungünstig zwischen 2 Prozessortakte fällt. Das Capture Verfahren wird 
beiläufig immer ungenauer, je kleiner das Capture-Ergebnis wird.

So, und jetzt darfst Du mal weitermachen, ist ja nicht meine Aufgabe ;D

von MWS (Gast)


Lesenswert?

MWS schrieb:
> Das könnte hier auch gehen,
> wenn der ICP1-Pin nicht anders verwendet wird.

Grad' gesehen, dass ICP1 auf JP1 raus geht, dürfte das LCD sein. Wenn Du 
Glück hast und dort nur ein Datenpin dran hängt, stört das nicht weiter, 
Du darfst halt dann nur messen, wenn Du nichts auf dem LCD ausgibst und 
vice-versa.

von Ulrich (Gast)


Lesenswert?

Timer1 kann man auch ohne ICP nutzen. ICP wäre zwar die Ideale Lösung, 
aber so viel schlechter ist es mit Interrupt und dann auslesen des 
Timers auch nicht.  Der Umweg über Interrupt und dann künstlich den ICP 
auszulösen ist nur umständlich, das auslesen des Timers braucht auch 
nicht länger und ist einfacher. Vor allem stört eine feste Verzögerung 
nicht, sondern nur eine Variabel.

Für die Anwendung hier reicht die Genauigkeit mit dem Interrupt auch 
aus. So lange kein anderer Interrupt aktiv ist, hat man eine unbekannte 
Verzögerung von maximal 6 Zyklen - so genau wird das Signal vom Mikrofon 
nicht sein.

von MWS (Gast)


Lesenswert?

Ulrich schrieb:
> Der Umweg über Interrupt und dann künstlich den ICP
> auszulösen ist nur umständlich, das auslesen des Timers braucht auch
> nicht länger und ist einfacher.

Dafür kann der TE bei neuer geeigneter Platine problemlos die bessere 
Methode nutzen, nur Int1 deaktivieren und ICP1 als Eingang.

> Vor allem stört eine feste Verzögerung nicht, sondern nur eine Variabel.

Eine variable Verzögerung ergibt sich bereits aus der ungewissen Zahl an 
Zyklen von Interrupt-Auslösung bis Ausführung, ist aber in beiden Fällen 
gering.

von alex (Gast)


Angehängte Dateien:

Lesenswert?

Kann mir jemand beim programmieren helfen? Ich habe bis jetzt immer nur 
mit dem Timer0 gearbeitet und weiß eigentlich nicht wie ich beginnen 
sollte.

Hier ist noch das Ausgangssignal des Komparators:

von Sven (Gast)


Lesenswert?

Ingo schrieb:
> Incremente = Überläufe * (2^Bits Timer)- neuer Wert+ alter Wert

Sicher?
Ich hätte gesagt so:
Incremente = Überläufe * (2^Bits Timer)+ neuer Wert - alter Wert

von Ulrich (Gast)


Lesenswert?

Die Programmierung ist mit Timer 1 auch nicht so viel anders. Das extra 
Zählen von Überläufen bei timer 0 ist deutlich komplizierter - das 
kommen nämlich noch Tücken und zusätzliche Fehler dazu, wenn das Signal 
fast gleichzeitig mit dem Überlauf auftritt.

Der wesentliche Unterschied bei timer1 ist, das man da gleich einen 16 
Bit Wert hat, und deshalb ohne den Interrupt für die Überläufe auskommt. 
Die Zeit ließt man im Interrupt vom Signal (z.B. int1) einfach direkt 
aus dem Timerregister TCNT1 aus. Dank C Compiler geht das gleich als 16 
Bit wert. Damit die Zeit reicht, braucht man ggf. einen Prescaler beim 
konfigurieren des Timers - ein Wert von 8 sollte reichen, bei hohen µC 
Takt ggf. besser :64.

Die Periodenlänge bekommt man dann aus der Differenz neuer Wert - alter 
Wert. Dabei sollten die Variablen 16 Bit unsigned sein, dann hat man 
auch keine Probleme falls dazwischen 1 Überlauf des Timers vorkommt. 
Erst wenn die Zeit über 2^16 Timer Ticks liegt gibt es Probleme - das 
sollte aber schon bei sehr niedriger Frequenz liegen (z.B. unter 10 Hz).

Das Abfangen ist da allerdings nicht so einfach ohne einen 2. Interrupts 
der ggf. kleine Fehler in der Messung verursachen kann.

von alex (Gast)


Lesenswert?

Kann mir jemand sagen wie ich die Register setzten muss, um diesen Modus 
den ich benötige zu erhalten?
Und wie kann ich die Werte dann herauslesen?
Ich wäre über einen Programmcode sehr dankbar (:

von Erich (Gast)


Lesenswert?

GOOGLE fragen:

"frequency measurement" "<Prozessortyp genaue Bezeichnung>" "source 
code"

Die Gänsefüßchen dran lassen !

von alex (Gast)


Lesenswert?

Naja, allzu viel findet man nicht!

Bis auf das habe ich nichts gefunden:
http://www.hackchina.com/en/r/53443/1602_main.c__html

jedoch werde ich aus dem Code nicht wirklich schlau....

von Gugelhupf (Gast)


Lesenswert?


von alex (Gast)


Lesenswert?

Ich habe es nun mal versucht zu schreiben. Jedoch habe ich ein Problem 
mit der Ausgabe, wenn ich ein Ton spiele, und gleich aufhöre wird ein 
Wert angezeigt (ich glaube sogar richtig) und wenn ich dauerhaft spiele 
bleibt 0 stehen...

SIGNAL(SIG_INTERRUPT1)
{
  altwert=wert;
  wert=ICR1;
}

int main(void)
{
  DDRA=0x00;
  DDRD=0x00;

  TCCR1B=0b00000010;

  MCUCR = 0x0C;
  GICR = 0x80;

  sei();
  lcd_init();

  while(1)
  {
    old=new;
    new=65536-altwert+wert;
    if(old==new)
    {
      lcd_pos(0,0);
      printf("Wert: %8d",new);
    }
  }
}

von Erich (Gast)


Lesenswert?

if(old==new)

Ja, du machst bei Änderungen auch keine Ausgabe.

von alex (Gast)


Lesenswert?

Erich schrieb:
> Ja, du machst bei Änderungen auch keine Ausgabe.

Ich mache dann eine Änderung wenn 2 Werte hintereinander gleich sind?

von alex (Gast)


Lesenswert?

Weiß niemand was falsch ist?

von Ulrich (Gast)


Lesenswert?

Der Programm-ausschnitt hat schon mal das Problem, dass nicht richtig 
auf das Ende einer Messung gewartet wird. Die Abfrage auf (old==new) 
wird damit fast wirkungslos - einen Durchlauf der While-schleife später 
ist die dann erfüllt, falls nicht zufällig gerade ein neuer Messwert 
kommt.

Ein weiters Problem könnte sein, dass die ISR für INT1 ist, ausgelesen 
wird aber ICR1, was dazu passen würde das das Signal am ICP Pin ist. 
Wenn das Signal an INT1 anliegt, so wie zuvor, dann müsste man da den 
Timer auslesen, also TCNT1.

von alex (Gast)


Lesenswert?

Danke für die Antwort,
ich lese nun mit dem TCNT1 aus!
Dabei komme ich auf einen extrem schwankenden Wert, eine völlig sinnlose 
Messung also!
Und außerdem ist er etwa um den Faktor 10 zu klein, als wie er sein 
sollte.

von alex (Gast)


Lesenswert?

Ich habe den angezeigten Wert zurückgerechnet, dieser würde ca. bei 
einer Abtastung von 1,5 Mhz stimmen, allerdings taste ich mit 12Mhz ab, 
wo bleiben die anderen 10,5?

von spess53 (Gast)


Lesenswert?

Hi

>Ich habe den angezeigten Wert zurückgerechnet, dieser würde ca. bei
>einer Abtastung von 1,5 Mhz stimmen, allerdings taste ich mit 12Mhz ab,
>wo bleiben die anderen 10,5?

CKDIV8 Fuse gesetzt?

MfG Spess

von alex (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe nur diese gesetzt.

von spess53 (Gast)


Lesenswert?

Hi

>Ich habe nur diese gesetzt.

Und weshalb sollte dein Controller mit 12MHz laufen. Du hast 1MHz 
interner RC-Oszillator eingestellt.

MfG Spess

von alex (Gast)


Lesenswert?

Welche Fuses müsste ich setzten?
Ich habe einen 12Mhz Quarz....

von spess53 (Gast)


Lesenswert?

Hi

>Welche Fuses müsste ich setzten?

Datenblatt?

>Ich habe einen 12Mhz Quarz....

Hoffentlich richtig angeschlossen.

MfG Spess

von Ingo (Gast)


Lesenswert?

@alex
Wie wäre es wenn du mal endlich anfängst zu begreifen was du da machst?! 
Beschäftige dich doch erstmal mit den Basics anstatt hier Hals über Kopf 
was lostreten zu wollen.


Ingo

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.