Level 1: Analoguhr lesen.
Level 2: Binäruhr lesen.
Level 3: UNIX-Timestamp-Uhr lesen.
Level 4: UNIX-Timestamp binär formatiert lesen.
Ich hänge aktuell bei Level 3 fest. Ich versuche das bis 2038 hin zu bekommen…
Die Uhr lesen kann man ja schon früh in der Kindheit. In der Schulzeit wurde mir das zu langweilig, ich kaufte mir eine Binäruhr für’s Handgelenk. Mitschüler guckten mehrfach drauf und verstanden es nicht. Im Zimmer daheim gab es immer noch die normale Uhr, wurde aber auch wenig später von meiner binären Wanduhr abgelöst.
Irgendwann kam mir der Gedanke, warum eigentlich noch komplexer?
Die UNIX-Timestamp, für die die es nicht wissen, wird in Sekunden seit dem 1.1.1970 UTC (also minus eine Stunde für uns) dargestellt. Wobei Schaltsekunden in diesem Fall eine Abweichung darstellen.
Realisiert mit einem ATmega328, verpackt als Arduino Nano. Ich hätte auch einen ATmega roh nehmen können, nur dann wären Funktionen schwerer einzurichten, dazu später. Die Anzeige stellt sich aus 10 7-Segmenten zusammen, die auch weit über mein Ableben reichen würden (20.11.2286, 19:46:39 MEZ für 9999999999). Wäre da allerdings nicht das Problem, dass die UNIX-Zeit als signed 32-bit integer gespeichert wird, also binär maximal 32 Stellen umfassen kann, wobei das erste Bit das Vorzeichen angibt. Effektiv nutzbar für die Zukunft sind also nur 31 Stellen, 231-1 ergibt 2147483647 entsprechend 19.1.2038, 5:14:07 MEZ. Das RTC-Modul, in diesem Fall ein DS3231, verarbeitet die Uhrzeit jedoch nicht so, von daher habe ich Chancen, dass ich die Uhr akkurat laufend mit unter die Erde nehme. Aber bis dahin ist noch genügend Zeit.
Herausforderung war es, die 7-Segmente auf möglichst kleinem Platz unterzubringen, ohne SMD-Bauteile zu nutzen. Shift-Register waren wie immer die erste Wahl, problematisch allerdings: ein 74HC595 versteckt sich im DIP16, ist also 8 Pins breit. Ein 7-Segment ist nur 5 Pins breit. Also immer 3 Pins Abstand? Kommt nicht in Frage. Also musste eine Lösung her, die möglichst auch noch auf die 11 Cent Platinen (24×18 Pins) passt. Die Lösung war eigentlich einfach (siehe Bild). Die Shift-Register immer gedreht platzieren, eine zweite Platine oben drauf, auf der nur die Segmente platziert sind. Die Pins befinden sich entgegen der Darstellung aber auf der Unterseite. Gelb ist die Clock, Orange Latch, Lila Output Enable und grün sind die Daten, angefangen unten rechts (freier Pin).
Gelötet nach Prinzip 08/15 sah das zwar nicht so elegant aus, funktioniert aber trotzdem.
Dadurch ergab sich noch eine Herausforderung: da jedes zweite Shift-Register gedreht ist, ist deren Verkabelung genau entgegengesetzt zu den anderen. Im Programm wird das mit einem char-Array der Größe 10 gelöst im FIFO-Prinzip: soll ein Zeichen in die Anzeige, so wird das erste Zeichen gelöscht, alle anderen rücken auf und das Zeichen wird hinten reingeschrieben. Der Datentyp char umfasst 8 Bit, also genau so viele, wie ein 7-Segment mit Punkt Elemente hat. Wird die Funktion, die Daten in die Register zu schreiben, aufgerufen, so wird für jeden char im Array abwechselnd MSB und LSB als Shift-Variante benutzt. Bedeutet die Bits des chars werden abwechselnd von vorne und hinten gelesen.
Es gibt unterschiedliche Funktionen von mir zum Ausgeben: writeString, writeInt, writeLong und writeDouble. Die Funktionen nehmen den entsprechenden Datentyp entgegen und rufen für jedes Zeichen eine Funktion auf, die die entsprechenden Bits für das Zeichen in das Array schreibt. Dargestellt werden können fast alle Buchstab ohne Probleme. M, W und V sind etwas schwer, aber man kann es auch erkennen. S und Z sind gleich mit 5 und 2. Der Trick dabei ist, je nach Zeichen zwischen Groß- und Kleinschreibung zu variieren. Ein T wäre beispielsweise groß nicht darstellbar, t hingegen schon. Sonderzeichen gibt es beide Akzente, das Circumflex, Grad-Symbol, Komma, Punkt, Ausrufezeichen, einfaches und doppeltes Anführungszeichen, Binde- und Unterstrich, das AT-Symbol, eckige Klammern und beide Schrägstriche. Das Kongurenzsybol oder Xi (Ξ) wird angezeigt, wenn das Zeichen nicht darstellbar ist.
Die Uhrzeit wird dann vom Uhrenmodul gelesen, als UNIX-Timestamp (now()-3600, von MEZ auf CET) and die Funktion writeLong übergeben. Diese schreibt dann die Zeit in das Array. Am Ende wird es in die Register geschrieben und angezeigt.
UNIX-Timestamp kann kaum einer lesen, also gibt es über den Taster noch unterschiedliche Modi. Die Zeit im normalen leserlichen Format (für MEZ), das Datum, da das DS3231 zur Stabilisierung des Takts auch einen Temperatursensor mit beinhaltet, kann die Temperatur auch in °C ausgegeben werden. Jetzt kommt noch die Verwendung des Nano statt des Chips alleinig ins Spiel: über die serielle Schnittstelle (direkt über USB mit dem aufgelöteten FTDI-Chip) kann im letzen Modus beliebiger Text angezeigt werden. Jede Zeile, die man schickt, wird auf den Segmenten ausgegeben. Insgesamt bis zu 20 Zeichen auf 10 Modulen, denn folgt ein Punkt auf ein anderes Zeichen, so wird dieser im gleichen Modul angezeigt. Bedeutet, wenn man 1. sendet, wird 1. in einem Modul angezeigt und nicht der Punkt in einem Separaten. Gleiches Prinzip wird beim Datum angewandt. Hier nutze ich einen Trick in meiner eigenen Funktion:
writeDouble(double d, int precision) wird mit 0 Nachkommastellen aufgerufen, der Punkt wird trotzdem an die Zahl angefügt.
Als zusätzliches Feature verfügt die Uhr über eine Dimm-Funktion, gesteuert über einen Fotowiderstand. Der wird eingelesen und entsprechend des Wertes wird das PWM-Signal an Output Enable gesteuert. Output Enable schaltet LOW die Pins der Register entsprechend durch, bei HIGH werden alle in einen Floating-Zustand gesetzt. Floating ist ein undefinierter Zustand irgendwo zwischen 0 und 1, reicht aber nicht aus, um die Segmente mit genügend Strom zu versorgen. Der Vermerk im Datenblatt „Floating LOW“ lässt vermuten, dass es ein sehr kleiner Pull-Down Widerstand ist, der die Leitung dann doch halbwegs definiert lässt.
Inzwischen habe ich Level 3.5 hinzugefügt: UNIX-Timestamp hexadezimal. Macht es nicht wirklich leichter…