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
> 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
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
nutz den CTC-Modus ist wie nen autorelaod in die ISR OCA/B packts du das was gerade bei der OVFL-ISR drinen steht.
@ 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.
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.
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
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
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.
@ 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.
@ 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.