Hallo, ich versuche mit einem ATMEGA48A zwei Frequenzen im Bereich von 1MHz zu messen (Takt ist 16MHz), die an T0/T1 anliegen. Nachfolgend der Code (LCD Ausgabe habe ich weggelassen damit es nicht zu lang wird) - das Problem ist das wenn ich das gleiche Signal an beide Eingänge anlege die Frequenzmessung von Timer0 richtig ist, Timer1 aber eine zu niedrige Frequenz misst. Bin echt am verzweifeln, woran kann das liegen?? volatile unsigned char time; volatile unsigned short frequencycounter_1, frequencycounter_2; volatile long frequency_1, frequency_2; // Timer2 Overflow Interrupt #pragma vector = TIMER2_OVF_vect __interrupt void TIMER2_OVF_vect_interrupt(void) { time++; if (time > 48) { TCCR0B = 0; TCCR1B = 0; TCCR2B = 0; frequency_1 = ((unsigned long)frequencycounter_1 * 256) + TCNT0; frequency_2 = ((unsigned long)frequencycounter_2 * 65536) + TCNT1; frequencycounter_1 = 0; frequencycounter_2 = 0; time = 0; TCNT0 = 0; TCNT1 = 0; TCNT2 = 0; TCCR0B |= (1<<CS00 | 1<<CS01 | 1<<CS02); TCCR1B |= (1<<CS10 | 1<<CS11 | 1<<CS12); TCCR2B |= (0<<CS20 | 0<<CS21 | 1<<CS22); } } // Timer0 Overflow Interrupt #pragma vector = TIMER0_OVF_vect __interrupt void TIMER0_OVF_vect_interrupt(void) { frequencycounter_1++; } // Timer1 Overflow Interrupt #pragma vector = TIMER1_OVF_vect __interrupt void TIMER1_OVF_vect_interrupt(void) { frequencycounter_2++; } int main(void) { time = 0; frequencycounter_1 = 0; frequencycounter_2 = 0; frequency_1 = 0; frequency_2 = 0; __enable_interrupt(); //Interrupt enable TIMSK0 |= (1 << TOIE0); TIMSK1 |= (1 << TOIE1); TIMSK2 |= (1 << TOIE2); TCCR0B |= (1<<CS00 | 1<<CS01 | 1<<CS02); TCCR1B |= (1<<CS10 | 1<<CS11 | 1<<CS12); TCCR2B |= (0<<CS20 | 0<<CS21 | 1<<CS22); while(1) { // [...] Frequenzen auf LCD ausgeben } }
Hallo, niemand eine Idee? Ich verzweifle daran, schon alles probiert... :( Gruß, Jan
Nur ein Verdacht: Lies mal 16.0.3 External Clock Source im Datenblatt und überdenke deine Manipulationen des Prescalers.
Hallo, was genau meinst Du? Ich setze ja nur jeweils den clock source für alle drei Timer, oder was meinst Du? Gruß, Jan
Wie groß ist der Fehler? Faktor ganzzahlig, im Prozent-Bereich, eine oder zwei Schwingungen verpasst, Streuung des Messergebnisses...
Hallo, ungefähr 10-11% Fehler maximal, allerdings wird er geringer je niedriger die Frequenz ist, bei ca. 500kHz ist der Fehler fast 0. Als würde der Timer nicht mitkommen... Allerdings ist es ja dasselbe Signal an beiden Eingängen, mit 50/50 Tastverhältnis. Gruß, Jan
Was da nicht mitkommt, ist die Interrupt-Verarbeitung. Bis der Kontroller dazu kommt, die Int-Befehle abzuarbeiten muss er erst aus dem Grundprogramm aussteigen. Bis dahin können mehrere Taktzyklen vergehen, deren Zahl sogar zufällig verschieden sein kann. Auch das Nullsetzen des Zählers wird dadurch beeinflusst. Dazu kommt noch, dass der zuständige Pin das Eingangssignal nicht direkt an den Zähler weiterleitet sondern mit dem Kontrollertakt gescannt wird. Ein Teil Fehlers kann auch dadurch entstehen, dass beim T0 (8bit) mehr Lesezeit gebraucht wird als beim T1 (16bit) Der durch Int-Verhalten entstehende Fehler kann dadurch verringert werden, dass man mit Compare-Register arbeitet. Aber die Torzeiterzeugung bleibt weiterhin mangelhaft. Für genaue Zeitmessung keine Kontroller sondern einen normalen Zähler als Messzähler und Zeitbasiszähler verwenden. Hier kommt noch zusätzlich die Zeile if...>48 in der main dazu: die Ints der Timer verpfuschen noch den zeitlichenn Ablauf der main, sodass auch die Messzeit kräftig gestört wird.
Das Problem tritt aber auch auf, wenn ich time so setze das der Timer1-Überlauf nie eintritt. Und im Timer2-Interrupt werden die Timer ja zuerst angehalten, und dann erst gelesen und wieder gestartet, also sollte dafür genug Zeit sein... Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1). Gruß, Jan
Aber die Messzeit wird doch erheblich durch den Programmablauf verändert. Einmal misst der Kontroller z.B. 50ms lang, das andere mal nur 49ms. Gerade dei Torzeit wird hier in der main bestimmt, und der Ablauf der main wird durch die Int's erheblich gestört. Übrigens : ist der Takt des Kontrollers wirklich 16MHz oder läuft er (per fuse) um den Faktor 8 herabgesetzt? Meiner Meinung nach ist es ein Pfui, Zeitmessungen per Kontroller durchzuführen und besonders wenn dabei ein C-Programm auf wenig kontrollierte Weise in Maschinensprache übersetzt wird.
Peter R. schrieb: > Aber die Messzeit wird doch erheblich durch den Programmablauf > verändert. Einmal misst der Kontroller z.B. 50ms lang, das andere mal > nur 49ms. Gerade dei Torzeit wird hier in der main bestimmt, und der > Ablauf der main wird durch die Int's erheblich gestört. Sorry, aber das ist doch Unsinn. Die Torzeit wird durch einen weiteren Timer per Interrupt erzeugt. Die einzige Varianz, die sich da ergibt, ist die Latenz dieses Interrupts, also maximal ein paar µS.
Timer2 hat glaub ich einen Clock Input der zum Zählen in Hardware genutzt werden kann. Guck mal ob Timer1 das auch hat, wenn ja nimm die zum Zählen und Timer0 als Zeitbasis.
Das mache ich ja, Timer0/1 haben einen externen Eingang, Timer2 nutze ich für die Zeit. Gruß, Jan
Stefan Ernst schrieb: > Sorry, aber das ist doch Unsinn. Die Torzeit wird durch einen weiteren > Timer per Interrupt erzeugt. Die einzige Varianz, die sich da ergibt, > ist die Latenz dieses Interrupts, also maximal ein paar µS. Der Zeitablauf wird hier durch Zählen innerhalb der main (if..>49..) bestimmt. Dabei wird 50 mal der 1ms-Int beteiligt. Die Streuung durch die Int-latency kann da um den Faktor 50 vergrößert werden, da die ints die main 50mal unterbrechen. Wer auf die oben gezeigte Methode Zeitmessungen durchführt, muss eben seine Ansprüche auf Genauigkeit herabsetzen. Oder er muss genau wissen, was die Messung verfälscht.
Jan schrieb: > Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms > Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1). Was mir aufgefallen ist: Bei 1Mhz Messfrequenz und 50ms Torzeit sollte der Zählerstand aber 50000 sein. Ansonsten: Das das durch die Interrupt Latenz nicht auf die Schwingung genau ist, ist dir ja klar. 3000 Ticks Differenz ist allerdings zu groß für diese banale Erklärung. Ich komm allerdings auch nicht drauf, was da los sein könnte.
Peter R. schrieb: > Stefan Ernst schrieb: >> Sorry, aber das ist doch Unsinn. Die Torzeit wird durch einen weiteren >> Timer per Interrupt erzeugt. Die einzige Varianz, die sich da ergibt, >> ist die Latenz dieses Interrupts, also maximal ein paar µS. > > Der Zeitablauf wird hier durch Zählen innerhalb der main (if..>49..) > bestimmt. Nein. Lies das Programm nochmal. Er benutzt den Timer 2 als Torwächter. In der ISR werden 49 Aufrufe abgezählt und beim 49. Aufruf die Timer 0 und 1 sofort gestoppt. Die komplette Steuerung der Torzeit findet im Overflow-Interrupt des Timers 2 statt. Das ihm bis zum tatsächlichen Stoppen des Timers ein paar Zyklen zu viel einlaufen ist ihm bewusst. Aber das sind keine 3000 Ticks Differenz der Messungen von Timer 0 und Timer 1.
Peter R. schrieb: > Der Zeitablauf wird hier durch Zählen innerhalb der main (if..>49..) > bestimmt. Das "if" steht in einem Interrupt, nicht in main. Peter R. schrieb: > Dabei wird 50 mal der 1ms-Int beteiligt. Die Streuung durch die > Int-latency kann da um den Faktor 50 vergrößert werden, da die ints die > main unterbrechen. Die Zähler laufen selbstständig und werden nicht per Software wieder auf Null gesetzt. Die Latenzen der 50 Interrupts summieren sich daher nicht auf. Nur die Latenz des letzten Interrupts schlägt zu Buche.
Karl Heinz Buchegger schrieb: > Jan schrieb: > >> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms >> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1). > > Was mir aufgefallen ist: > Bei 1Mhz Messfrequenz und 50ms Torzeit sollte der Zählerstand aber 50000 > sein. Ja richtig, das war bei einer kleineren Frequenz. Bei 1MHz ist der Fehler sogar noch größer... Ich komme einfach nicht darauf was da los ist... Gruß, Jan
> Wer auf die oben gezeigte Methode Zeitmessungen durchführt, > muss eben seine Ansprüche auf Genauigkeit herabsetzen. Oder > er muss genau wissen, was die Messung verfälscht. Darum gehts doch noch gar nicht. Das Problem ist, dass er signifikant verschiedene Messwerte erhält, je nachdem ob er Timer 0 oder Timer 1 benutzt. Wenn die Werte falsch sind, sollten sie bei beiden Timern gleich falsch sein. Sind sie aber nicht. Die Werte laufen auseinander und im Moment kann das keiner erklären.
Jan schrieb: > Ja richtig, das war bei einer kleineren Frequenz. Bei 1MHz ist der > Fehler sogar noch größer... > > Ich komme einfach nicht darauf was da los ist... Hast du im Datenblatt nachgesehen, ob es technologische Beschränken für die Maximalfrequenz gibt, mit denen die Timer getaktet werden dürfen? (Ich kanns mir zwar nicht vorstellen, dass es da zwischen den Timern Unterschiede gibt, aber in der Not ist jeder Strohhalm recht)
Apropos Strohhalm Was ist hiermit
1 | while(1) |
2 | {
|
3 | |
4 | // [...] Frequenzen auf LCD ausgeben
|
5 | |
6 | }
|
irgendeine Chance, dass sich da ein long/int Fehler in der Ausgabe eingeschlichen hat?
Ja, bis 1/2 Taktfrequenz, empfohlen wird nur bis 1/4 zu gehen, aber da habe ich bei 16MHz ja noch gut Reserve... Gruß, Jan
Jan schrieb: > Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms > Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1). Und du bist dir wirklich ganz sicher, was die Zuordnung angeht? Es ist wirklich Timer1 derjenige, bei dem die Takte fehlen? Wäre es umgekehrt, kämen zeitweise (und zwar zu lange) in main gesperrte Interrupts in Betracht.
> was genau meinst Du? Ich setze ja nur jeweils den clock source für alle > drei Timer, oder was meinst Du? "Enabling and disabling of the clock input must be done when T1/T0 has been stable for at least one system clock cycle, otherwise it is a risk that a false Timer/Counter clock pulse is generated." Ich würde das wiederholte Disable/Enable im TIMER2_OVF_vect_interrupt komplett weglassen.
1 | volatile unsigned char time; |
2 | volatile unsigned short frequencycounter_1; |
3 | volatile unsigned short frequencycounter_2; |
4 | volatile unsigned long frequency_1; |
5 | volatile unsigned long frequency_2; |
6 | |
7 | // Timer2 Overflow Interrupt
|
8 | #pragma vector = TIMER2_OVF_vect
|
9 | __interrupt void TIMER2_OVF_vect_interrupt(void) |
10 | {
|
11 | uint8_t tmp_tcnt0 = TCNT0; |
12 | uint16_t tmp_tcnt1 = TCNT1; |
13 | |
14 | time++; |
15 | if (time > 48) |
16 | {
|
17 | frequency_1 = tmp_tcnt0 + frequencycounter_1 * 256UL; |
18 | frequency_2 = tmp_tcnt1 + frequencycounter_2 * 65536UL; |
19 | time = 0; |
20 | frequencycounter_1 = 0; |
21 | frequencycounter_2 = 0; |
22 | TCNT0 = 0; |
23 | TCNT1 = 0; |
24 | TCNT2 = 0; // neue Torzeit starten |
25 | }
|
26 | }
|
27 | |
28 | // Timer0 Overflow Interrupt
|
29 | #pragma vector = TIMER0_OVF_vect
|
30 | __interrupt void TIMER0_OVF_vect_interrupt(void) |
31 | {
|
32 | frequencycounter_1++; |
33 | // Kann frequencycounter_1 überlaufen?
|
34 | }
|
35 | |
36 | // Timer1 Overflow Interrupt
|
37 | #pragma vector = TIMER1_OVF_vect
|
38 | __interrupt void TIMER1_OVF_vect_interrupt(void) |
39 | {
|
40 | frequencycounter_2++; |
41 | }
|
42 | |
43 | int main(void) |
44 | {
|
45 | TIMSK0 |= (1 << TOIE0); |
46 | TIMSK1 |= (1 << TOIE1); |
47 | TIMSK2 |= (1 << TOIE2); |
48 | // External clock source on T0 pin. Clock on rising edge.
|
49 | TCCR0B |= (1<<CS00 | 1<<CS01 | 1<<CS02); |
50 | // External clock source on T1 pin. Clock on rising edge.
|
51 | TCCR1B |= (1<<CS10 | 1<<CS11 | 1<<CS12); |
52 | TCCR2B |= (0<<CS20 | 0<<CS21 | 1<<CS22); // clkT2S/64 (From prescaler) |
53 | __enable_interrupt(); //Interrupt enable |
54 | |
55 | while(1) |
56 | {
|
57 | unsigned long tmp_frequency_1; |
58 | unsigned long tmp_frequency_2; |
59 | |
60 | // Interruptfesten Zugriff beachten!
|
61 | // To Do: Interrupts sperren
|
62 | tmp_frequency_1 = frequency_1; |
63 | tmp_frequency_2 = frequency_2; |
64 | // To Do: Interrupts freigeben
|
65 | |
66 | // [...] Frequenzen tmp_frequency_X auf LCD ausgeben
|
67 | }
|
68 | }
|
>> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms >> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1). Diese Zahlen verstehe ich nicht. F_CPU 16 MHz, 8-Bit Timer2 Prescaler 64 mit Softwaretimer 0..48 ergibt eine Torzeitvon 50,176 ms und bei einem 1 MHz exteren Signal an T0/T1 müssten gelten: 50176 = 0 + 196 * 256UL; 50176 = 50176 + 0 * 65536UL;
Krapao schrieb: > Ich würde das wiederholte Disable/Enable im TIMER2_OVF_vect_interrupt > komplett weglassen. Habe ich ausprobiert, keine Änderung. >>> Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms >>> Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1). > > Diese Zahlen verstehe ich nicht. > > F_CPU 16 MHz, 8-Bit Timer2 Prescaler 64 mit Softwaretimer 0..48 ergibt > eine Torzeitvon 50,176 ms und bei einem 1 MHz exteren Signal an T0/T1 > müssten gelten: > > 50176 = 0 + 196 * 256UL; > 50176 = 50176 + 0 * 65536UL; Wie gesagt da hatte ich mich vertan, das war bei einer kleineren Frequenz. Mit 1MHz wird der Fehlen noch größer. Gruß, Jan
Jan schrieb: > Und es sind nicht nur einige wenige Zähl-Takte, sondern z.B. bei 50ms > Torzeit 30000 (Timer0) gegen ca. 27000 (Timer1). "ca." nützt nichts! Zeig mal die genauen Werte für verschiedene Frequenzen, dann kann man vielleicht eine Regel und daraus den Grund erkennen. Welchen Compiler benutzt Du überhaupt? Peter
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.