DCF (Dekodierung mit PIC-Mikrocontroller, Elektronik)


zurück zu Elektronik, Homepage


3. Software:

Die Aufgabe der PIC-Software besteht bei der DCF-Dekodierung aus mehreren Teilaufgaben:

nach oben

3.1. Das Dekodierverfahren

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.

nach oben

3.2. Benötigte Register, Konstanten und Portdefinition

Register:
Für die DCF-Dekodierung sind neben einigen internen Register (SFR, Spezielle Funktions-Register) noch eine ganze Menge eigener Register notwendig:

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
    
nach oben

3.3. Initialisierung (Unterprogramm "INIT")

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.

nach oben

3.4. Unterprogramme für die DCF-Dekodierung

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.

nach oben

3.4.1. Unterprogramm "DCFROUTINE"

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.

nach oben

3.4.2. Unterprogramm "DCFUPSEKUNDE"

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.

nach oben

3.4.3. Unterprogramm "DCFUPMINUTE"

Aufgaben:

Anmerkung:
Das temporäre Register TEMP1 wird hier nur als Hilfsregister benötigt, und kann daher auch in anderen Unterprogrammen verwendet werden.

nach oben

3.4.4. Unterprogramm "INNEREUHR"

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.

nach oben


zurück zu Elektronik, Homepage

Autor: Buchgeher Stefan
Erstellt: 9. Dezember 2003
Letzte Änderung: 10. Oktober 2004