Elektronik-Projekt: DCF-Uhr mit CPLD


zurück zu Elektronik, Homepage

DCF-Uhr im Betrieb

4. Softwareentwurf mit CPLDs am Beispiel DCF-Uhr

4.1. Grundlagen

Komplexe digitale Schaltungen sind stets eine Zusammensetzung aus kombinatorischer Logik (in Form von Addierer, Adressdekodierer, Multiplexer und Demultiplexer, usw.) und sequentieller Logik (in Form von Zählern, Schieberegister, diskreten FlipFlops und Zustandsmaschinen). Es ist daher unbedingt notwendig, dass man diese digitalen Grundschaltungen beherrscht.

nach oben

4.2. Einteilung der Aufgabe in Blöcke

Bei der Entwicklung von Schaltungen mit CPLDs gibt es eine Vielzahl von Ansätzen und Vorgehensweisen. Eine davon ist die so genannte "Top-Down-Methode". Bei dieser Methode wird der gesamten Prozess in Blöcke unterteilen. Diesen Blöcken ordnet man bestimmte Aufgaben zu, wobei man sich vorerst noch nicht um deren Innenleben zu kümmern braucht. Auf dieser Ebene denkt man über den gesamten Prozess nach und versucht dabei das Zusammenspiel dieser Blöcke zu optimieren. Erscheint einem diese Aufteilung optimal, so Unterteilt man die einzelnen Blöcke wieder in Unterblöcke. Dabei geht man genau so vor, wie in der ersten Ebene. Nun kann man sich auf die Aufteilung dieser bereits kleineren Blöcke konzentrieren und muss sich nicht mehr so intensiv mit dem gesamten Prozess kümmern. Diese Vorgehensweise wiederholt man so lange, bis man schließlich bei Blöcken mit einer überschaubaren Größe angekommen ist. Diese Blöcke können dann meist als einfache Zustandsmaschinen oder als kombinatorische Logik realisiert werden.

nach oben

4.3. Einteilung in Blöcke am Beispiel der DCF-Uhr

Bevor man die gestellte Software-Aufgabe in Blöcke einteilen kann, muss man sich zuerst im Klaren sein, was denn die Software alles erledigen muss, und wie man diese am besten und sinnvollsten realisiert. (Dabei spielt sicher auch die Erfahrung eine wichtige Rolle!)

Hier, bei der DCF-Uhr hat die Software (im Zusammenspiel mit der Hardware) folgende Aufgaben:

Bild 4.1. zeigt die Blockeinteilung für die DCF-Uhr. Bei dieser ersten Aufteilung spricht man auch vom "Top-Design" oder "Top-Level-Design".

Top-Level-Design

Hier, bei der DCF-Uhr erfolgt das Top-Level-Design aus zwei Blöcken.

Anmerkung: Die hier getroffene Blockeinteilung ist sicher nur eine Möglichkeit von mehreren. Hier liegt die Freiheit des Entwicklers, die geforderte Aufgabe in sinnvolle Teilaufgaben zu zerlegen, was mitunter auch ein wenig Erfahrung benötigt.

Block "DCF-Dekodierung"

Dieser Block hat die Haupt-Aufgabe aus dem seriellen DCF-Datenstrom das Telegramm (Low- und High-Bits) zu ermitteln und diese zwischenzuspeichern.

Die Bilder 4.2. und 4.3. zeigen die Realisierung dieses Blocks. Dieser Block besteht aus einer Zustandsmaschine (iZM_DCF), einem Zähler (iZAEHLER_DCF) zur Ermittlung von Zeiten, einem Schiebregister (iSR_DCF) zur Speicherung des Telegramms, und einigen D-FlipFlops zur Speicherung diverser Zustände und zur Synchronisierung des DCF-Eingangs mit dem Systemtakt (iFF_DCF_In).

Block DCF Dekodierung Zustandsdiagramm

