Hi
Ich bin ein wenig verwirrt. Ich benutze den Atmega8 mit einem 12 Mhz
Quarz und dem Timer2 in CTC Mode.
Das unten aufgeführte Programm ruft den CTC Interrupt alle 1.002ms auf.
In diesem zähle ich einen Zähler bis auf 998 um auf exakt 1s zu kommen.
Über das Display gebe ich den Sekundenwert alle ~10Sek. aus.
Wenn ich jetzt warte bis die Puls Variable auf 600 springt (10 min.)
steht meine Stopuhr (Iphone) welche parallel läuft auf 10 Minuten und 11
Sekunden. Habe ich noch einen Fehler oder sind die Dinger so ungenau das
11 Sekunden Abweichung möglich sind.
Gruß
Thomas
#include <avr/io.h>
#include "lcd-routines.h"
#include <util/delay.h>
#include <stdint.h>
#include <stdio.h>
#include <avr/interrupt.h>
#define F_CPU 12000000UL
char Buffer[20];
uint16_t counter1 = 0, counter2 = 0, Puls = 0;
//------------------------------------------------------------------
int main(void)
{
//8 Bit Timer2 Einstellungen
TCCR2 |= (1<<WGM21) | (1<<CS21)| (1<<CS22); //Teiler: 256, 12Mhz
OCR2 = 47;
TIMSK |= (1<<OCIE2);
sei();
lcd_init();
//------------------------------------------------------------------
//------------------------------------------------------------------
while(1)
{
}
return 0;
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
ISR(TIMER2_COMP_vect) //Interrut Timer2 Vergleicher (Alle 1,002ms)
{
counter1++;
counter2++;
if(counter1 >= 998) // Alle 1000ms Drehzahl berechnen (1,002ms X 998)
{
Puls++;
counter1 = 0;
}
if(counter2 >= 9980) // Alle ~10s Wert ausgeben
{
counter2 = 0;
lcd_setcursor( 0, 1 );
itoa( Puls, Buffer, 10 );
lcd_string(Buffer);
}
}
Laut Berechnung müsste das Ding 13 Sekunden in 10 Minuten falsch gehen, da ist eher's IPhone kaputt. Sieh Dir die Formel im Datenblatt zu CTC an, ist eigentlich für PWM zu finden, trifft hier aber genauso zu.
Wie kommt man auf die 1002 bzw 998? Sind da nicht noch irgendwelche Nachkomma-Reste zu beachten, die sich aufsummieren?
@Thomas Webster (ram99) >Ich bin ein wenig verwirrt. Ich benutze den Atmega8 mit einem 12 Mhz >Quarz und dem Timer2 in CTC Mode. >Das unten aufgeführte Programm ruft den CTC Interrupt alle 1.002ms auf. >In diesem zähle ich einen Zähler bis auf 998 um auf exakt 1s zu kommen. Ob das so günstig ist? >TCCR2 |= (1<<WGM21) | (1<<CS21)| (1<<CS22); //Teiler: 256, 12Mhz Stimmt. Aber 12 MHz / 256 = 46,875 kHz >OCR2 = 47; Das teilt durch 48! Denn der Zähler geht von 0-47! Aber selbst wenn du durch 47 teilen würdest, kämen immer noch 997,34 Hz raus. Bei Teilung durch 998 bleibt eine Fehler von 660ppm, macht 40ms Fehler pro Minute und 57s Fehler pro Tag. Wie man es richtig macht, siehe AVR - Die genaue Sekunde / RTC Dein Methode mit 2 Zählern ist ungünstig. Es ist deutlich besser und einfacher, den 2. Zähler mit dem ersten zu verketten, anstatt parallel zu zählen. Damit vermeidet man ein Desynchronisieren durch Programmfehler. >ISR(TIMER2_COMP_vect) //Interrut Timer2 Vergleicher (Alle 1,002ms) >{ > counter1++; > counter2++; if(counter1 >= 998) // Alle 1000ms Drehzahl berechnen (1,002ms X 998) { Puls++; counter1 = 0; } > if(counter2 >= 9980) // Alle ~10s Wert ausgeben > { > counter2 = 0; > lcd_setcursor( 0, 1 ); > itoa( Puls, Buffer, 10 ); > lcd_string(Buffer); > } >} Das ist ein No-GO! Solche Ausgaben haben in der ISR NICHTS zu suchen! Wenn nämlich deine LCD-Ausgabe länger als 2x1,002ms dauert, verschluckst du Interrupts. Siehe Interrupt. Besser so.
1 | #include <avr/io.h> |
2 | #include "lcd-routines.h" |
3 | #include <util/delay.h> |
4 | #include <stdint.h> |
5 | #include <stdio.h> |
6 | #include <avr/interrupt.h> |
7 | #define F_CPU 12000000UL
|
8 | |
9 | char Buffer[20]; |
10 | volatile uint16_t Puls = 0; |
11 | volatile uint8_t flag_10s; |
12 | |
13 | //------------------------------------------------------------------
|
14 | int main(void) |
15 | {
|
16 | //8 Bit Timer2 Einstellungen
|
17 | TCCR2 |= (1<<WGM21) | (1<<CS21)| (1<<CS22); //Teiler: 256, 12Mhz |
18 | OCR2 = 47; |
19 | TIMSK |= (1<<OCIE2); |
20 | |
21 | sei(); |
22 | lcd_init(); |
23 | //------------------------------------------------------------------
|
24 | //------------------------------------------------------------------
|
25 | while(1) |
26 | {
|
27 | if (flag_10s) { |
28 | flag_10s=0; |
29 | lcd_setcursor( 0, 1 ); |
30 | itoa( Puls, Buffer, 10 ); |
31 | lcd_string(Buffer); |
32 | }
|
33 | }
|
34 | return 0; |
35 | }
|
36 | //-------------------------------------------------------------------
|
37 | //-------------------------------------------------------------------
|
38 | ISR(TIMER2_COMP_vect) //Interrut Timer2 Vergleicher (Alle 1,002ms) |
39 | {
|
40 | static uint16_t counter1 = 0, counter2 = 0; |
41 | |
42 | counter1++; |
43 | |
44 | if(counter1 >= 998) // Alle 1000ms Drehzahl berechnen (1,002ms X 998) |
45 | {
|
46 | Puls++; |
47 | counter1 = 0; |
48 | counter2++; |
49 | if(counter2 = 10) // Alle ~10s Wert ausgeben |
50 | {
|
51 | counter2=0; |
52 | flag_10s=1; |
53 | }
|
54 | }
|
55 | }
|
12 MHz / 256 = 46,875 kHz 46,875 kHz = 5^6 * 3 (Primfaktorzerlegung) Diese Zahl kann man nun ohne Rest aufteilen in 5^3=125 und 5^3*3=375. Den Teilerfaktor 125 packt man in den Hardwarezähler Timer 2 mit OCR2 = 124; // Teiler durch 125! Den Teilerfaktor 375 packt man in die ISR als Softwaretimer. Damit bleibt nur noch die Toleranz vom Quarz als Fehler übrig. Et voilà!
Mist, oben ist noch der falsche Teiler drin! Also gleich richtig machen.
1 | #include <avr/io.h> |
2 | #include "lcd-routines.h" |
3 | #include <util/delay.h> |
4 | #include <stdint.h> |
5 | #include <stdio.h> |
6 | #include <avr/interrupt.h> |
7 | #define F_CPU 12000000UL
|
8 | |
9 | char Buffer[20]; |
10 | volatile uint16_t Puls = 0; |
11 | volatile uint8_t flag_10s; |
12 | |
13 | //------------------------------------------------------------------
|
14 | int main(void) |
15 | {
|
16 | //8 Bit Timer2 Einstellungen
|
17 | TCCR2 |= (1<<WGM21) | (1<<CS21)| (1<<CS22); //Teiler: 256, 12Mhz |
18 | OCR2 = 124; // teilt durch 125 !!! |
19 | TIMSK |= (1<<OCIE2); |
20 | |
21 | sei(); |
22 | lcd_init(); |
23 | //------------------------------------------------------------------
|
24 | //------------------------------------------------------------------
|
25 | while(1) |
26 | {
|
27 | if (flag_10s) { |
28 | flag_10s=0; |
29 | lcd_setcursor( 0, 1 ); |
30 | itoa( Puls, Buffer, 10 ); |
31 | lcd_string(Buffer); |
32 | }
|
33 | }
|
34 | return 0; |
35 | }
|
36 | //-------------------------------------------------------------------
|
37 | //-------------------------------------------------------------------
|
38 | ISR(TIMER2_COMP_vect) //Interrut Timer2 Vergleicher , 375 Hz |
39 | {
|
40 | static uint16_t counter1 = 0, counter2 = 0; |
41 | |
42 | counter1++; |
43 | |
44 | if(counter1 >= 375) // Alle 1000ms Drehzahl berechnen (1,002ms X 998) |
45 | {
|
46 | Puls++; |
47 | counter1 = 0; |
48 | counter2++; |
49 | if(counter2 = 10) // Alle ~10s Wert ausgeben |
50 | {
|
51 | counter2=0; |
52 | flag_10s=1; |
53 | }
|
54 | }
|
55 | }
|
@ STK500-Besitzer (Gast) >> if(counter2 == 10) // Alle ~10s Wert ausgeben >Schönheitsfehler... Aufmerksamkeitstest ;-) (Scheiß C-Stolperfallen, ich vermisse TurboPascal 8-0)
Hi, vielen Dank für die Hilfe. Ich habe jetzt erst nach über 5 Jahren wieder angefangen mit den Teilen zu arbeiten. Freu mich auch über den Hinweiß mit der Ausgabe des Werts in der ISR. Die Idee mit der Primfaktorzerlegung finde ich auch cool Danke Thomas
Thomas Webster schrieb: > Wenn ich jetzt warte bis die Puls Variable auf 600 springt (10 min.) > steht meine Stopuhr (Iphone) welche parallel läuft auf 10 Minuten und 11 > Sekunden. Zum Überprüfen des Programms ist ein iPhone denkbar ungeeignet, weil es die Fehler nicht analysieren und nach Software- und Taktfehler trennen kann. Der Simulator in Atmel-Studio zeigt mit "Stop Watch" und "Cycle Counter" taktgenau die Zeiten und Prozessorzyklen an. Damit sieht man gleich, ob die Anzahl von Schleifendurchläufen oder die Zeit zwischen Timer-Interrupts richtig ist.
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.