Forum: Mikrocontroller und Digitale Elektronik atmega8-Timer1 zu langsam


von Flo (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
ich bin noch recht neu dabei, was µCs angeht, aber ich dachte mir ich 
bau mir (so als erste Projekt) eine eigene Funk-Uhr, die die Zeit dann 
auch gleich auf einem Siebensegment-Display anzeigt. Das hat auch alles 
so weit funktioniert: Das DCF77-Signal konnte ich ohne Probleme 
empfangen und entschlüsseln und das Multiplexing der 6 
Siebensegment-Elemente hat auch gut gefunzt. Aber das eingeständige 
Weiterzählen auch ohne DCF77 hängt grad ein bisschen.

Also erstmal mein allgeminer Aufbau:
Ein Atmega8 mit einem externem 8Mhz Quarz. Über SPI ist ein 
Schieberegister angeschlossen, welches die Ziffern an die 
7Segmentanzeigen weiter gibt. Diese sind jeweils zwischen Pin5 und Pin0 
des PORTC angeschlossen und werden darüber gemultiplext. Es sind 
7Segmentanzeigen mit einer CommonAnode.

Zum Multiplexen nutze ich den Timer1 der mit 300Hz zwischen den 
einzelnen Anezigen hinunherschaltet.
Das DCF77-SIgnal taste ich (eigendlich) mit einer 1kHz-Frequenz ab. In 
der selben Routine wird auch die interne Zeit gezählt. Das sollte 
eigendlich über den Timer0 laufen, jedoch läuft er auf einmal zu 
langsam, er ist nur halb so schnell wkie eigendlich gedacht.

Ich weiss, dass sowas viele Ursachen haben kann aber ich hoffe trozdem, 
dass ihr mir helfen könnt.

Mein Code ist im Anhang, und da ich noch ein Anfänger bin bitte reißt 
mir nich gleich den Kopf ab wenn es estwas absolut offensichtliches ist. 
Ich sitze jetzt schon lage davor und habe irgendwie die distanz 
verloren.

Vielen vielen Dank im Voraus
Grüße Flo

von Oliver (Gast)


Lesenswert?

> TCNT0 = (255-125);        //Nach 125 ticks ISR auslösen -> 1kHz

Das ist soweit in Ordnung.

Aber was passiert mit TCNT0 nach dem ersten Overflow?

Oliver

von Flo (Gast)


Lesenswert?

Ohhhhhhhhhh, mist! Ja klar, gehts.  Danke Olivier!!!

Aber irgendwie ist es noch recht ungenau.  Innerhalb von 20 Minuten 
läuft meine Uhr schon 10sec nach. Da ich ein Quarz benutze und da 
(8Mhz/(64*125)) ganzzahlig teilt müsste es doch einiger maßen genau 
sein, oder ?
Ist einfach mein Quarz immer noch nicht genau oder liegt das an einer 
schlechten Programmierung ? Kann ich das noch genauer bekommen ?


Vielen Dank

von chris (Gast)


Lesenswert?

nutz den CTC-Modus ist wie nen autorelaod in die ISR OCA/B packts du das 
was gerade bei der OVFL-ISR drinen steht.

von Falk B. (falk)


Lesenswert?

@ Flo (Gast)

>Ein Atmega8 mit einem externem 8Mhz Quarz.

Sind auch die AVR Fuses richtig gesetzt? Mit etwas Glück läuft dein 
AVR mit 8MHz internem RC-Takt und nicht mit dem Quarz.

>Aber irgendwie ist es noch recht ungenau.  Innerhalb von 20 Minuten
>läuft meine Uhr schon 10sec nach.

10s / (20*60s) = 0,8%, so ungenau it kein Quarz, ist rein physikalisch 
nicht möglich (außer der Oszillator ist vollkommen vermurkst).

>Das DCF77-SIgnal taste ich (eigendlich) mit einer 1kHz-Frequenz ab.

100 Hz reichen locker. Für den Ganzen Spaß reicht EIN Timer spielend.
1
//Multiplexing-Routine
2
ISR(TIMER1_COMPA_vect) {
3
  static uint8_t s = 0;
4
  PORTC = stelle[s];
5
  switch(s) {
6
    case 0: SPI_MasterTransmit(ziffer[anzeige.sekunde%10]); break;
7
    case 1: SPI_MasterTransmit(ziffer[anzeige.sekunde/10]); break;
8
    case 2: SPI_MasterTransmit(ziffer[anzeige.minute%10]); break;
9
    case 3: SPI_MasterTransmit(ziffer[anzeige.minute/10]); break;
10
    case 4: SPI_MasterTransmit(ziffer[anzeige.stunde%10]); break;
11
    case 5: SPI_MasterTransmit(ziffer[anzeige.stunde/10]);
12
  }
13
  s++;
14
  if(s==6) s=0;
15
}

Das ist ungünstig. Warum muss man immer wieder die "rechenintensiven" 
Divisonen und Modulooperationen machen? EINMAL beim Zeit weiterzählen 
reicht.

von Falk B. (falk)


Lesenswert?

Ach ja, das mit dem Nachladen per Software klappt sowieso NUR, wenn eine 
andere ISR den Interrupt für maximal PRESCALER Takte unterbrechen kann. 
Deine MUX-ISR ist aber eher länger als 64 Takte. Ein Grund mehr für CTC 
bzw. nur EINEN Timer. Ausserdem ist 8 MHz  64  256 nicht 1 kHz sondern 
eher 488,28125. Ein Grund mehr, warum das nicht genau ist.

von Flo (Gast)


Lesenswert?

Danke Chris und Falk Brunner,

Falk Brunner schrieb:
> Sind auch die AVR Fuses richtig gesetzt? Mit etwas Glück läuft dein
> AVR mit 8MHz internem RC-Takt und nicht mit dem Quarz.

Ja darauf habe ich extra geachtet, da ich sowas sonst immer vergesse :)