Die Hauptaufgabe der Zustandsmaschine (iZM_DCF) ist es aus dem seriellen Datenstrom am Eingang die LOWs und HIGHs zu ermitteln. Gemäß Abschnitt 2 erfolgt zu jeder Sekunde ein Impuls. Ein Low-Impuls dauert zwischen 80ms und 120ms und ein High-Impuls zwischen 160ms und 240ms. Bei einem Minutenwechsel bleibt dieser Impuls aus. Das obere Bild (Zustandsdiagramm, links oben) zeigt noch einmal diesen Zusammenhang. Für die Auswertung ist aber nicht diese Impulszeit interessant, sondern vielmehr die Zeit zwischen zwei Impulsen (siehe oberes Bild, links oben). Denn mit dieser Zeit kann eindeutig zwischen einem Low, einem High und einem Minutenwechsel unterschieden werden. Die Grundidee ist nun die, dass, diese Zeit mit Hilfe eines Zählers (iZAEHLER_DCF) gezählt wird, wobei der Zähler bei einer steigenden Flanke am Eingang freigegeben (also gestartet wird) und bei einer fallenden Flanke am Eingang wird der Zählwert eingelesen und ausgewertet. Im Zustandsdiagramm sind dies die Zustände Z0, Z1 und Z2. Erfolgt aber nach einer gewissen Zeit keine fallende Flanke, so erfolgt ein Time-Out, also ein Fehler. Gemäß dem Zustandsdiagramm erfolgt dann nach dem Zustand Z1 der Zustand Z7, wo die beiden Fehler-FlipFlops (iFF_DCF_BitFehler und iFF_DCF_Fehler) gesetzt werden. Anschließend gelangt die Zustandsmaschine in den Ausgangszustand (Z0) zurück und wartet auf die nächste steigende Flanke.
Erfolgt aber eine fallende Flanke am Eingang innerhalb der Time-Out-Zeit, so gelangt die Zustandmaschine nun zum Zustand Z2. hier erfolgt die Auswertung des Zählerstands des Zählers iZAEHLER_DCF.

Schieberegister iSR_DCF

Noch eine Anmerkung zum Zähler iZAEHLER_DCF:
Der Systemtakt beträgt, wie schon im Abschnitt 3 erläutert, f = 32,768 kHz. Da der Zähler iZAEHLER_DCF mit diesem Takt getaktet wird ergibt sich eine Zeit T von

Formel 1

Für eine Zeit von Beispielsweise t = 880ms (wie oben erwähnt) ergibt sich somit ein Zählerstand von

Formel 2

Also ein Zählwert von 28835.

Achtung: Der Nachteil bei dieser Auswertemethode ist, dass die Uhr um 1 Sekunde nachgeht, da ja die Auswertung am "Ende der Sekunde erfolgt", und da beginnt ja schon die "neue Sekunde". Im Allgemeinen spielt dass aber nur eine sehr untergeordnete Rolle.

Block "Freilaufende Uhr"

Dieser Block hat die Haupt-Aufgabe eine (freilaufende) Uhr zu erzeugen, welche auch geladen werden kann. Die einfachste Realisierung einer Uhr besteht darin Zähler hintereinander zu schalten, wobei der Ausgang des ersten Zählers der Takt für den zweiten Zähler ist, der Ausgang des zweiten Zählers der Takt für den nächsten Zähler usw. Diese asynchrone Methode wird als "Ripple-Counter" bezeichnet. Ihr größter Vorteil ist ihre Einfachheit. Ihre Nachteile sollten aber auch nicht unerwähnt bleiben. Diese asynchrone Methode eignet sich nur für niedrige Frequenzen und es können beim Übergang von einem Zählwert zum nächsten Zählwert kurzzeitig falsche Zählwerte auftreten. Beide Nachteile sind bei dieser Anwendung als DCF-Uhr aber von untergeordneter Rolle, da der Systemtakt mit 32.768kHz sehr gering ist, und die Uhrzeit nur mit 7-Segment-Anzeigen angezeigt werden soll. Unser menschliches Auge bekommt es gar nicht mit, wenn kurzzeitig (kurzzeitig bedeutet hier im 10ns-Bereich) eine "falsche" Uhrzeit angezeigt wird.

Bild 4.5. zeigt die Realisierung der freilaufenden Uhr.

Block Freilaufende Uhr

Dieser Block wird hier wieder in mehrere Blöcke gemäß Abschnitt 4.2 aufgeteilt. Hier handelt es sich im Wesentlichen um verschiedene Zähler. Die Aufgabe des ersten Zählers ist die Erzeugung eines 1-Sekunden-Taktes aus dem Systemtakt. Die nächsten drei Blöcke sind für die Uhrzeit zuständig. Hier handelt es sich jeweils um BCD-Zähler von 0 bis 59 (für Sekunden und Minuten) bzw. von 0 bis 23 (für die Stunden).

