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
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
@ 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.
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
staticuint8_tcnt;
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
staticuint8_ts=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.