In Europa ist der Zeitzeichensender DCF77 gut zu empfangen. Daher bietet er sich an, damit eine Uhr zu synchronisieren. Der Zeitzeichenkode ist auf der DCF77 Webseite ausführlich dokumentiert, so daß ich hier nicht mehr darauf eingehe. Bei Conrad gab es auch lange Zeit einen entsprechenden Empfangsmodul zu kaufen, der auch zuverlässig funktioniert. Er besteht aus der Ferritantenne mit Empfangschip und einigen Transistoren zur Pegelanpassung, so daß eine direkte Speisung mit 5V, sowie 5V-Ausgänge möglich sind.
Als Hardware ist der ATTINY12 eingesetzt worden und auch vollkommen ausreichend. Vorteilhaft ist, daß nicht mal ein Quarz benötigt wird, da der ATTINY12 einen internen RC-Oszilator besitzt.
Auch sollte man die Brown-Reset-Fuse setzen, damit der IC auch wirklich bei jedem Einschalten sicher arbeitet. Das ist nämlich bei solchen AVRs ohne Brown-Out bzw. deaktivierten Brown-Out leider nicht der Fall. Es hat aber lange gedauert, ehe Atmel das bei den neueren AVRs geschnallt hat, obwohl ich das schon 1996 bei der Vorstellung des AT90S1200 moniert hatte.
Außerdem wird noch ein Schieberegister 74HC164 zur parallelen Ansteuerung des LCD benötigt. Das LCD ist mit einem Standard HD44780 Controller und hat 2 * 16 Zeichen Darstellung.
Ein Schaltplan ist nicht nötig, da die Anschlüsse aus dem "Clock.h"-File hervorgehen, bzw. der Anschluß des 74HC164 ist im "LCD12s.inc" beschreiben. Und wo die 5V an den ATTINY12 rankommen, geht ja aus dem Datenblatt hervor.
Das Programm ist in einzelne Module aufgeteilt. Bezüglich der geringen Stacktiefe des ATTINY12 war es notwendig, bestimmte Funktionen per RJMP aufzurufen, da nur eine Unterprogrammtiefe von 2 erlaubt ist. Diese Funktionen sind mit "Name_inline" gekennzeichnet und springen zu "Name_ret" zurück.
In diesem File sind alle Definitionen zusammengefaßt, wie z.B. die Registeraufteilung und die Anschlußbelegung des ATTINY12
Das ist das Hauptprogramm, in das alle Module per "include" zusammengefaßt werden. Außerdem enthält es die Initialisierungsroutine und die Hauptschleife.
Die Hauptschleife ruft ständig die Routine zur DCF77-Dekodierung auf. Es lassen sich aber noch weitere Routinen einfügen. Es muß bloß sichergestellt werden, daß die DCF77-Dekodierung mindestens einmal je Sekunde ausgeführt wird, da die einzelnen Bits mit 1 Baud empfangen werden.
Des weiteren wird die interne Uhr jede Sekunde weitergeschaltet und angezeigt. Eine interne Uhr ist in jedem Fall notwendig, um Zeiten zu überbrücken, wenn der Empfang mal gestört sein sollte. Außerdem wird dadurch die 59. Sekunde wiederhergestellt, die ja zur Synchronisation im DCF77-Signal unterdrückt wird.
In der Initialisierung wird auch das Kalibrationsbyte gelesen.
Achtung !
Dieses Byte steht nur bei einem nagelneuen ATTINY12 auf dem letzten Wort und wird bei einem Löschen mit gelöscht. D.h. es ist zuerst auszulesen und dann in der "DB"-Anweisung am Ende einzutragen. Wenn man den ATTINY12 ohne vorheriges Löschen programmiert, kann die "DB" Anweisung einfach weggelassen werden.
Diese Routine bietet nichts besonderes. Es wird auf der ersten Zeile die Uhrzeit und auf der 2. Zeile das Datum ausgegeben. Die Umwandlung in 2 Dezimalziffern erfolgt mit der schnellen Subtraktionsmethode.
Zusätzlich ist am Ende der zweiten Zeile eine Statusanzeige vorgesehen: Der Buchstabe "E" zeigt an, ob ein Error erkannt wurde und daher das Paket verworfen wird. Der Errorstatus wird erst mit dem nächsten Synchronimpuls zurückgesetzt. Das Zeichen "*" wechselt mit jedem empfangenen Bit, d.h. man kann sehen, ob überhaupt was empfangen wird.
Der 2. Teil diese Moduls enthält eine komplette Uhr mit Datumsweiterschaltung. D.h. auch wenn zu Silvester der Empfang mal gestört sein sollte, wird der Jahreswechsel nicht versäumt. Jeder Aufruf bewirkt eine Weiterschaltung um eine Sekunde.
Dieses Modul enthält die nötigen Funktionen zum seriellen Ansteuern eines Standard LCDs. Die Verschaltung mit dem LCD und dem 74HC164 ist auch angegeben, so daß kein extra Schaltplan benötigt wird. Es wurde da auch wieder auf geringe Stacktiefe und Registernutzung optimiert. Funktionell entspricht sie aber der schon früher in diesem Forum erklärten C51-Routine.
Hier sind nur 2 Macros, um etwas Schreibarbeit einzusparen.
Dieses ist der zentrale Timer Interrupt Handler. Er dient sowohl der Sekundentakterzeugung für die interne Uhr, als auch der Pulslängenmessung zur Dekodierung des DCF77-Signals. Bei 1.2MHz CPU-Takt und T0 als 8-Bit-Timer wird ein Vorteiler von 64 verwendet. Damit ergeben sich etwa 1.2E6 / 256 / 64 = 73 Aufrufe je Sekunde. Damit läßt sich noch bequem die Synchronimpulszeit von 2 Sekunden in einem Byte zählen.
Diese 73 Hz sind trotz Kalibrationsbyte nur ein ungefährer Wert. Da jedoch das DCF77-Signal hochgenau ist, werden einfach die Impulse je Minute gezählt und dann durch 60 geteilt als Reloadwert für den Sekundentakt verwendet. Das ist aber noch zu ungenau, deshalb wird abhängig vom Rest der Division der Reloadwert verlägert, so daß sich immer exakt die Interruptzahl je Minute ergibt, die vorher mit dem DCF77-Signal ermittelt wurde. Dadurch ist auch bei längerem Ausfall des DCF77-Signals eine relativ genaue Zeitableseung möglich. Ich habe etwa 1 Sekunde Abweichung je Stunde ermittelt.
Die Pulslängen werden auch sofort ausgewertet und entsprechende Flags gesetzt, wenn eine Synchronpause oder ein 0- bzw- 1-Bit erkannt wurde. Zu starke Abweichungen von den Sollwerten werden als Fehler markiert.
Diese Routine macht die für die oben beschriebene Angleichung an das DCF77-Signal nötige Division / 60 mit Rest.
Das ist die eigentliche Auswertung des DCF77-Kodes. Ich habe schon viele Auswerteroutinen gesehen, aber keine ist so kurz, schnell und übersichtlich. Die meisten sind so lang, daß sie gerade mit Mühe in einen AT90S8515 reinpassen. Es handelt sich dabei um riesige Programmketten, die für jedes Bit einen extra Behandlungsroutine aufrufen.
Bei näherer Betrachtung der Kodierung fällt aber einem sofort auf, daß die Behandlung der Bits sich doch stark ähnelt: Es wird immer ein entsprechender Bitwert zu einer Speicherzelle addiert, das ist alles.
Statt also 38 verschiedenen Routinen aufzurufen, habe ich nur eine Routine, die sich aus einer Tabelle mit 38 Einträgen die Adresse holt und den Wert, der zu dieser Adresse zu addieren ist. Deshalb ist meine Routine so kurz und effiizient. Verwunderlich, daß darauf noch kein anderer gekommen ist.
Zusätzlich kennzeichnet der Tabellenwert 0x00 die 3 Stellen, an denen das Paritätsbit überprüft werden muß
Da ja die interne Uhr und der DCF77-Sender völlig asynchron voneinander laufen, müssen sie miteinander synchronisiert werden. Dabei ist es sowohl möglich, daß der interne Timer den Sekundentakt kurz vor aber auch kurz nach dem DCF77-Synchronbit erzeugt. Erfolgt er vorher, ist daß nicht so schlimm, dann wird eben die Uhr mit der DCF77-Zeit gesetzt, die es eben gerade auch selber errechnet hat.
Kommt der Impuls aber kurz danach, würde die interne Uhr sofort weiterzäheln und eine Sekunde vorgehen. Deshalb wird in der DCF77-Routine das T-Bit gesetzt, sobald eine gültige Zeit übernommen wurde. Und dieses T-Bit bewirkt dann in der Main-Routine, das diese neue Zeit auch sofort angezeigt wird, als auch daß ein eventuell gesetztes Sekundenbit rückgesetzt wird und so nicht zum Zuge kommt. Zum Zuge muß es aber kommen und kommt es auch in jedem Fall, wenn ein fehlerhaftes DCF77-Signal empfangen und es daher ignoriert wurde.
Ähnliche Probleme sind auch zu beachten, wenn noch ein externer RTC-Chip Verwendung findet, was aber nur im ferneren Ausland Sinn macht, d.h. wenn das DCF77-Signal komplett ausfällt.
Da sowohl Register als auch Flash übrig ist, lassen sich noch einige Erweiterungen denken, wie. Ausgabe der Zeit über RS-232, Anschluß von DS18B20 Temperatursensoren oder Weckzeitprogrammierung. Die Weckzeit sollte man vorzugsweise im EEPROM speichern. Es macht ja sonst keinen Sinn, daß nach einem Netzausfall die Uhrzeit durch das DCF77-Signal automatisch wieder hergestellt wird.
Man kann auch alle diese Erweiterungen einbauen, aber dann sollte man auf einen ATTINY26 umsteigen, da sonst die Anschlüsse nicht mehr ausreichen.