www.mikrocontroller.net

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


Autor: Doran S. (utzer)
Datum:
Angehängte Dateien:

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

Autor: Michael U. (amiga)
Datum:

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

Autor: Günter R. (galileo14)
Datum:

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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

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

Autor: Doran S. (utzer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe nun mein Programm mal zu Testzwecken einwenig geändert:
#include<avr/io.h>
#include<inttypes.h>
#ifndef F_CPU
#define F_CPU 4000000UL
#endif
#include<util/delay.h>
#include<stdint.h>
#include <avr\interrupt.h>
#include "UART.h"

  volatile int sec;
  volatile int min;
  volatile int hor;
  volatile int count;

  volatile char seconds[5];
  volatile char minuts[5];
  volatile char hours[5];

int main(void)
{
stellen:

  DDRB=0xFF;
  DDRC=0x00;
  PORTB=0xFF;

  uint16_t comp;
  comp=0x9C3F;  //=39999

  sec=0;
  min=0;
  hor=0;
  count=0;

  uart_puts("\nGeben Sie die aktuelle Stunden ein.\n");
  _delay_ms(5);
  uart_gets(hours, 5);
  _delay_ms(5);
  uart_puts("\nGeben Sie die aktuellen Minuten ein.\n");
  _delay_ms(5);
  uart_gets(minuts, 5);
  _delay_ms(5);
  uart_puts("\nGeben Sie die aktuellen Sekunden ein.\n");
  _delay_ms(5);
  uart_gets(seconds, 5);
  _delay_ms(5);
  uart_puts("\n");
  _delay_ms(5);

  sec=atoi(seconds);
  min=atoi(minuts);
  hor=atoi(hours);

  OCR1AH = comp >> 8;
  OCR1AL = comp;
  
  sei();          //enable global(!) interrupts

  TCCR1B |= (1<<WGM12);  //compare enable

  TIMSK |= (1<<OCIE1A);  //compare enable too!

  TCCR1B |= (1<<CS10);  //CPU/1

  TIMSK |= (1<<TOIE1);  //enable interrupts

  while(1)
  {
    if((PINC & 0x01)==0)      //Wenn Taster 0 gedrückt
    {
      cli();      //disable global(!) interrupts
      TCCR1B &= ~(1<<CS10);
      goto stellen;
    };
  };

  return 0;
};

ISR(TIMER1_COMPA_vect)
{
  count=count+1;    //important!

  if(count==100)    //(CPU/1/comp)=100, important!
  {
    count=0;    //important!
    sec=sec+1;

      if(sec==60)
      {
        sec=0;
        min=min+1;
      };

      if(min==60)
      {
        min=0;
        hor=hor+1;
      };

      if(hor==24)
      hor=0;
      

      itoa(sec, seconds, 10);

      uart_puts(seconds);
  };
};

habe es grad am Laufen, werde demnächst berichten, ob immernoch eine 
"Zweitverschiebung" auftritt. Der Quaz ist ein standartquaz, NSK 4,000.

mfg

utzer

Autor: Florian D. (Gast)
Datum:

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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

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

Autor: Doran S. (utzer)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include<avr/io.h>
#include<inttypes.h>
#ifndef F_CPU
#define F_CPU 4000000UL
#endif
#include<util/delay.h>
#include<stdint.h>
#include <avr\interrupt.h>
#include "UART.h"

  volatile int sec;
  volatile int min;
  volatile int hor;
  volatile int count;
  volatile int secondfull;

  volatile char seconds[5];
  volatile char minuts[5];
  volatile char hours[5];

ISR(TIMER1_COMPA_vect)
{
  count=count+1;    //important!

  if(count==100)    //(CPU/1/comp)=100, important!
  {
    count=0;    //important!
    secondfull=1;
  };
};

void UhrStellen()
{
  uart_puts("\nGeben Sie die aktuelle Stunden ein.\n");
  _delay_ms(5);
  uart_gets(hours, 5);
  _delay_ms(5);
  uart_puts("\nGeben Sie die aktuellen Minuten ein.\n");
  _delay_ms(5);
  uart_gets(minuts, 5);
  _delay_ms(5);
  uart_puts("\nGeben Sie die aktuellen Sekunden ein.\n");
  _delay_ms(5);
  uart_gets(seconds, 5);
  _delay_ms(5);
  uart_puts("\n");
  _delay_ms(5);

  sec=atoi(seconds);
  min=atoi(minuts);
  hor=atoi(hours);

  sei();
};

void ZeitAnzeigen()
{
  itoa(sec, seconds, 10);
  itoa(min, minuts, 10);
  itoa(hor, hours, 10);
  
  uart_puts("\n");
  uart_puts(hours);
  uart_puts(":");
  uart_puts(minuts);
  uart_puts(":");
  uart_puts(seconds);
  uart_puts("\n");
};

int main(void)
{
  DDRB=0xFF;
  DDRC=0x00;
  PORTB=0xFF;

  uint16_t comp;
  comp=0x9C3F;  //=39999

  sec=0;
  min=0;
  hor=0;
  count=0;
  secondfull=0;

  UhrStellen();

  OCR1AH = comp >> 8;
  OCR1AL = comp;
  
  sei();          //enable global(!) interrupts

  TCCR1B |= (1<<WGM12);  //compare enable

  TIMSK |= (1<<OCIE1A);  //compare enable too!

  TCCR1B |= (1<<CS10);  //CPU/1

  TIMSK |= (1<<TOIE1);  //enable interrupts

  while(1)
  {
    if((PINC & 0x01)==0)
    {
      cli();      //disable global(!) interrupts
      UhrStellen();
    };

    if(secondfull==1)    //(CPU/1/comp)=100, important!
    {
      sec=sec+1;
      secondfull=0;

      if(sec==60)
      {
        sec=0;
        min=min+1;
      };

      if(min==60)
      {
        min=0;
        hor=hor+1;
      };

      if(hor==24)
      hor=0;
      
      ZeitAnzeigen();
    };
  };

  return 0;
};

mfg

utzer

PS: vielen Dank für die Antworten

Autor: Peter Dannegger (peda)
Datum:

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

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

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

Autor: ... ... (docean) Benutzerseite
Datum:

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

UND
http://www.mikrocontroller.net/articles/AVR_-_Die_...

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

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

Autor: Doran S. (utzer)
Datum:

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

mfg

utzer

Autor: Florian D. (Gast)
Datum:

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

Autor: Doran S. (utzer)
Datum:

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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und mal was Grundlegendes über Interrupts lesen . . .

Autor: Hans-jürgen Herbert (hjherbert) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nimm doch die Netzfrequenz. Die ist für Uhren genau,

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.