Die Aufgabe der PIC-Software besteht bei der DCF-Dekodierung aus mehreren Teilaufgaben:
Der PIC-I/O-Eingang, an dem das DCF-Empfangsmodul (mit einer Anpass-Schaltung) angeschlossen ist, wird zyklisch (ca. alle 4 ms) vom Unterprogramm DCFROUTINE abgefragt. Als Zeitbasis für den 4-ms-Takt dient der Timer-0-Interrupt. Dieser kann je nach Anwendung entweder so eingestellt werden, dass er alle 4 ms einen Interrupt auslöst, oder er wird so eingestellt dass er z. B. alle 500µs oder alle 1ms einen Interrupt erzeugt und ein Zählregister zählt die notwendige Anzahl an Interruptaufrufen bis zu einer Zeitbasis von 4 ms. Hier in dieser Dokumentation wird der Einfachheit halber die erstere Variante verwendet. Der Aufruf des Unterprogramms DCFROUTINE erfolgt hier aber nicht von der ISR (Interrupt-Service-Routine), sondern vom Hauptprogramm. Die ISR setzt nur alle 4 ms ein Flag zur Kennzeichnung, dass das Hauptprogramm das Unterprogramm DCFROUTINE aufrufen soll.
Die Aufgabe des Unterprogramms DCFROUTINE besteht darin, aus dem vom DCF-
Empfangsmodul empfangenen Datenstrom die Informationen LOW, HIGH und den
Minutenwechsel (also das Ausbleiben der 59. Sekunde) gemäß dem im Abschnitt 1
(Grundlegendes zu DCF) gewonnen Erkenntnisse zu dekodieren. Für diese
Informationen befinden sich im DCF-Statusregister (DCFSTATUS) entsprechende
Flags. (siehe Abschnitt 3.2 Benötigte
Register, Konstanten und Portdefinition).
Weiters beinhaltet das Register DCFSTATUS noch den Zustand des DCF-Eingangs 4
ms vor dem Aufruf des Unterprogramms DCFROUTINE. Mit Hilfe des aktuellen
Zustands und des Zustands vor 4 ms kann nun erkannt werden, ob sich der Pegel am
DCF-Eingang geändert hat.
Das Hauptprogramm prüft nun ständig die Flags DCFNEUESEK (wird zusätzlich zu
DCFLOW bzw. DCFHIGH gesetzt, wenn eine gültige Sekunde empfangen wurde) und
DCFNEUEMIN, und ruft die zugehörigen Unterprogramme DCFUPSEKUNDE bzw.
DCFUPMINUTE auf. Die Aufgabe des Unterprogramms DCFUPSEKUNDE ist es, die
empfangenen Low- und High-Pegel in den Register DCFTELEGRAMM1 bis
DCFTELEGRAMM8 zwischenzuspeichern.
Die Aufgabe des Unterprogramms DCFUPMINUTE ist dagegen etwas umfangreicher:
Zunächst die Zeit und das Datum aus den in den Registern DCFTELEGRAMM1 bis
DCFTELEGRAMM8 zwischengespeicherten Werten entsprechend der Tabelle im Abschnitt
1. Grundlegendes zu DCF zusammensetzen. Anschließend die
soeben gewonnen Zeit- und Datumsinformationen mit den Zeit- und Datumsinformationen
der vorhergehenden Minute vergleichen. Die Zeit- und Datumswerte der soeben empfangenen
Minute sind nur dann gültig, wenn sich die Minute um maximal 1 von der vorhergehenden Minute
unterscheidet, während sich die restlichen Zeit- und Datumsinformationen mit denen der
vorhergehenden Minute nicht unterscheiden. Dies ist nur eine einfache Überprüfung, die
aber manche richtigen Telegramme als falsch auswertet. (Siehe Abschnit
3.4.3 Unterprogramm "DCFUPMINUTE")
Parallel zur Dekodierung des DCF-Eingangs erzeugt das Unterprogramm INNEREUHR eine reine Softwareuhr. Für diesen Zweck ist eine genaue 1-Sekunden-Zeitbasis notwendig. Diese Zeitbasis wird von der schon erwähnten Timer-0-ISR (Interrupt Service Routine) zusätzlich zur 4ms-Zeitbasis erzeugt. Auch für diese Zeitbasis wird in der ISR ein entsprechendes Flag gesetzt, und das Hauptprogramm ruft das Unterprogramm INNEREUHR auf, wenn dieses Flag gesetzt ist. Diese zusätzliche Softwareuhr mag auf dem ersten Blick unnötig erscheinen. Es hat sich aber gezeigt, dass ein DCF-Empfang oft auch über eine längere Zeit nicht möglich ist. In diesem Fall würde die Uhr „stehen“. Diese Zeit wird mit einer zusätzlichen Softwareuhr so überbrückt, dass der Betrachter der Uhr davon nichts bemerkt.
Register:
Für die DCF-Dekodierung sind neben einigen internen Register (SFR, Spezielle
Funktions-Register) noch eine ganze Menge eigener Register notwendig:
Bit Flagname Funktion
0 DCFPORTNEU Akt. Zustand DCF-Porteingang
1 DCFPORTALT Vorhergehender Zustand am DCF-Porteingang
2 DCFNEUESEK gesetzt, wenn neue Sekunde begonnen
3 DCFNEUEMIN gesetzt, wenn neue Minute begonnen
4 DCFLOW gesetzt, wenn Low-Signal erkannt
5 DCFHIGH gesetzt, wenn High-Signal erkannt
6 DCFFEHLER gesetzt, wenn ein Fehler erkannt
7 DCFSYNC gesetzt, wenn Uhr mit DCF synchronisiert
Wert Bedeutung
1 Montag
2 Dienstag
3 Mittwoch
4 Donnerstag
5 Freitag
6 Samstag
7 Sonntag
Bit Flagname Funktion
0 DCFRESANT Ist dieses Bit gesetzt so wurde die Reserveantenne zum
Versenden des DCF-Telegramms verwendet
1 DCFSOMWIN Ist dieses Bit gesetzt, so kündigt es eine Umstellung
zwischen Sommer- und Winterzeit an. Dieses Bit wird eine
Stunde vor der Zeitumstellung gesetzt. Ab der Zeitumstellung
enthält es wieder den Wert 0
2 DCFSOMMER Während der Sommerzeit ist dieses Bit gesetzt
3 DCFWINTER Während der Winterzeit ist dieses Bit gesetzt
4 DCFSCHALTSEK Ist dieses Bit gesetzt, so kündigt es eine Schaltsekunde an
Konstanten:
Die Konstante KONSTISR1SEK gibt die Anzahl der notwendigen ISR-Aufrufe für die
1-Sekunden Zeitbasis an.
Hier wird die ISR alle 4ms aufgerufen, daher ergibt sich für diese Konstanten der
Wert 250 (250 x 4ms = 1000ms = 1 Sekunde)
Bei einem LOW dauert die Absenkung des Trägers zwischen 80ms und 120ms, für ein
High dauert die Absenkung des Trägers zwischen 160ms und 240ms (siehe auch Abschnitt
1. Grundlegendes zu DCF). Die Konstanten KONSTDCFLMIN,
KONSTDCFLMAX, KONSTDCFHMIN und KONSTDCFHMAX dienen zur Ermittlung
eines LOW oder eines HIGH.
Das Unterprogramm DCFROUTINE wird alle 4ms aufgerufen, daher ergeben sich für die
Konstanten folgende Werte:
Es kann erforderlich sein, dass bei einer Anwendung die PIC-Taktfrequenz erhöht werden muss. In diesem Fall kann es notwendig sein, diese Konstanten anzupassen.
Portdefinition:
Im Allgemeinen wird bei jeder Anwendung der Eingangspin für die DCF-Dekodierung an
einem anderen Portpin verwendet. Damit dies in der Software nur an einer Stelle
berücksichtigt werden muss befindet sich in der Software eine Portdefinition für den
DCF-Eingang. Diese besteht aus den folgenden 3 Parametern:
Achtung: Wird für DCFINPORT der Port A verwendet, so muss für DCFINTRIS das zum Port A zugehörige TRIS-Register definiert werden. Eine mögliche Portdefinition ist:
DCFINPORT equ PORTB
DCFINTRIS equ TRISB
DCFIN equ 0
Dieses Unterprogramm dient zur Initialisierung des Mikrocontrollers. Der Portpin an
dem der DCF-Empfänger angeschlossen ist muss als Eingang definiert werden.
In der Initialisierungsroutine muss für den ersten Aufruf des Unterprogramms
DCFROUTINE der aktuelle Zustand dieses Portpins gelesen, und im Register
DCFSTATUS kopiert werden.
Da das Unterprogramm DCFROUTINE zyklisch (alle 4ms) aufgerufen wird, ist eine entsprechende Zeitbasis notwendig. Diese wird mit Hilfe eines Timer-Interrupt erzeugt. Für die Definition der Zeitbasis ist hier das Mikrocontrollerinterne Funktions-Register OPTREG (in der Registerbank 1) zuständig. Damit bei einer PIC-Taktfrequenz von 4,096MHz eine Zeitbasis von 4ms erzeugt wird, muss das Register OPTREG mit dem binären Wert b‘00000011‘ geladen werden. Das Zählregister für dies Zeitbasis (Funktions-Register TMR0, in Registerseite 0) muss gelöscht werden.
Weiters müssen einige Register vorbelegt (gelöscht) werden.
Zur DCF-Dekodierung sind 3 Unterprogramme notwendig:
Weiters das Unterprogramm INNEREUHR. Dieses Unterprogramm sorgt dafür, dass, wenn kein gültiges DCF-Telegramm emfangen werden kann, die Uhrzeit trotzdem jede Sekunde aktualisiert wird. Ist für eine längere Zeit kein korrekter DCF-Empfang möglich, so würden die Minuten, Stunden, Tage usw. stehen bleiben, da im Unterprogramm DCFUPMINUTE nur gültige DCF-Telegramme übernommen werden.
Dazu kommt noch ein "allgemeines" Unterprogramm zur Umwandlung einer 2stelligen BCD-Zahl in eine Binärzahl. Dieses Unterprogramm wird hier allerdings nicht näher beschrieben.
Das Unterprogramm DCFROUTINE ist für die DCF-Dekodierung die wichtigste Komponente.
Diese Routine wird ca. alle 4 ms aufgerufen
Aufgabe und Vorgehensweise:
Die Aufgabe des Unterprogramms DCFROUTINE besteht darin aus den Abtastungen
herauszufinden, ob ein Low, ein High oder ein Minutenwechsel gesendet wurde. Dazu
wird bei jedem Aufruf des Unterprogramms DCFROUTINE entweder das Zählregister
DCFPULS oder das Zählregister DCFPAUSE um 1 erhöht (inkrementiert), wenn sich
der Pegel vom DCF-Eingang zum vorhergehenden Aufruf dieses Unterprogramms nicht
verändert hat. Ist der DCF-Eingang Low (also logisch 0), so wird das Zählregister
DCFPULS um 1 erhöht, ist der DCF-Eingang High (also logisch 1), so wird das
Zählregister DCFPAUSE um 1 erhöht. Beide Zählregister können maximal den Wert
255 aufnehmen, was einer Zeit von 1020 ms (oder 1,02 Sekunden) entspricht (=255 *
4ms).
Unterscheidet sich der aktuelle Pegel des DCF-Eingangs mit dem Pegel vom
vorhergehenden Aufruf, so spricht man von einer Flanke, wobei hier zwischen einer
fallenden und einer steigenden Flanke unterschieden werden muss. Bei einer fallenden
Flanke (der aktuelle Pegel des DCF-Eingangs ist logisch 0, während der Pegel beim
vorhergehenden Aufruf noch logisch 1 war), erfolgt zunächst eine Auswertung des
Zählregisters DCFPULS. Beinhaltet dieses Zählregister einen Wert zwischen den
Konstanten KONSTDCFLMIN und KONSTDCFLMAX, so wurde ein LOW des DCF-
Telegramms ermittelt. Im DCF-Statusregister (DCFSTATUS) werden daher die Flags
DCFLOW und DCFNEUESEK gesetzt. Das Flag DCFNEUESEK dient als Zeichen einer
neu empfangenen und gültigen Sekunde. Anschließend wird das Zählregister
DCFPULS gelöscht. Beinhaltet das Zählregister DCFPULS einen Wert zwischen den
Konstanten KONSTDCFHMIN und KONSTDCFHMAX, so wurde ein HIGH des DCF-
Telegramms ermittelt. Im DCF-Statusregister (DCFSTATUS) werden daher die Flags
DCFHIGH und DCFNEUESEK gesetzt. Das Flag DCFNEUESEK dient auch hier als
Zeichen einer neu empfangenen und gültigen Sekunde, und auch das Zählregister
DCFPULS wird anschließend gelöscht. Beinhaltet das Zählregister DCFPULS einen
Wert der weder zwischen den Konstanten KONSTDCFLMIN und KONSTDCFLMAX
noch zwischen den Konstanten KONSTDCFHMIN und KONSTDCFHMAX liegt, so
handelt es sich um einen Übertragungsfehler. In diesem Fall wird das Fehlerflag
(DCFFEHLER) im DCF-Statusregister (DCFSTATUS) gesetzt und auch das
Zählregister DCFPULS wird anschließend gelöscht. Das Flag DCFNEUESEK wird hier
aber nicht gesetzt, da es sich in diesem Fall um keine gültige Sekunde handelt. Das
Fehlerflag wird erst bei der Erkennung einer neuen Minute wieder gelöscht. Achtung:
Solange das Fehlerflag gesetzt ist erfolgt keine Auswertung des Zählregisters
DCFPULS. Dieses Zählregister wird aber dennoch bei jeder fallenden Flanke
zurückgesetzt.
Bei einer steigenden Flanke (der aktuelle Pegel des DCF-Eingangs ist logisch 1,
während der Pegel beim vorhergehenden Aufruf noch logisch 1 war) wird das
Zählregister DCFPAUSE gelöscht.
Entsprechend dem im Abschnitt 1. Grundlegendes zu DCF
beschriebenen Protokoll erfolgt in der 59. Sekunde
keine Absenkung des Trägers. Da es also in der 59. Sekunde keine fallende Flanke
gibt, gibt es auch keine steigende Flanke. Das Zählregister DCFPAUSE „läuft über“, da
es nur einen Zählbereich von 0 bis 255 besitzt. Dieser Überlauf wird hier ausgenützt.
Zur Bestimmung einer neuen Minute bzw. zur Bestimmung des Telegrammbeginns. Ist
das Fehlerflag gesetzt, so wird es nun gelöscht. War es nicht gesetzt, so wird das Flag
DCFNEUEMIN im DCF-Statusregister (DCFSTATUS) gesetzt.
Das im Abschnitt 5. Download downloadbare Flussdiagramm soll das soeben beschriebene und umfangreiche Unterprogramm verdeutlichen. In den grau hinterlegten Bereichen erfolgt das Setzen oder Rücksetzen der zur weiteren Verwendung notwendigen Übergabeflags im DCF-Statusregister (DCFSTATUS). Die Unterprogramme DCFUPSEKUNDE und DCFUPMINUTE greifen auf diese Flags zu.
Anmerkung:
Die temporären Register TEMP1 und TEMP2 werden hier nur als Hilfsregister benötigt.
Sie können daher auch in anderen Unterprogrammen verwendet werden.
Aufgaben:
Vorgehensweise:
Ist das Flag DCFLOW (im Register DCFSTATUS) gesetzt das Carryflag (im SFR STAT)
löschen, ist aber das Flag DCFHIGH (ebenfalls im Register DCFSTATUS) gesetzt das
Carryflag setzten. Dieses Carryflag nun dem Telegramm hinzufügen. Dieser Vorgang
erfolgt mit einem so genannten Schiebebefehl. Dabei werden alle Bits des Registers
DCFTELEGRAMM1 auf die nächst höhere Position verschoben. (Bit 6 wandert ins Bit 7,
Bit 5 wandert ins Bit 6 usw. Bit 0 wandert ins Bit 1). Der Inhalt vom Carryflag wandert
ins Bit 0. Der Inhalt von Bit 7 wird ins Carryflag geschoben. Bei den Registern
DCFTELEGRAMM2 bis DCFTELEGRAMM6 erfolgt derselbe Vorgang. (Der Inhalt von
Bit 7 des Registers DCFTELEGRAMM1 wird ins Carry geschoben, und das Carry aber
weiter in das Bit 0 des Registers DCFTELEGRAMM2)
Anmerkung:
Die Flags DCFLOW und DCFHIGH können nie gleichzeitig gesetzt sein. Es ist nur
möglich, dass entweder nur DCFLOW oder nur DCFHIGH gesetzt ist, aber nie beide
gleichzeitig.
Aufgaben:
Anmerkung:
Das temporäre Register TEMP1 wird hier nur als Hilfsregister benötigt, und kann daher
auch in anderen Unterprogrammen verwendet werden.
Aufgabe:
ür den Fall dass kein gültiges DCF-Telegramm empfangen werden kann sorgt dieses
Unterprogramm dafür, dass die Uhrzeit trotzdem jede Sekunde aktualisiert wird. Ist für
eine längere Zeit kein korrekter DCF-Empfang möglich, so würden die Minuten,
Stunden, Tage usw. stehen bleiben, da ja im Unterprogramm DCFUPMINUTE nur
gültige DCF-Telegramme übernommen werden.
Dieses Unterprogramm wird also parallel zu den Unterprogrammen für die
DCF-Dekodierung aufgerufen.
Vorgehensweise:
Die Sekunden (Register UHRSEKUNDE) um 1 erhöhen. Beinhalte dieses Register nun
den Wert 60, so beginnt eine neue Minute. Daher die Sekunden löschen und die
Minuten (Register UHRMINUTE) um 1 erhöhen. Beinhaltet dieses Register nun den
Wert 60, so beginnt eine neue Stunde. Daher die Minuten löschen und die Stunden
(Register UHRSTUNDE) um 1 erhöhen. Beinhaltet dieses Register nun den Wert 24, so
beginnt ein neuer Tag. Daher die Stunden löschen. Ist das Mitzählen des Datums
notwendig, so muss nun ein Register für den Tag (z.B. DATUMTAG) um 1 erhöht
werden und dieses Überprüft werden, wobei diese Prüfung nun nicht mehr so einfach
ist, da ja jeder Monat unterschiedlich viele Tage besitzt. Erschwerend kommt auch noch
hinzu, dass auch die Schaltjahre miteinbezogen werden müssen!
Achtung:
Dieses Unterprogramm muss daher jede Sekunde aufgerufen werden.