Falk Brunner schrieb:
> 100 Hz reichen locker. Für den Ganzen Spaß reicht EIN Timer spielend.

Soll ich also meine DCF77-Abfrage(bzw. das Weiterzählen) zusammen mit 
dem Multiplexing in einer Routine realisieren? Ich taste dann aber halt 
mit 400 Hz oder so einem ähnlichen Wert ab, damit das Multiplexing nicht 
zu langsam wird, oder ?


Falk Brunner schrieb:
> Das ist ungünstig. Warum muss man immer wieder die "rechenintensiven"
> Divisonen und Modulooperationen machen? EINMAL beim Zeit weiterzählen
> reicht.

Ich bin mir nicht sicher ob ich verstanden habe wo du dir die Rechnung 
vorstellst. Wenn ich doch nun das Weiterzählen und das Multiplexing in 
einer Routine unterbringe, dann muss ich diese Rechnungen doch 
zwangsläfuig jedes mal machen oder ?



Vielen Dank
Gruß Florian

von MarioT (Gast)


Lesenswert?

Flo schrieb:
> Aber irgendwie ist es noch recht ungenau.  Innerhalb von 20 Minuten
> läuft meine Uhr schon 10sec nach. Da ich ein Quarz benutze und da
> (8Mhz/(64*125)) ganzzahlig teilt müsste es doch einiger maßen genau
> sein, oder ?
> Ist einfach mein Quarz immer noch nicht genau oder liegt das an einer
> schlechten Programmierung ? Kann ich das noch genauer bekommen ?

http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC

von BattMan (Gast)


Lesenswert?

Für Anwendungen bei denen es auf eine gute Zeitgenauigkeit ankommt nehme 
ich
immer Quarze mit 3,686411 MHz. Die Frequenz lässt sich prima auf einen 
Interrupt im 1 Sekunden Takt mit dem TIMER1 einstellen.

So als mal als kleiner Tipp.

von Falk B. (falk)


Lesenswert?

@ Flo (Gast)

>> 100 Hz reichen locker. Für den Ganzen Spaß reicht EIN Timer spielend.

>Soll ich also meine DCF77-Abfrage(bzw. das Weiterzählen) zusammen mit
>dem Multiplexing in einer Routine realisieren?

Ja.

> Ich taste dann aber halt
>mit 400 Hz oder so einem ähnlichen Wert ab,

Nö, man kann PROBLEMLOS in der ISR einen kleinen Zähler in Software 
laufen lassen, der nur alle X Takte eine andere Funktion aufruft.
1
ISR(bla) {
2
3
  static uint8_t cnt;
4
5
  cnt++;
6
  if (cnt==4) {
7
     cnt =0;
8
     // Mach was langsames bei jedem 4. ISR-Aufruf
9
  }
10
11
  // Mach was schnelles bei jedem ISR -Aufruf
12
13
}

