www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Timingprobleme Uhr


Autor: Valentin Buck (nitnelav) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
#define F_CPU 3686400
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include "lcd-routines.h"

char c_hours, c_minutes, c_seconds; //Die aktuelle Zeit

char counter1; //Eine Variable, die anzeigt, ob der Timer gepiept hat

ISR(TIMER1_COMPA_vect){
counter1 ++; //Variable setzen
}

void initTimer(){
OCR1A = 1799; //Um auf 1Hz zu kommen
TCCR1B =  (1<<WGM12) | (1<<CS12) | (1<<CS10); //Prescaler 1024, CTC-Mode
TIMSK = (1<<OCIE1A);//Timer-Interrupt aktivieren
sei();//Interrupts erlauben
}

void init(){
lcd_init();
initTimer();
lcd_string("Clocktest:");
}

void clocktick(){
c_seconds ++;//Bei jedem Tick die Sekunden erhöhen

if (c_seconds == 60){//Wenn 60 Sekunden um sind, Minute ++
c_seconds = 0;
c_minutes ++;}

if (c_minutes == 60){//Wenn 60 Minuten um sind, Stunde ++
c_minutes = 0;
c_hours ++;}

if (c_hours == 24){//Wenn 24 Stunden rum sind, neuer Tag
c_hours = 0;}
}


void lcd_ausgabe(){
char buffer[80];
sprintf(buffer, "%d : %d : %d", c_hours, c_minutes, c_seconds);  
lcd_setcursor(0,2);
lcd_string(buffer);
}

void lcdausgabe(){
lcd_setcursor(0,2);
char stunde[10];
itoa(c_hours, stunde, 10);
lcd_string(stunde);
lcd_string(" : ");
char minute[10];
itoa(c_minutes, minute, 10);
lcd_string(minute);
lcd_string(" : ");
char sekunde[10];
itoa(c_seconds, sekunde, 10);
lcd_string(sekunde);
}

void timer_tick(){
clocktick();

}

int main(){
init();
while(1){
if (counter1 == 1){
timer_tick();
counter1 = 0;}
lcdausgabe();
_delay_ms(100);
}
return 0;
}
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein counter1 muss volatile sein, siehe Interrupt.

MFG
Falk

Autor: Valentin Buck (nitnelav) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Valentin Buck (nitnelav) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

>Mit den 3600.

Genau sind es 3599.

MfG Spess

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.