Forum: Compiler & IDEs Uhr mit Mega32, allerdings ohne DCF77


von Doran S. (utzer)


Angehängte Dateien:

Lesenswert?

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

von Michael U. (amiga)


Lesenswert?

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

von Günter R. (galileo14)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

von Doran S. (utzer)


Lesenswert?

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

von Florian D. (Gast)


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> 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).

von Doran S. (utzer)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von ... .. (docean) Benutzerseite


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> (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"

von Doran S. (utzer)


Lesenswert?

ok, werde die Uhr jetzt ein Zeit lang beobachten und dann die Werte 
angleichen. Vielen Dank für die Antworten.

mfg

utzer

von Florian D. (Gast)


Lesenswert?

> 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.

von Doran S. (utzer)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

Und mal was Grundlegendes über Interrupts lesen . . .

von Hans-jürgen H. (hjherbert) Benutzerseite


Lesenswert?

Nimm doch die Netzfrequenz. Die ist für Uhren genau,

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.