Neben dem Systemtakt (clock_DCF) sind noch die Leitungen "Uhr freigeben", "Laden" und das dekodierte DCF-Telegramm vorhanden. Die freilaufende Uhr soll erst dann "mit ihrer Arbeit beginnen", wenn ein gültiges DCF-Telegramm empfangen wurde. Erst ab diesem Zeitpunkt wird die Anzeige mit der Leitung "Uhr freigeben" aktiviert und die freilaufende Uhr beginnt unabhängig von der DCF-Dekodierung zu laufen, wobei aber jedes Mal wenn ein gültiges DCF-Telegramm empfangen wurde die Uhr (also die Zähler) mit den Daten aus dem DCF-Telegramm überschrieben werden. Dazu ist die Leitung "Laden" zuständig. Weiters soll die Leitung "Laden" auch den Sekundenzähler und den Zähler für den Sekundentakt löschen.

Block "Zähler für Sekundentakt"

Dieser Block hat die Aufgabe aus dem Systemtakt (32.768kHz) einen 1-Sekunden-Takt zu erzeugen. Wenn man bedenkt, dass 32768 nichts anderes als 215 ist, und man außerdem weiß, dass jede Stufe eines Binärzähler durch 2 dividiert, so liegt es nahe für diese Aufgabe einen 15-Stufigen-Binärzähler zu verwenden. Altera bietet in seiner Entwicklungsumgebung (Quartus) u.a. solche Zähler (lpm_counter) an, welche noch über einige Zusatzfunktionen (wie Zähler freigeben, Zähler löschen, Zähler laden usw.) verfügen. Diese Zusatzfunktionen kommen uns sehr gelegen, da wir diese hier teilweise verwenden, denn dieser Zähler soll wie Bild 4.5. zeigt, freigegeben und gelöscht werden können.

Block "BCD-Zähler 0 - 59 (Sekunden)"

Bild 4.6. zeigt einen BCD-Zähler für die Sekunden. Dieser besteht aus einem 4-Bit-Zähler für die Sekunden-Einer (iZAEHLER_SEK_E) und einem 3-Bit-Zähler für die Sekunden-Zehner (iZAEHLER_SEK_Z), wobei diese Zähler aber nicht ihren gesamten Zählbereich (0 bis 15 bzw. 0 bis 7) ausnützen sondern nur den für Sekunden sinnvollen Bereich von 0 bis 9 (Einer) und 0 bis 5 (Zehner). Diese Eigenschaft lässt sich in der Software für jeden Zähler angeben. Die Steuerleitung "Laden" setzt beide Zähler zurück.

Block BCD Zaehler Sekunden

Der Takt für den Sekunden-Einer-Zähler (iZAEHLER_SEK_Z) stammt vom Block "Zähler für Sekundentakt" (siehe Bild 4.5.). Als Takt für den Sekunden-Zehner-Zähler (iZAEHLER_SEK_Z) dient der höchstwertige Ausgang (q[3]) des Sekunden-Einer- Zählers (iZAEHLER_SEK_E) in negierter Form (siehe Bild 4.6.). Bild 4.7. zeigt weshalb. Beim Übergang von 9 nach 0 des Sekunden-Einer-Zählers geht der Ausgang q[3] von high nach low. Da dies der einzige high-low-Übergang von q[3] ist und dieser genau dann auftritt wenn der Zählstand der Sekunde von 9 nach 0 übergeht kann dieser als Takt für die Zehner-Stelle dienen. Allerdings in negierter Form.

Zeitdiagramm des Zählers für die Einerstelle der Sekunde

Ähnliches gilt als Takt für den nachfolgenden Minuten-Zähler-Block. Als Takt für den Minuten-Einer-Zähler (iZAEHLER_MIN_E) dient der höchstwertige Ausgang (q[2]) des Sekunden-Zehner-Zählers (iZAEHLER_SEK_Z) in negierter Form (siehe Bild 4.6.). Bild 4.8. zeigt weshalb. Beim Übergang von 5 nach 0 des Sekunden-Zehner-Zählers geht der Ausgang q[2] von high nach low. Da dies der einzige High-Low-Übergang von q[1] ist und dieser genau dann auftritt wenn der Zählstand der Minute von 5 nach 0 übergeht kann dieser als Takt für die Einer-Stelle für die Minute dienen. Allerdings in negierter Form.

