Tinkering Arduino

eine kleine Welt der Elektronik.

Binäre Zeitausgabe Mit Echtzeituhr (DS1307)

Mit der letzten Bestellung kam auch der DS1307 Real Time Clock Bausatz bei mir an. Dieser einfache Bausatz ist innerhalb von ca. 15 Minuten zusammengebaut und lädt dann sofort ein, mit der Uhr zu spielen.

Beginnen wir aber am Anfang …

Der Aufbau

Der Bausatz von adafruit industries (Bezugsquelle: Watterott.com) kommt in einer kleinen Antistatikfolie und enthält eine kleine Anzahl von Bauelementen. Über die URL auf dem Aufkleber kommt man schnell zur sehr guten englischsprachigen Anleitung, die auch durch viele Bilder unterstützt mit wenigen Englischkenntnissen, den Bausatz schnell zusammenlöten lässt. Schritt für Schritt lötet man die Wiederstände, den Keramikkondensator, den Quarz und den Chip (DS1307) auf. Dann noch den Batteriehalter und die Stiftleiste für das Steckbrett. Fertig. Das ganze sollte dann aussehen, wie auf dem Foto.

Nach der Installation der Arduino-Bibliotek “RTClib” und dem Neustart der Arduino IDE kann man die mitgelieferten Beispielprogramme ausprobieren. Diese zeigen, wie die Uhr ausgelesen wird und wie sie über die Systemzeit des Rechners gesetzt werden kann. Also, erst die Uhrzeit des Rechners einstellen, dann das Programm starten.

Die Schaltung

Weiterhin habe ich hier noch ein Arduino mit vorinstallierten LEDs. Das Arduino kompatible Board kommt aus dem Lernpaket “Lichteffekte mit Arduino”, welches mit freundlicher Weise der Franzis Verlag zur Verfügung gestellt hat. Es ist aber auch möglich, über die Ausgänge und ein Steckbrett die LEDs selbst zu stecken. Dann kann man auch mit den den Abständen spielen um so die Stunden und Minuten besser trennen zu können.

Die Schaltung ist dann auch schnell gesteckt.

Da die Echtzeituhr an A4 (SCL) und A5 (SDA) geschaltet ist (siehe Belegung für die benutzte Wire-Bibliothek), können diese für die Uhrzeit nicht genutzt werden. Damit fallen PIN20 und PIN19 für uns heraus. Um ein wenig Abstand zu den nicht beeinflussbaren LEDs zu haben, beschränken wir uns auf die LEDs an PIN1-PIN17. Das reicht gerade so aus, um die Uhrzeit abzubilden. Für 60 Minuten (0-59) benötigen wir in der binären Darstellung 6 LEDs, denn 26 = 64. Für die 24 Stunden benötigen wir 5 LEDs, denn 25 = 32. Für eine 12 Stundenanzeige würden sogar 4 LEDs ausreichen. Weiterhin soll die Uhr die Sekunden ausgeben. Um die Stunden und Minuten optisch zu trennen, soll wie bei Uhren mit 7-Segment-Anzeigen ein Punkt zwischen der anzuzeigenden Stunde und der anzuzeigenden Minute blinken. Im Bild habe ich einmal alle verwendeten PINs eingeschalten. Die ersten vier LEDs zeigen die Stunde an, dann folgt nach einer nicht verwendeten LED der Punkt für die Sekunden, dann wieder eine nicht verwendete LED. Dann kommen die sechs LEDs für die Minuten.

Wer das Arduino Board nicht aus dem Franzis Paket hat, kann auch gern die folgende Schaltung aufbauen. Bei dieser Version könnte man sicherlich die Beschaltung der PINs optimieren, müsste dann aber die Programme entsprechend anpassen. Damit es in den Hinweisen zum Programm einfacher wird, lassen wir die Belegung, die sich aus den gleichen Abständen der PINs auf dem Arduino von Franzis befinden.

Das Programm

Wie üblich habe ich die Programmierung in unterschiedliche Funktionen ausgelagert, um möglichst flexibel zu sein und schnell mal eine Funktion umzunutzen.

Aber erst einmal den Anfang des Programms mit den notwendigen Includes und dem Setup. Das habe ich soweit aus dem Beispielprogramm übernommen. Ich habe allerdings noch die Zeilen 10-13 hinzugefügt, um die PINs 0-17 entsprechend für die LEDs auf OUTPUT zu stellen. Dabei definiere ich auch die nicht verwendeten LEDs zu OUTPUT-PINs und setze diese auf LOW.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 RTC;

void setup () {
    Wire.begin();
    RTC.begin();

    for (uint8_t pin=0; pin<18; ++pin) {
      pinMode(pin, OUTPUT);
      digitalWrite(pin, LOW);
    }

    if (! RTC.isrunning()) {
      Serial.println("RTC is NOT running!");
    }
}

Bevor es jetzt zur Funktion loop() geht, sollen drei andere Funktionen eingefügt werden. Zunächst die Funktion setLED5() für die Ausgabe für vier LEDs der Stundenanzeige. Diese bekommt eine Dezimalzahl und ein Offset. Der Offset lässt die Position, wo die Stunde angezeigt werden soll, flexibel ausgeben und wird durch den Aufruf bestimmt.

1
2
3
4
5
6
7
void setLED5 (int decimal, int offset) {
    digitalWrite(offset, HIGH && (decimal & B00010000));
    digitalWrite(1 + offset, HIGH && (decimal & B00001000));
    digitalWrite(2 + offset, HIGH && (decimal & B00000100));
    digitalWrite(3 + offset, HIGH && (decimal & B00000010));
    digitalWrite(4 + offset, HIGH && (decimal & B00000001));
}

