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!
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.
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.
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
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
> Ä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.
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
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 ;-)
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
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
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.
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.
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.
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"
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
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.
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.
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.
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:
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.
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 (:
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);
}
}
}
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.
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.
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?
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
@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