Zeitdiagramm des Zählers für die Zehnerstelle der Sekunde

Block "BCD-Zähler 0 - 59 (Minuten)"

Bild 4.9. zeigt einen BCD-Zähler für die Minuten. Auch dieser Block besteht aus einem 4-Bit-Zähler (für die Minuten-Einer, iZAEHLER_MIN_E) und einem 3-Bit-Zähler (für die Minuten-Zehner, iZAEHLER_MIN_Z). Der Unterschied zum Block "BCD-Zähler 0-59 (Sekunden)" ist, dass diese beiden Zähler geladen werden können. Und zwar mit der Minuten-Information aus dem DCF-Telegramm. Die Steuerleitung "Laden" hat also hier die Aufgabe die beiden Zähler mit den anliegenden Daten aus dem DCF-Telegramm zu laden. Diese Daten liegen an den Zähler-Eingängen data[3..0] (für die Einerstelle) bzw. data[2..0] für die Zehnerstelle an. Als Takt für Einerzähler dient, wie schon vorher erwähnt, der höchstwertige Zählerausgang des Zählers für die Sekunden-Zehnerstelle (q[2]), allerdings in negierter Form. Als Takt für den Minuten-Zehner-Zähler dient der höchstwertige Ausgang (q[3]) des Minuten-Einer-Zählers (iZAEHLER_MIN_E) in negierter Form (siehe Bild 5.9.). Auch hier gelten die gleichen Überlegungen wie für den Block "BCD-Zähler 0-59 (Sekunden)".

Block BCD Zaehler Minuten

Block "BCD-Zähler 0 - 23 (Stunden)"

Bild 4.10. zeigt einen BCD-Zähler für die Stunden. Auch dieser Block besteht aus einem 4-Bit-Zähler (für die Stunden-Einer, iZAEHLER_STD_E) und einem 2-Bit-Zähler (für die Stunden-Zehner, iZAEHLER_STD_Z).

Block BCD Zaehler Stunden

Die Besonderheit bei diesem BCD-Zähler ist, dass dieser nur bis 23 zählen soll. Dies erreicht man dadurch, indem man, sobald der BCD-Zähler zum Wert 24 gelangt beide Zähler löscht. Diese Aufgabe übernimmt das UND-Gatter (siehe Bild 4.10.). Der Ausgang dieses UND-Gatters ist mit den Lösch-Eingängen (.aclr) der beiden Zähler verbunden. D.h. sobald beide Eingänge am UND-Gatter logisch high sind wird der Zähler gelöscht und beginnt wieder beim Wert 0 zu zählen. Das Zeitdiagramm (Bild 4.11.) zeigt uns warum q[2] der Einerstelle und q[1] der Zehnerstelle als "Auslöser" für das Löschen der Zähler zu verwenden sind.

Zeitdiagramm (BCD-Zähler 0-23, Stunden)

Die Kombination q[2] (Einer) = 1 und q[1] (Zehner) = 1 tritt wie im Bild 4.11. zu erkennen nur beim BCD-Zählwert 24 auf.
Der "Nachteil" bei dieser Methode ist, dass kurzzeitig der Wert 24 am Ausgang des BCD-Zählers ansteht. Hier, bei dieser Anwendung als einfach DCF-Uhr, welche nur die Uhrzeit anzeigen soll ist dieser Nachteil nicht von großer Bedeutung, da die Zeit, wo dieser "falsche" Wert angezeigt wird nur sehr, sehr gering ist (im ns-Bereich), und daher vom menschlichen Auge gar nicht wahrgenommen wird.

Beginnt man nun mit den zuletzt beschriebenen Blöcken und fügt man diese in den jeweils übergeordneten Block ein, also die Bilder 4.10., 4.9. und 4.6. in 4.5., diesen und 4.2. in das Top-Level-Design (Bild 4.1.) so ergibt sich das fertige Design. Bild 4.12a. zeigt dieses fertige Gesamtdesign für die Anwendung des CPLD als DCF-Uhr. Bild 4.12b. zeigt nochmals die Zustandsmaschine (iZM_DCF) vom Bild 4.3.

Fertiges Design Zustandsdiagramm
nach oben


zurück zu Elektronik, Homepage

Autor: Buchgeher Stefan
Erstellt: 1. November 2005
Letzte Änderung: