Hallo, ich habe mir eine Uhr programmiert, allerdings geht sie nach 5 Stunden bereits um ca 4 Sekunden nach. Der Mega32 bezieht seinen Takt aus einem 4MHZ-Quarz. Mit dem UART habe ich keine Probleme. Was kann ich machen, damit meine Uhr nicht mehr falsch geht? Mir ist schon bewusst, dass es nicht ganz genau geht, aber dass die Uhr so ungenau ist? Brauche ich da vielleicht einen Uhrenquarz? Oder liegt es daran, der der ISR zulange ausgeführt wird? mfg utzer
Hallo, rechne doch einfach mal aus, wie lange Deine Interruptroutine alle Sekunde braucht... Du rufst sie alle 10ms auf, verbrätst aber schon 7ms mit nutzlosen _delay_ms(1); gibst mehrere Zeichen über UART aus und konvertierst noch die Daten. Da würde es moch garnicht wundern, wenn da kedesmal mindestens 1 IRQ verpasst wird. Gruß aus Berlin Michael
Das wichtigste ist, festzustellen, ob die Interrupt-Funktion mit der exakten Frequenz aufgerufen wird und der Teilerfaktor richtigist (dazu kann man testweise und vorübergehend einen Pin definieren und dort ein Rechtecksignal ausgeben und es überprüfen). Ist der Quarz genau genug? Dann solltest Du die Ausgaben aus der Interrupt-Funktion rausnehmen und in den Vordergrund stellen (also in die "main"). In der Interrupt-Funktion könntest Du alle Sekunde ein globales Flag auf 1 setzen; dieses fragst Du im Vordergrund ab; sobald es 1 wird, setzt Du es zurück und machst Deine sekundliche Ausgabe; evt. ganz schnell die Zeitdaten auf lokale Variablen umkopieren und dabei Interrupt disable. Dann hast Du genug Zeit zur Verarbeitung.
So geht das nicht. Du kannst nicht innerhalb einer ISR mehrere Millisekunden lange warten und davon ausgehen, daß keine IRQ verpasst wird! Innerhalb von Millisekunden sind schon Weltreiche aufgestiegen und wieder untegegangen... Johann
Hallo, ich habe nun mein Programm mal zu Testzwecken einwenig geändert:
1 | #include<avr/io.h> |
2 | #include<inttypes.h> |
3 | #ifndef F_CPU
|
4 | #define F_CPU 4000000UL
|
5 | #endif
|
6 | #include<util/delay.h> |
7 | #include<stdint.h> |
8 | #include <avr\interrupt.h> |
9 | #include "UART.h" |
10 | |
11 | volatile int sec; |
12 | volatile int min; |
13 | volatile int hor; |
14 | volatile int count; |
15 | |
16 | volatile char seconds[5]; |
17 | volatile char minuts[5]; |
18 | volatile char hours[5]; |
19 | |
20 | int main(void) |
21 | {
|
22 | stellen:
|
23 | |
24 | DDRB=0xFF; |
25 | DDRC=0x00; |
26 | PORTB=0xFF; |
27 | |
28 | uint16_t comp; |
29 | comp=0x9C3F; //=39999 |
30 | |
31 | sec=0; |
32 | min=0; |
33 | hor=0; |
34 | count=0; |
35 | |
36 | uart_puts("\nGeben Sie die aktuelle Stunden ein.\n"); |
37 | _delay_ms(5); |
38 | uart_gets(hours, 5); |
39 | _delay_ms(5); |
40 | uart_puts("\nGeben Sie die aktuellen Minuten ein.\n"); |
41 | _delay_ms(5); |
42 | uart_gets(minuts, 5); |
43 | _delay_ms(5); |
44 | uart_puts("\nGeben Sie die aktuellen Sekunden ein.\n"); |
45 | _delay_ms(5); |
46 | uart_gets(seconds, 5); |
47 | _delay_ms(5); |
48 | uart_puts("\n"); |
49 | _delay_ms(5); |
50 | |
51 | sec=atoi(seconds); |
52 | min=atoi(minuts); |
53 | hor=atoi(hours); |
54 | |
55 | OCR1AH = comp >> 8; |
56 | OCR1AL = comp; |
57 | |
58 | sei(); //enable global(!) interrupts |
59 | |
60 | TCCR1B |= (1<<WGM12); //compare enable |
61 | |
62 | TIMSK |= (1<<OCIE1A); //compare enable too! |
63 | |
64 | TCCR1B |= (1<<CS10); //CPU/1 |
65 | |
66 | TIMSK |= (1<<TOIE1); //enable interrupts |
67 | |
68 | while(1) |
69 | {
|
70 | if((PINC & 0x01)==0) //Wenn Taster 0 gedrückt |
71 | {
|
72 | cli(); //disable global(!) interrupts |
73 | TCCR1B &= ~(1<<CS10); |
74 | goto stellen; |
75 | };
|
76 | };
|
77 | |
78 | return 0; |
79 | };
|
80 | |
81 | ISR(TIMER1_COMPA_vect) |
82 | {
|
83 | count=count+1; //important! |
84 | |
85 | if(count==100) //(CPU/1/comp)=100, important! |
86 | {
|
87 | count=0; //important! |
88 | sec=sec+1; |
89 | |
90 | if(sec==60) |
91 | {
|
92 | sec=0; |
93 | min=min+1; |
94 | };
|
95 | |
96 | if(min==60) |
97 | {
|
98 | min=0; |
99 | hor=hor+1; |
100 | };
|
101 | |
102 | if(hor==24) |
103 | hor=0; |
104 | |
105 | |
106 | itoa(sec, seconds, 10); |
107 | |
108 | uart_puts(seconds); |
109 | };
|
110 | };
|
habe es grad am Laufen, werde demnächst berichten, ob immernoch eine "Zweitverschiebung" auftritt. Der Quaz ist ein standartquaz, NSK 4,000. mfg utzer
dein standardquarz schwingt nicht exakt bei 4MHz, von denen du in deiner berechnung ausgehst. zudem ist die schwingfrequenz temperaturabhängig. wenn das auch nur einige Hz sind, innerhalb von ein paar stunden summiert sich das hoch, und du siehst auf deiner uhr eine "zeitverschiebung". zu deinem code: 1. lass die goto-befehle. wenn man das einmal im programm macht, naja, aber bei wiederholtem gebrauch kannst ganz schnell übelsten spaghetti-code schreiben, der erstens total unleserlich wird, und zweitens du keine chance beim debuggen hast, weil du nichtmehr weißt, wo dein prozessor gerade steht. lager das uhr stellen in eine funktion (z.B. void setclock () ) aus, und ruf die an den entsprechenden stellen auf. 2. keinen langwierigen code in der isr. am besten nur kurz ein flag setzen und das in der hauptschleife im main abfragen. deine uart und itoa funktionen gehören zu solch einem langwierigen code. setz an der stelle in der isr ein flag (int bSecondFull = 1), und frag das dann in deiner while schleife ab (if (bSecondFull)). und mach deine ausgabe dann hier. 3. die funktion itoa kannst dir sparen. die braucht bloß ewig viel flash. mach dir die ascii-tabelle zu nutze: die zahl 0 hat ascii-code 48, 1 hat 49, etc. also der ascii-code, den du über usart schicken musst, ist deine ziffer plus 48. anbei code für einen 8 bit integer (nData), der über die usart übertragen werden soll: [c] USART_Transmit(48+nData/100); USART_Transmit(48+(nData/10)%10); USART_Transmit(48+nData%10); [\c] USART_Transmit() musst durch eine Funktion ersetzen, die ein 8-bit-zeichen überträgt. vg flo
> allerdings geht sie nach 5 Stunden bereits um ca 4 Sekunden nach.
Ein Verhältnis von 4sec/(5*3600)sec ergibt ca. 200ppm.
Für einen Standardquarz ist diese Abweichung zwar schon recht hoch
(üblich sind etwa 100ppm), aber Standardquarz ist ja auch nicht so
richtig definiert?
Zur Zeitmessung gibt es Uhrenquarze mit 32768Hz.
Die sind besser (ca. 10ppm).
Hallo, habe jetzt das ganze ein wenig umgeändert, aber zu Testzwecken ist immernoch itoa und atoi drin(macht das was wegen der Programmgenaugkeit? oder liegt das echt nur am Quarz?) Hab mit jetzt n Uhrenquarz bestellt. Muss ich dann den 4MHZ Quarz rausnehmen und durch den Uhrenquarz ersetzen, oder wie mache ich das? Hier mein aktuelles Programm:
1 | #include<avr/io.h> |
2 | #include<inttypes.h> |
3 | #ifndef F_CPU
|
4 | #define F_CPU 4000000UL
|
5 | #endif
|
6 | #include<util/delay.h> |
7 | #include<stdint.h> |
8 | #include <avr\interrupt.h> |
9 | #include "UART.h" |
10 | |
11 | volatile int sec; |
12 | volatile int min; |
13 | volatile int hor; |
14 | volatile int count; |
15 | volatile int secondfull; |
16 | |
17 | volatile char seconds[5]; |
18 | volatile char minuts[5]; |
19 | volatile char hours[5]; |
20 | |
21 | ISR(TIMER1_COMPA_vect) |
22 | {
|
23 | count=count+1; //important! |
24 | |
25 | if(count==100) //(CPU/1/comp)=100, important! |
26 | {
|
27 | count=0; //important! |
28 | secondfull=1; |
29 | };
|
30 | };
|
31 | |
32 | void UhrStellen() |
33 | {
|
34 | uart_puts("\nGeben Sie die aktuelle Stunden ein.\n"); |
35 | _delay_ms(5); |
36 | uart_gets(hours, 5); |
37 | _delay_ms(5); |
38 | uart_puts("\nGeben Sie die aktuellen Minuten ein.\n"); |
39 | _delay_ms(5); |
40 | uart_gets(minuts, 5); |
41 | _delay_ms(5); |
42 | uart_puts("\nGeben Sie die aktuellen Sekunden ein.\n"); |
43 | _delay_ms(5); |
44 | uart_gets(seconds, 5); |
45 | _delay_ms(5); |
46 | uart_puts("\n"); |
47 | _delay_ms(5); |
48 | |
49 | sec=atoi(seconds); |
50 | min=atoi(minuts); |
51 | hor=atoi(hours); |
52 | |
53 | sei(); |
54 | };
|
55 | |
56 | void ZeitAnzeigen() |
57 | {
|
58 | itoa(sec, seconds, 10); |
59 | itoa(min, minuts, 10); |
60 | itoa(hor, hours, 10); |
61 | |
62 | uart_puts("\n"); |
63 | uart_puts(hours); |
64 | uart_puts(":"); |
65 | uart_puts(minuts); |
66 | uart_puts(":"); |
67 | uart_puts(seconds); |
68 | uart_puts("\n"); |
69 | };
|
70 | |
71 | int main(void) |
72 | {
|
73 | DDRB=0xFF; |
74 | DDRC=0x00; |
75 | PORTB=0xFF; |
76 | |
77 | uint16_t comp; |
78 | comp=0x9C3F; //=39999 |
79 | |
80 | sec=0; |
81 | min=0; |
82 | hor=0; |
83 | count=0; |
84 | secondfull=0; |
85 | |
86 | UhrStellen(); |
87 | |
88 | OCR1AH = comp >> 8; |
89 | OCR1AL = comp; |
90 | |
91 | sei(); //enable global(!) interrupts |
92 | |
93 | TCCR1B |= (1<<WGM12); //compare enable |
94 | |
95 | TIMSK |= (1<<OCIE1A); //compare enable too! |
96 | |
97 | TCCR1B |= (1<<CS10); //CPU/1 |
98 | |
99 | TIMSK |= (1<<TOIE1); //enable interrupts |
100 | |
101 | while(1) |
102 | {
|
103 | if((PINC & 0x01)==0) |
104 | {
|
105 | cli(); //disable global(!) interrupts |
106 | UhrStellen(); |
107 | };
|
108 | |
109 | if(secondfull==1) //(CPU/1/comp)=100, important! |
110 | {
|
111 | sec=sec+1; |
112 | secondfull=0; |
113 | |
114 | if(sec==60) |
115 | {
|
116 | sec=0; |
117 | min=min+1; |
118 | };
|
119 | |
120 | if(min==60) |
121 | {
|
122 | min=0; |
123 | hor=hor+1; |
124 | };
|
125 | |
126 | if(hor==24) |
127 | hor=0; |
128 | |
129 | ZeitAnzeigen(); |
130 | };
|
131 | };
|
132 | |
133 | return 0; |
134 | };
|
mfg utzer PS: vielen Dank für die Antworten
Doran Strehnisch wrote: > Hab mit jetzt n Uhrenquarz bestellt. > Muss ich dann den 4MHZ Quarz rausnehmen und durch den Uhrenquarz > ersetzen, Das ist Quatsch. Ein Uhrenquarz brauchst Du nur, wenn es aufs Stromsparen ankommt (Batteriebetrieb). Uhrenquarze mögen zwar ohne Abgleich etwas genauer sein, sind aber nicht so konstant wie MHz-Quarze. Z.B. mögen sie keine großen Temperaturschwankungen (möglichst konstante Zimmertemperatur). Peter
Doran Strehnisch wrote: > Hallo, > > habe jetzt das ganze ein wenig umgeändert, aber zu Testzwecken ist > immernoch itoa und atoi drin(macht das was wegen der Programmgenaugkeit? > oder liegt das echt nur am Quarz?) Hab mit jetzt n Uhrenquarz bestellt. > Muss ich dann den 4MHZ Quarz rausnehmen und durch den Uhrenquarz > ersetzen, oder wie mache ich das? Du beobachtest deine Uhr eine Zeitlang und rechnest dir dann aus, wie schnell der Quarz wirklich schwingt. Danach werden die Timer-Konstanten angepasst, so dass der Interrupt mit der richtigen Häufigkeit aufgerufen wird.
Warum hinter jeden } ein ;? brauch C nicht cli und sei nicht verteilen. immer als Paare verwenden bzw noch besser http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Interrupts_mit_dem_AVR_GCC_Compiler_.28WinAVR.29 UND http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC
> (möglichst konstante Zimmertemperatur). Handgelenkstemperatur... >> den 4MHZ Quarz rausnehmen und durch den Uhrenquarz ersetzen... Wird wohl so ohne weiters nicht gehen. Da müsstest du hoch die ganze Quarzbeschaltung anpassen. Ich würde den Quarz einfach mal abgleichen, probier doch mal geringfügig andere Kondensatoren gegen GND. Siehe dazu auch den Beitrag "Ziehkondensator"
ok, werde die Uhr jetzt ein Zeit lang beobachten und dann die Werte angleichen. Vielen Dank für die Antworten. mfg utzer
> den 4MHZ Quarz rausnehmen und durch den Uhrenquarz ersetzen...
du kannst den Uhrenquarz als Taktgeber für den Timer2 des Atmega32
benutzen. Les mal im Datenblatt unter RTC (RealTimeClock) nach. Da ist
es genau beschrieben. Kurz gesagt: du hängst ihn an die PINS C6 und C7
und taktest damit den Timer2. Mit nem Prescaler von 128 kriegst du jede
Sekunde einen Timer-Overflow.
Hallo, > es genau beschrieben. Kurz gesagt: du hängst ihn an die PINS C6 und C7 > und taktest damit den Timer2. Mit nem Prescaler von 128 kriegst du jede > Sekunde einen Timer-Overflow. ok, danke, werde ich testen, wenn es anderst nicht funktioniert. mfg utzer
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.