>> Das ist ungünstig. Warum muss man immer wieder die "rechenintensiven"
>> Divisonen und Modulooperationen machen? EINMAL beim Zeit weiterzählen
>> reicht.

>Ich bin mir nicht sicher ob ich verstanden habe wo du dir die Rechnung
>vorstellst. Wenn ich doch nun das Weiterzählen und das Multiplexing in
>einer Routine unterbringe, dann muss ich diese Rechnungen doch
>zwangsläfuig jedes mal machen oder ?

Nö, du hast es nicht verstanden. Du legst ein Array an, wo die 
Ausgabedaten für das Multiplexing schon fix und fertig drinstehen. Dann 
muss man nur noch AUSGEBEN, nicht mehr umrechnen.
1
//Multiplexing-Routine
2
ISR(TIMER1_COMPA_vect) {
3
  static uint8_t s = 0;
4
5
  PORTC = stelle[s];
6
  SPI_MasterTransmit(ziffer[out_data[s]]);
7
  s++;
8
  if(s==6) s=0;
9
}
10
//Routine für DCF77 und internes Zeitzählen
11
ISR(TIMER0_OVF_vect) {
12
  /*
13
      DCF77 zeug
14
      ....
15
      ....
16
  */
17
  
18
  anzeige.msekunde++;
19
  if(anzeige.msekunde == 1000) {
20
    anzeige.msekunde = 0;
21
    anzeige.sekunde++;
22
    if(anzeige.sekunde == 60) {
23
      anzeige.sekunde = 0;
24
      anzeige.minute++;
25
      if(anzeige.minute == 60) {
26
        anzeige.minute = 0;
27
        anzeige.stunde++;
28
        if(anzeige.stunde == 24) {
29
          anzeige.stunde = 0;
30
        }
31
      }
32
    }
33
    // EINMAL pro Sekunde die Daten neu berechnen
34
    // nicht jedes Mal beim Multiplexen
35
    out_data[0] = anzeige.sekunde / 10;
36
    out_data[1] = anzeige.sekunde % 10;
37
    out_data[2] = anzeige.minute / 10;
38
    out_data[3] = anzeige.minute % 10;
39
    out_data[4] = anzeige.stunde / 10;
40
    out_data[5] = anzeige.stunde % 10;
41
  }
42
}

PORTC = stelle[s];

Hier brauchst du ganz sicher nicht ganze 8 Bits des Ports. Mach also 
besser eine maßgeschneiderte Bitmanipulation.

von Falk B. (falk)


Lesenswert?

@ BattMan (Gast)

>immer Quarze mit 3,686411 MHz. Die Frequenz lässt sich prima auf einen
>Interrupt im 1 Sekunden Takt mit dem TIMER1 einstellen.

Jain. 1s ist meist zu lang für uC Anwendungen, man will eher was im 
Bereich von 100-1000 Hz haben.

3,6864 MHz und Vielfache davon sind Baudratenquarze, und wie es der 
Zufall will, ein Produkt auf Zweier und Zehnerpotzen 
(Primfaktorzerlegung).

3686400 Hz = 32 * 115200 = 2^14 * 3^2 * 5^2 = 2^12 * 3^2 * 100

Jetzt kann man sich aussuchen, welche ganzzahlige Frequnz man haben 
möchte, ob 200, 400, 300, 900 Hz. Das ist dann ganzzahlig auf 1 Hz 
teilbar. Einen Teil des Faktors 2^12 kann man mit dem Prescaler des 
Timers erledigen, den Rest macht der Timer selber.

2^12 = 4096 = 2^8 * 2^4, Sprich 8 Bit Zähler mit 256 Schritten + 
Prescaler 16. Hmm, gibt es beim AVR nicht, also entweder CTC Mode oder 
die doppelte Quarzfrequenz.

Zur Not geht auch Prescaler 1024 und manuelles Nachladen des Timers in 
der ISR wie zu seeligen 8051 Zeiten. Wenn kein anderer Interrupt läuft 
geht das sicher.

Oder man nimmt einfach Timer1 im CTC Mode, damit kann man nahezu jede 
beliebige Quarzfrequenz auf eine ganzzahlige dekadische Frequenz teilen.

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.