Forum: Mikrocontroller und Digitale Elektronik Timingprobleme Uhr


von Valentin B. (nitnelav) Benutzerseite


Lesenswert?

Hallo,
ich hab mal wieder ein Problem mit der richtigen Anwendung von C.
Es handelt sich um eine einfache Uhr.
Bei meiner Hardware handelt es sich um Stromversorgung,ein 
industriestandard-kompatibles LCD, einen 3,6864 Mhz Quarz mit 22pf 
Kerkos und einen ATMEGA-8.
Ich wollte die Uhr mit einem Timerinterrupt von dem 16-bit Timer 1 
laufen lassen. Da ich 3,6864 Mhz habe, nehme ich also einen Prescaler 
von 1024 und einen Compare-Wert von 1799.
In der Hauptschleife lasse ich noch die Routinen für ein LCD laufen (mit 
den Routinen aus dem GCC-Tutorial).
Im Interrupt setze ich nur einen Wert, den ich dann im Mainloop 
verarbeite, also prizipiell mache ich da nicht viel.
Trotzdem scheint die Uhr etwa 50% zu langsam zu sein.
Woran könnte das liegen?
Hier mein Quellcode:
1
#define F_CPU 3686400
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
#include <stdlib.h>
6
#include "lcd-routines.h"
7
8
char c_hours, c_minutes, c_seconds; //Die aktuelle Zeit
9
10
char counter1; //Eine Variable, die anzeigt, ob der Timer gepiept hat
11
12
ISR(TIMER1_COMPA_vect){
13
counter1 ++; //Variable setzen
14
}
15
16
void initTimer(){
17
OCR1A = 1799; //Um auf 1Hz zu kommen
18
TCCR1B =  (1<<WGM12) | (1<<CS12) | (1<<CS10); //Prescaler 1024, CTC-Mode
19
TIMSK = (1<<OCIE1A);//Timer-Interrupt aktivieren
20
sei();//Interrupts erlauben
21
}
22
23
void init(){
24
lcd_init();
25
initTimer();
26
lcd_string("Clocktest:");
27
}
28
29
void clocktick(){
30
c_seconds ++;//Bei jedem Tick die Sekunden erhöhen
31
32
if (c_seconds == 60){//Wenn 60 Sekunden um sind, Minute ++
33
c_seconds = 0;
34
c_minutes ++;}
35
36
if (c_minutes == 60){//Wenn 60 Minuten um sind, Stunde ++
37
c_minutes = 0;
38
c_hours ++;}
39
40
if (c_hours == 24){//Wenn 24 Stunden rum sind, neuer Tag
41
c_hours = 0;}
42
}
43
44
45
void lcd_ausgabe(){
46
char buffer[80];
47
sprintf(buffer, "%d : %d : %d", c_hours, c_minutes, c_seconds);  
48
lcd_setcursor(0,2);
49
lcd_string(buffer);
50
}
51
52
void lcdausgabe(){
53
lcd_setcursor(0,2);
54
char stunde[10];
55
itoa(c_hours, stunde, 10);
56
lcd_string(stunde);
57
lcd_string(" : ");
58
char minute[10];
59
itoa(c_minutes, minute, 10);
60
lcd_string(minute);
61
lcd_string(" : ");
62
char sekunde[10];
63
itoa(c_seconds, sekunde, 10);
64
lcd_string(sekunde);
65
}
66
67
void timer_tick(){
68
clocktick();
69
70
}
71
72
int main(){
73
init();
74
while(1){
75
if (counter1 == 1){
76
timer_tick();
77
counter1 = 0;}
78
lcdausgabe();
79
_delay_ms(100);
80
}
81
return 0;
82
}
Dass ich die timer_tick-Routine eingebaut habe, kommt daher, dass ich 
noch eine Wecker-Funktion einbauen möchte.
Die beiden Anzeige-Routinen rühren daher her, dass ich ein wenig damit 
experimentiert habe, alle Operationen in eine sprintf-Anweisung zu 
packen. Leider scheint das noch langsamer zu sein...
Könnte das Zeitproblem vielleicht etwas mit den _delay_ms(100) zu tun 
haben?
Ich würde mich freuen, wenn ihr mir weiterhelfen könntet.
Mit freundlichen Grüßen,
Valentin Buck

von Karl H. (kbuchegg)


Lesenswert?

Valentin Buck schrieb:

