Forum: Mikrocontroller und Digitale Elektronik Timer (Uhrzeit) ist ungenau


von Christoph Hess (Gast)


Lesenswert?

Hi, ich verwende einen MEGA8535.

Der Timer Interrupt sieht bei mir so aus:

// Timer 0 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
 if (zehntelsek < 9) ++zehntelsek;
  else
     {
     zehntelsek = 0;
     if (sekunden < 59) ++sekunden;
     else
        {
        sekunden = 0;
        if (minuten < 59) ++minuten;
        else
           {
           minuten = 0;
           if (stunden < 23) ++stunden;
           else
              {
              stunden = 0;
              if (wochentag < 6) ++wochentag;
              else wochentag = 0;
              if ((monat == 1)||(monat == 3)||(monat == 5)||
                  (monat == 7)||(monat == 8)||(monat == 10))
                  {
                  if (tag < 31) ++tag;
                  else
                     {
                     tag = 1;
                     ++monat;
                     }
                  }
              else if (monat == 12)
                  {
                  if (tag < 31) ++tag;
                  else
                     {
                     tag = 1;
                     monat = 1;
                     ++jahr;
                     }
                  }

              else if (monat == 2)
                  {
                  if (((jahr%4==0) && (jahr%100!=0)) || (jahr%400==0))
                     {
                     if (tag < 29) ++tag;
                     else
                        {
                        tag = 1;
                        ++monat;
                        }
                     }
                  else
                     {
                     if (tag < 28) ++tag;
                     else
                        {
                        tag = 1;
                        ++monat;
                        }
                     }
                  }
              else
                  {
                  if (tag < 30) ++tag;
                  else
                     {
                     tag = 1;
                     ++monat;
                     }
                  }
              }
           }
        }
     }
TCNT1H=0xCF;
TCNT1L=0x2C;
}

void main(void)
{
TCCR1A=0x00;
TCCR1B=0x03;
TCNT1H=0xCF;
TCNT1L=0x2C;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

TIMSK=0x04;

#asm("sei")

while (1)#
 {
  Ausgabe....
 }
}
Mein Board ist mit einem 8Mhz Quarz bestückt.
Wie kann ich meine Uhr genauer machen, ohne dass ich einen anderen
Timer Interrupt verwenden muss?

von Der2te_Michael (Gast)


Lesenswert?

Hi,

also ich würde in der ISR nur ein Flag setzen, und die Auswertung nicht
in der ISR ausführen, sondern mittels einer Funktion die aus der
main-loop aufgerufen wird. Denn es werden vorm Reloaden der
Timerregister alle Befehle abgearbeitet, was Zeit kostet und die Uhr
ungenau macht.

Oder du zählst die verbrauchten Takte in der ISR für die Abfrage und
ziehst diese von den Reloadwerten ab.

Grüße,

Der2te_Michael

von Peter Dannegger (Gast)


Lesenswert?

Jede Codezeile benötigt Zeit für die Ausführung !!!

Deshalb muß, einen Timer zu setzen nach einer rein willkürlichen
Ausführungszeit, zwangsläufig ungenau sein.

Hinzukommen noch bei größeren Programmen Verzögerungen durch andere
Interrupts.

Und genau deshalb haben die AVR-Entwickler die "Clear on Compare
Match" Funktion eingebaut. Nutze sie !


Außerdem sind MC-Quarze in der Regel etwas schneller als die
Nennfrequenz, d.h. entweder genau messen oder ne Weile laufen lassen,
ausstoppen und dann den Comparewert korrigieren.


Peter

von Jens D. (jens) Benutzerseite


Angehängte Dateien:

Lesenswert?

ich wuerde evtl auch hin gehen und einen 2ten externen quarz nehmen,
wenn es eine uhr werden soll,,

ich hatte sowas mal in asm gemacht mit einem LCD Display..

im anhang findest du das ASM programm etwas kommentiert nur achte bitte
nicht darauf, wie ich die bits gesetzt habe
war meuin erstes ASM programm seit 5 jahren ;)

Gruss Jens

von Jens D. (jens) Benutzerseite


Lesenswert?

okok
bevor ihr mich nun schlagen tut =)

ist auch fast alles im int handler :(
aber genau lief sie..
3 tage laufen lassen mit dem gong einer uhr gestartet und immer mit dem
gong auf 0:0:0 gewesen;)

von Christoph Hess (Gast)


Lesenswert?

Wie und wo muss ich in meiner oben aufgeführten Interrupt Routine was
verändern?

von Peter Dannegger (Gast)


Lesenswert?

"bevor ihr mich nun schlagen tut =)"

Ja wollen wir.
Was sollen wir denn mit einem Hex-File anfangen ?


Peter

von Jadeclaw (Gast)


Lesenswert?

@Christoph:

TCNT1H=0xCF;
TCNT1L=0x2C;

Diese zwei Teile DIREKT als ERSTES in der Interrupt-Routine
aufrufen.

Dann wird der Timer IMMER mit einer definierten und gleichbleibenden
Verzögerung neu gesetzt.
Zum Abgleich dann TCNT1L tunen.

Gruss
Jadeclaw.

von Michael Wilhelm (Gast)


Lesenswert?

Etwas grundsätzliches zu der Interrupt-Routine:

da die Bedingungen, die abgearbeitet werden müssen, nicht konstant
sind, fütter den Timer am Anfang der Routine mit den Reload-Werten. Und
bei 10 Einsprüngen in der Sekunde soll die Abarbeitungsgeschwindigkeit
schon reichen.

Eleganzer ist aber auf alle Fälle Peters Vorschlag mit dem Clear on
Compare. Wenn die Hardware es anbietet, warum nicht nutzen?