Eigentlich gibt es hier für Arduino-Prgrammierer nicht viel zu erwähnen. Es werden mit digitalWrite() die LEDs an den vier Positionen gesetzt. Diese berechnen sich mit dem Wert der Variable offset. Interessant ist sicherlich, wie sich der Wert berechnet, der dann die LED an der Position ein- oder ausschaltet. Beginnend mit der inneren Klammer für das erste digtialWrite(). Hier wird die bitweise logische Verknüpfung von decimal und B00010000 gebildet. Angenommen decimal ist eine 19, dann entspricht der auszugebene Wert B00010011. Bei der bitweisen logischen UND-Verknüfung wird jede Stelle beider Zahlen mit einander verglichen und die Stelle im Ergebnis ist nur 1, wenn beide Stellen 1 eins sind.

19 = B00010011 2^5 = 16 = B00010000 Ergebnis
1. Stelle 0 0 0
2. Stelle 0 0 0
3. Stelle 0 0 0
4. Stelle 1 0 0
5. Stelle 0 0 0
6. Stelle 0 0 0
7. Stelle 1 0 0
8. Stelle 1 0 0

Das Ergebnis aus der logischen bitweisen Verknüpfung ist also B00010000. Mit diesem Ergebnis wird eine logische Verknüpfung mit HIGH errechnet, denn wir können mit digitalWrite den Wert B00010000 = 25 = 16 nicht zuweisen. Da aber der Wert B00010000 = 25 = 16 ungleich 0 ist, wird die logische Verknüpfung mit HIGH ebenfalls ein HIGH liefern. Damit wird die LED eingeschaltet. Dies wird nun für alle nachfolgenden Stellen auch gemacht und so kann man ohne viel if-Bedingungen oder case-Anweisungen die entsprechenden PINs auf HIGH setzen und somit die LEDs zum leuchten bringen.

Eine weitere Funktion deckt die Zahlen kleiner 64 ab, also die Zahlen, die wir für die Minuten benötigen. Die Ausgabe für setLED6() könnte wie folgt aussehen. Die Berechnung folgt dem gleichen Schema.

1
2
3
4
5
6
7
8
void setLED6 (int decimal, int offset) {
    digitalWrite(offset, HIGH && (decimal & B00100000));
    digitalWrite(1 + offset, HIGH && (decimal & B00010000));
    digitalWrite(2 + offset, HIGH && (decimal & B00001000));
    digitalWrite(3 + offset, HIGH && (decimal & B00000100));
    digitalWrite(4 + offset, HIGH && (decimal & B00000010));
    digitalWrite(5 + offset, HIGH && (decimal & B00000001));
}

Das kann aber noch optimiert werden, da sich die Funktion setLED6() und setLED5() kaum unterscheiden.

1
2
3
4
5
void setLED6 (int decimal, int offset) {
    digitalWrite(offset, HIGH && (decimal & B00100000));

    setLED5(decimal, offset+1);
}

In der neuen kürzeren Funktion wird die eine LED mehr zuerst gesetzt, da die mit dem 6.Bit verglichen wird. Dann wird die Funktion für 5 LEDs aufgerufen. Damit die Position stimmt, welche LED leuchten soll, muss noch das Offset um 1 erhöht werden, also die Ausgabe um eine LED nach rechts verschoben werden. Das könnte man nun rekursiv fortführen. Wir beschränken uns hier aber auf die zwei Funktionen, da diese für das Vorhaben ausreichen.

Eine dritte Funktion benötigen wir für das Blinken von Sekunde zu Sekunde. Die LED soll zu jeder ungeraden Sekunde leuchten, zu jeder geraden Sekunde erlöschen. Hier kann man die Modulo von 2 Funktion anwenden. Diese liefert den Rest der ganzzahligen Division einer Zahl durch 2. Für gerade Zahlen keinen Rest, also 0, für ungerade Zahlen erhält man den Rest 1.

1
2
3
void blinkLEDforSeconds (int decimal, int offset) {
    digitalWrite(3 + offset, decimal%2);
}

Da die Funktionen definiert sind, fällt unsere loop() Funktion auch schön übersichtlich aus.

1
2
3
4
5
6
7
8
9
10
void loop () {
    DateTime now = RTC.now();

    setLED5(now.hour(), 0);
    setLED6(now.minute(), 8);

    blinkLEDforSeconds(now.second(), 6);

    delay(1000);
}

In der ersten Zeile wird die Zeit von der Echtzeituhr gelesen, dann rufen wir die bereits bekannten Funktionen für die Stunden und die Minuten auf. Die nächste aufgerufene Funktion sorgt für das Blinken pro Sekunde und dann wartet das Programm eine Sekunde, um dann erneut die Zeit auszulesen und darzustellen.

Das Arduino-Programm clock_to_binary_led_01.ino mit ein paar anderen Ausgaben könnt ihr hier herunterladen. Es sollte soweit vollständig sein und erspart den einen oder anderen Tippfehler.

Ein kleines Video vom aktuellen Programm zeigt, wie die Zeit sofort nach dem Einschalten angezeigt wird. Die aktuelle Aufnahmezeit 01110 * 100011 (14:35) wechselt nach ein paar Sekunden auf 01110 * 100100 (14:36).

Comments