> Ich wollte die Uhr mit einem Timerinterrupt von dem 16-bit Timer 1
> laufen lassen. Da ich 3,6864 Mhz habe, nehme ich also einen Prescaler
> von 1024 und einen Compare-Wert von 1799.

Wie kommst du auf 1799?

Wenn dein Haupttakt 3686400 Takte pro Sekunde sind
und du einen Vorteiler von 1024 benutzt, dann macht der
Timer logischerweise 3686400 / 1024 = 3600 Zählvorgänge in der Sekunde 
und nicht 1800

Tada. Da ist dein Faktor 2

von Falk B. (falk)


Lesenswert?

Dein counter1 muss volatile sein, siehe Interrupt.

MFG
Falk

von Valentin B. (nitnelav) Benutzerseite


Lesenswert?

Ich hab mal die Interrupts in einer Minute gezählt.
Da kamen statt 60 nur 30 raus.
Ich hab dann mal den OCR-Wert halbiert(also auf 899) - und es 
geht(fast)!
Leider jetzt 6 Sekunden zu schnell...

@Karl heinz Buchegger:
Laut der Formel im Datenblatt ist die Frequenz des CTCs
    f_clk
f= ----------------------
   2*Prescaler*(1+OCRnA)

-> 1799
Geht irgendwie aber nicht

@Falk Brunner
Danke, jetzt funktionierts auch!

Allerdings mache ich mir auch noch Sorgen um die LCD-Anzeige:
Wie kann ich statt
0 : 0 : 0
00 : 00 : 00
anzeigen lassen?
Im Moment wird, sobald die Sekunden einmal durch sind immer nur die 
erste Stelle geändert. Die 9 bleibt steht stehen!
Könnt ihr mit da helfen?

mit freundlichen Grüßen,
Valentin Buck

von Karl H. (kbuchegg)


Lesenswert?

Valentin Buck schrieb:
> Ich hab mal die Interrupts in einer Minute gezählt.
> Da kamen statt 60 nur 30 raus.
> Ich hab dann mal den OCR-Wert halbiert(also auf 899) - und es
> geht(fast)!
> Leider jetzt 6 Sekunden zu schnell...

Die Zahl ist komplett daneben.

Kontrolliere mal, ob dein Quarz überhaupt aktiv ist.


> @Karl heinz Buchegger:
> Laut der Formel im Datenblatt ist die Frequenz des CTCs
>     f_clk
> f= ----------------------
>    2*Prescaler*(1+OCRnA)

Du schreibst jetzt 100 mal:
Ich soll nicht einfach stumpfsinnig Fomeln aus dem Datenblatt 
abschreiben.


Die Formel ist gedacht für

ISR( ... )
{
  toggle Output Pin
}

Da daher 2 ISR Aufrufe erfolgen müssen, um 1 Schwingung zu erzeugen, 
taucht der 2er in der Formel auf.

Die Wette steht, dass dein Quarz zur Zeit noch gar nicht aktiv ist, d.h. 
dein µC noch mit rund 1Mhz arbeitet. Probiers mal mit 976, da wirds dann 
ziemlich gut stimmen (oder auch nicht, je nach Umgebungstemperatur)

1000000 / 1024 = 976.56

> Allerdings mache ich mir auch noch Sorgen um die LCD-Anzeige:
> Wie kann ich statt
> 0 : 0 : 0
> 00 : 00 : 00
> anzeigen lassen?

  sprintf( buffer, "%02d:%02d:%02d", stunde, minute, sekunde );

(Du brauchst auch noch ein C-Buch)

von Valentin B. (nitnelav) Benutzerseite


Lesenswert?

Ohhhh, natürlich!
Der externe Quarz...

Jetzt gehts!
Mit den 3600.
Ich glaube, ich habe verstanden, wie man die Zyklen berechnet.

Die Ausgabe hab ich jetzt auch geändert - funktioniert auch!

Welches C-Buch ist denn gut?
(bisher orientierte ich mich eher am Internet,
aber was auf Papier ist doch besser...)

Mit freundlichen Grüßen,
Valentin Buck

von spess53 (Gast)


Lesenswert?

Hi

>Mit den 3600.

Genau sind es 3599.

MfG Spess

von Falk B. (falk)


Lesenswert?

@Valentin Buck (nitnelav)

>Welches C-Buch ist denn gut?

Die Klassiker, K&R, Programmieren in C. Oder einen der diversen Ableger, 
sind auch OK.

http://www.mikrocontroller.net/buecher/

MFG
Falk

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.