Und im Assemblerlisting nachschauen, wieviel Pops und Pushes in der
Routine vom Compiler eingesetzt werden, genauso die 4 (?) Takte, die
der Controller braucht, um die Routine anzusprechen.

MW

von Niels H. (monarch)


Lesenswert?

Besser noch ist es die Autoreload-funktion des Timers zu nutzen, als den
wert "händisch" in der Int-routine zu setzen. Bis der erste Codeteil
der Introutine ausgeführt wird, vergehen immerhin 40 microsekunden.

von Jens D. (jens) Benutzerseite


Angehängte Dateien:

Lesenswert?

ooooooooopSS
verzeihung =)

eins zu tief geklickt

hier nun die ASM datei

von Niels H. (monarch)


Lesenswert?

Zu dem Quellcode hab ich ne Frage bezüglich der Interrupt Vektoren:

Ist es nicht so, das ein reti ein ein-byte-Befehl ist und ein rjmp ein
zwei-byte-befehlt?

Ist nicht demnach die Vektortabelle absolut korrupt?

von Jens D. (jens) Benutzerseite


Lesenswert?

verwchselst du nicht zwichen 1 takt und 2 takt befehlen??

bei einem 8 BIT µC wird es kaum mehr als 256 Befehle geben, darum ist
jeder befehl 1 bit lang

womit sollte man sonnst aus der tabelle herrauskommen??
da steht nur mach das dahin die tabelle ist dafuer ausgelegt

schau dir mal hier das tutorial einfach an ;)

Gruss Jens

von Niels H. (monarch)


Lesenswert?

Während der reti-Befehl ohne parameter auskommt, ist er nur ein Byte
lang. Der rjmp-befehl braucht aber ein parameter, nämlich die
sprungweite. Daher besteht ein rjmp nun aus dem Machinencode für
"rjmp" + Parameter Sprungweite, ein weiteres Byte. Macht zwei byte.

So wars zumindest bei den 8031/51ern....

Ich konnte mir meine Frage schon zwischenzeitlich selbst beantworten.
 Bei den AVR RISC ist jeder befehl+eventueller parameter IMMER 16bit
breit. Auch ein reti.

Von daher ist die Vektortabelle auch valide.

von TravelRec. (Gast)


Lesenswert?

Einfachste Variante für eine genaue Uhr: Timer-Prescaler so einstellen,
daß sich aus Quarzfrequenz dividiert durch Prescaler eine ganze Zahl
ohne Kommastellen ergibt. Dann das Timer-Compare Register mit einem
möglichst hohen Wert laden, durch den die zuvor erhaltene Zahl
dividiert, wieder zu eine ganze Zahl ergibt. Dann den Timer auf "Clear
on Compare match" (CTC-mode) einstellen und den Interrupt bei compare
match auslösen. In dem Interrupt dann einen Zähler incrementieren und
auf die Restzahl hin vergleichen und wenn sie erreicht ist, den Zähler
zurücksetzen und so weiter. So erhält man eine sehr genaue Sekunde, nur
abhängig von der Quarzgenauigkeit und völlig unbeeindruckt vom Code.

von Jochen (Gast)


Lesenswert?

ich hab gedacht der timer läuft über -> interrupt -> timer läuft aber
gleich wieder weiter (verwende allerdings kein AVR)

von Jochen (Gast)


Lesenswert?

ach jetzt da, hier wird keine autoreload genommen oder??

von Niels H. (monarch)


Lesenswert?

Nein, kein Autoreload.

Und das retten der register per push und pop ist IMO auch nicht ganz
korrekt platziert....

von Christoph Hess (Gast)


Lesenswert?

Hallo,

ich habe ein Beispiel gesehen von Peter Danneger:
SIGNAL (SIG_OUTPUT_COMPARE1A)
{
/*********************************************************************** 
*/
/*      Insert Key Debouncing Here      */
/*********************************************************************** 
*/

#if XTAL % DEBOUNCE                     // bei rest
  OCR1A = XTAL / DEBOUNCE - 1;    // compare DEBOUNCE - 1 times
#endif
  if( --prescaler == 0 ){
    prescaler = (uchar)DEBOUNCE;
    second++;        // exact one second over
#if XTAL % DEBOUNCE      // handle remainder
    OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per
second
#endif
  }
}

Diesen Code habe ich an meine Applikation angepasst. Dabei habe ich die
If und Endif anweisungen entfernt. Stimmt dann dieser Code so:
SIGNAL (SIG_OUTPUT_COMPARE1A)
{
/*********************************************************************** 
*/
/*      Insert Key Debouncing Here      */
/*********************************************************************** 
*/

#if XTAL % DEBOUNCE                     // bei rest
  OCR1A = XTAL / DEBOUNCE - 1;    // compare DEBOUNCE - 1 times
#endif
  if( --prescaler == 0 ){
    prescaler = (uchar)DEBOUNCE;
    second++;        // exact one second over
#if XTAL % DEBOUNCE      // handle remainder
    OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per
second
#endif
  }
}

von Christoph Hess (Gast)


Lesenswert?

Sorry mein Code sieht dann so aus:
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
 if (XTAL % DEBOUNCE)                     // bei rest
  OCR1A = XTAL / DEBOUNCE - 1;    // compare DEBOUNCE - 1 times

  if( --prescaler == 0 )
   {
    prescaler = DEBOUNCE;
    sekunde++;
    Uhrzeit(sekunde);        // exact one second over
if(XTAL % DEBOUNCE)      // handle remainder
    OCR1A = XTAL / DEBOUNCE + XTAL % DEBOUNCE - 1; // compare once per
second
   }
}

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.