Timingprobleme Uhr

OP Persönliche Seite #1714208
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
#1714228
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
OP Persönliche Seite #1714266
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
#1714277
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)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren