Eine DS1307 als Uhr verwenden

Raspberry Pi

Hier zeige ich euch wie ihr euer Raspberry Pi mit einer DS1307 I²C Real-time clock verbindet, die ihr anschließend für viele verschiedene Zwecke nutzen könnt.

-> Die Schaltung:

Die Beschaltung der DS1307 sieht so aus:

DS1307
Wichtig ist, dass ihr die Pull-Up Widerstände für den I²C an 3,3V legt, damit der I²C Bus einen Pegel von 3,3V führt.
Schließt ihr die Widerstände an 5V an, so kann dies euer Raspberry Pi zerstören.
Alle notwendigen Anschlüsse befinden sich an dem P1 vom Raspberry Pi.

-> Das C-Programm:

Das C-File um die DS1307 auszulesen könnt ihr euch am Ende der Seite downloaden.
Wie funktioniert dieses Programm nun?
Als aller erstes seht ihr diese Funktion.

Diese Funktion hat nachher den Zweck, dass eine Zahl, die im BCD-Format vorliegt (so wie die DS1307 sie ausgibt), in eine Dezimalzahl umgewandelt wird.
Wie dies funktioniert zeige ich euch an einem Beispiel.
Die DS1307 gibt z.B. die Zahl 10 (dezimal) zurück.
Diese Zahl entspricht dem BCD-Code 0001 0000 (beim BCD-Code wird jede Ziffer einer Zahl separat in einen Binärcode umgewandelt).
Der BCD-Code 10 entspricht also dem Hex-Wert 0x10 oder Dezimal 16.
Von diesem Wert wird nun der Rest von einer Division dieses Wertes durch 16 bestimmt (ausgedrückt durch das % oder auch „Modulo“ genannt):

Anschließend wird der Wert mit dem Produkt aus 10 * (Wert/16) addiert.
In dem Beispiel beträgt das Produkt:

Aus diesen beiden Werten wird nun die Summe gebildet:

Und diese Summe wird als Dezimalzahl aus der Funktion übergeben.
Ein zweites Beispiel:

Zahl aus der RTC: 15
Binärcode der Zahl: 0001 0101
Wert des Binärcodes: 21
Modulo: 21/16 = 5
Produkt: 10 * (21/16) = 1,…… (es wird nur die Zahl 1 gespeichert, da es sich bei
der Variable Wert um einen Char handelt)
Summe: 5 + 10 = 15
Die Zahl aus der RTC entspricht also der Dezimalzahl 15.

Als nächstes beginnt das Hauptprogramm, wo mit dieser If-Abfrage

ein File von einem Device geöffnet wird dessen Namen vorher in der Variable Device gespeichert wurde. Wenn das File erfolgreich geöffnet wurde, wird der Variable File eine Zahl zugewiesen, mit der das geöffnete File identifiziert werden kann..
Sollte aus irgendeinem Grund das Device nicht erfolgreich geöffnet werden können, sprich es wird ein Wert zurück gegeben der kleiner als 0 ist, wird ein Fehler ausgegeben.
Mit dem O_RDWR lege ich fest, dass ich aus dem File lesen und in das File schreiben möchte.
Der nächste Schritt ist das setzen und überprüfen der I²C Adresse. Dies geschieht mit folgender Abfrage:

Die Variable RTC beinhaltet die Adresse der DS1307, also die 0x68. Auch hier ist es wichtig, dass diese Adresse NICHT das R/W Bit enthält. Dies wird vom Linuxsystem automatisch hinzugefügt.
Sollte kein Device mit der angegebenen Adresse am Bus angeschlossen sein, so gibt die Funktion einen Wert zurück der kleiner als 0 ist.
Ist dies der Fall wird ein Fehler in der Konsole ausgegeben.
Nun ist die Verbindung zwischen Raspberry Pi und der DS1307 hergestellt und es kann damit begonnen werden Daten auszutauschen.
Als allererstes stelle ich den Zeiger für die Register innerhalb der DS1307 auf die Startadresse 0.
Dies geschieht so:

Ich setze die erste Stelle des Arrays Date auf 0 und anschließend schreibe ich den Wert mittels der write Funktion in die geöffnete Datei vom I²C.
Die Funktion ist wie folgt aufgebaut:

  1. File: Das File in das geschrieben werden soll. Hierbei handelt es
    sich um das vorher geöffnete I²C File.
  2. Date: Die Variable, dessen Inhalt in die Datei geschrieben werden
    soll.
  3. 1: Die Anzahl der Bytes die geschrieben werden sollen. Da ich nur
    den Wert 0x00 übertragen möchte, sprich 1 Byte, steht dort eine
    1.
  4. != 1: Die Funktion write gibt als Rückgabewert die Anzahl der
    geschriebenen Bytes zurück. Stimmt die Anzahl mit 1, also
    die Anzahl der zu schreibenden Bytes, nicht überein wurden
    entweder keine Bytes oder zu viele Bytes geschrieben und es
    wird ein Fehler ausgegeben.

Nachdem der Zeiger auf 0 gestellt wurde, kann damit begonnen werden die einzelnen Register der DS1307 auszulesen.
Dies wird mittels read Funktion bewerkstelligt:

Diese Funktioniert quasi genau so wie eine write Funktion (nur das diese halt ließt und eine write Funktion schreibt ;) ).
Die Variable Buffer wurde am Anfang des Programmes als Konstante festgelegt und umfasst den Wert 7.
Dadurch werden 7 Bytes ausgelesen und nacheinander in Date (einem 7 Byte großen Array) gespeichert.
Auch hier wird wieder ein Fehler ausgegeben, sobald die Anzahl der gelesenen Bytes nicht mit der Anzahl zu lesender Bytes überein stimmt.
Sobald der Wert der gelesenen Werte mit der Vorgabe überein stimmt, springt das Programm in den Zweig der Abfrage der für die Weiterverarbeitung der Daten verantwortlich ist.
Dort werden als allererstes die Werte, die in der Variable Date gespeichert sind, vom BCD Format in eine Dezimalzahl umgewandelt. Dafür verwende ich die Funktion BCD2D, welche ich am Anfang des Artikels bereits erklärt habe.
Das ganze sieht so aus:

Die For-Schleife wird so lange durchlaufen, bis der Wert von Buffer (der ja für die Anzahl der zu lesenden Bytes steht) erreicht wurde.
Bei jedem Durchlauf wird die passende Stelle im Array Date umgewandelt und wieder in dem Array Date gespeichert.
Danach werden die einzelnen Werte ausgegeben.
Da die DS1307 nur einen Zahlenwert für den Wochentag ausgibt, habe ich noch eine Switch-Case Anweisung in das Programm integriert um herauszufinden welcher Wochentag gerade ist:

So wird jedem Zahlenwert ein Wochentag zugeordnet.
Bevor ihr das Programm nun starten könnt, müsst ihr erst die aktuelle Uhrzeit in die DS1307 reinschreiben.
Dies habe ich per Konsolenbefehle erledigt:

Dadurch setze sich den Zeiger innerhalb der RTC auf 0.
Anschließend stelle ich die Uhrzeit ein (hier 14:30:00 Donnerstag, den 01.11.2012):

Nachdem das Programm mit dem Befehl

kompiliert und mittels

gestartet wurde (vorher in das Verzeichnis /Programme wechseln oder ein anderes anlegen/angeben) sieht die Ausgabe so aus:

DS1307:Raspi

-> Die RTC als Uhr einbinden:

Da die RTC nun funktioniert, könnt ihr diese als Uhr für euer Raspberry Pi einbinden.
Diese funktioniert dann genau so wie die Uhr in eurem PC.
Als allererstes gebt ihr

Anschließend könnt ihr mit dem Befehl

die Uhr auslesen. Eine Ausgabe sieht dann z.B. so aus:

Thu 01 Nov 2012 15:58:04 CET -0.009341 seconds

Diese Zeit berücksichtigt aber nicht die aktuelle Zeitzone.
Um diese mit zu berücksichtigen, müsst ihr erst die richtige Zeitzone einstellen. Dafür verwendet ihr den Befehl:

Anschließend erscheint folgendes Fenster:

Raspi_TZ
Dort könnt ihr eure Zeitzone einstellen. Die Eingaben bestätigt ihr anschließend mit Ok.
Nach einem reboot wird bei der Zeitausgabe über

auch die aktuelle Zeitzone berücksichtigt.
Der Nachteil ist auch hier wieder, dass die Festlegung der DS1307 als Systemuhr nur bis zum Neustart gültig ist.
Um dies zu umgehen habe ich diese zwei Zeilen in der Datei /etc/rc.local hinzugefügt:

Dadurch wird der Befehl

echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device

bei jedem Neustart ausgeführt.
Nun habt ihr eine funktionierende Systemuhr die auch nach einem Stromausfall weiter läuft ;)

 

Dokumentation:

 

-> Zurück zum I²C

31 thoughts on “Eine DS1307 als Uhr verwenden
  1. Schöner Artikel :) Ich habe nur eine Frage: An welche Adern kommen die Pull-Up Wiederstände dran und welche Werte sollte sie haben?

  2. „Wichtig ist, dass ihr die Pull-Up Widerstände für den I²C an 3,3V legt, damit der I²C Bus einen Pegel von 3,3V führt.
    Schließt ihr die Widerstände an 5V an, so kann dies euer Raspberry Pi zerstören.
    Alle notwendigen Anschlüsse befinden sich an dem P1 vom Raspberry Pi.“

    Ich habe die aktuelle Version mit 512 MB Ram und 2 USB Schnittsellen.
    Als Nicht-Elektroniker verstehe ich das nicht ganz. Wenn ich das Schaltbild sehe, würde ich Pin 3 und 4 auf Masse legen, Pin 8 mit 5 Volt vom USB-Port versorgen (oder einem anderen Pin, der 5 V führt) und als Batterie eine 3 V Knopfzelle einsetzen. Die Schaltung gibt es fertig hier http://www.ebay.de/itm/Tiny-RTC-I2C-DS1307-AT24C32-Real-Time-Clock-Module-For-Arduino-AVR-UNO-ARM-PIC-/261093897322?pt=Elektromechanische_Bauelemente&hash=item3cca68b46a für knapp 4 Euro. Kann man die verwenden, oder muss ich die Version, wie von dir oben beschrieben zusammenbauen?

    Die beiden Datenleitungen würde ich direkt an die GPIO Pins 3 und 5 anschließen. So lese ich zumindest das hier http://www.rn-wissen.de/index.php/Raspberry_PI:_GPIO

    Aber gemäß deiner Warnung wäre das wohl fatal. Wo genau kommen denn dann noch die Widerstände hin, bzw. wo muss was anders angeschlossen werden, als ich es oben geschrieben habe? Tut mir leid, mit so einer für dich banalen Frage zu kommen, aber ich möchte sicher sein, dass die kleine Himbeere nicht vorzeitig abraucht.

    Frank

    • Hallo Frank,

      die Pull-Up Widerstände befinden sich schon auf dem Raspberry Pi, sprich du brauchst keine zusätzlichen.
      Das Modul kannst du wahrscheinlich nicht ohne weiteres verwenden, da die Pull-Up Widerstände für den I²C sehr wahrscheinlich an die 5V Betriebsspannung angeschlossen sind (ich kann es leider nicht genau sagen, da mir ein Schaltplan des Moduls fehlt).
      Wenn dies der Fall ist darfst du das Modul NICHT ohne Pegelwandler anschließen, da die I²C Pins des Raspberry Pi keine 5V vertragen.
      Das „Problem“ ist der DS1307 Chip. Dieser Chip arbeitet nur mit 5V Betriebsspannung und von daher musst du mit zwei unterschiedlichen Spannungen arbeiten (5V Betriebsspannung und 3,3V Signalspannung für den I²C).
      Du kannst (wenn du möchtest und dir die Sache zu unsicher ist) mir auch gerne das Board zu schicken dann schaue ich mal drüber, baue es um, teste es und schicke es dir wieder zu (brauchst nur den Versand zu übernehmen).
      Oder du baust dir selber ein Modul auf (gemäß dem Schaltplan oben).
      Falls du noch weitere Fragen haben solltest, schreib mir ruhig eine E-Mail (auch wenn du meinst es seien banale Fragen….lieber einmal mehr gefragt als einmal zu wenig) :)

      Gruß
      Daniel

      • Den Schaltplan zu dem Tiny RTC Modul gibt es hier: http://www.hobbyist.co.nz/sites/default/files/docs/RTC/Tiny_RTC_schematic.pdf

        Wie schon richtig vermutet, liegen die beiden Datenleitungen mittels R2 und R3 als Pullup auf 5V. Da der Raspi eigene Pullups für 3,3V hat, einfach R2 und R3 auslöten und fertig. Auf die Art und Weise habe ich gerade ein solches Modul am Raspi in Betrieb genommen. Praktischerweise bekommt man den 32k EEPROM auch direkt mit in den Zugriff (Vcc / GND / SCL / SDA liegen an beiden Stiftleisten gleich an). Mal sehen, was sich damit für Unfug treiben läßt ;)

        • Hey Steffen,

          ja genau. Bei fertigen Modulen IMMER vorsichtig sein. Die haben meistens Pull-Up Widerstände drauf gelötet und dann kann das schnell in die Hose gehen.
          Viel Spaß mit der RTC :)

          Gruß
          Daniel

  3. Hallo Kampi!

    Vielen Dank erstmal für die Anleitung.
    Leider habe ich ein Problem mit den Berechtigungen.

    Ich benutze nicht den DS1307 sondern den MCP7940 – aber die sind kompatibel, von daher dürfte das eigentlich keine Rolle spielen.

    Bei der Zeile „echo…“ kommt aber bei mir immer die Meldung „Keine Berechtigung“.
    Auch SUDO hilft nichts. Es funktioniert nur wenn ich vorher mit „sudo bash“ eine Root-Konsole öffne und dann dort die Zeile ausführe.

    Das hilft mir aber nicht weiter wenn ich das ganze beim Systemstart ausführen wil – oder gibt es eine Möglichkeit mit der selben Berechtigungdie die rc.local auszuführen?

    Hast du deinen Tipp für mich?

    • Hey,

      die rc.local wird (meines Wissens nach) mit root-Rechten ausgeführt.
      Wie sieht deine rc.local aus? Ist die von den Berechtigungen her richtig gesetzt (also 777)?

      Gruß
      Daniel

      • Hallo,
        vielen Dank für deine Hilfe.
        Ich bin jetzt hinter den Fehler gekommen: Das Problem war nie die RTC sondern dass ich immer probiert habe ohne sudo die hwclock auszulesen, das geht aber scheinbar nur mit root…
        Arg! Das zeigt mal wieder: umso länger man nach dem Fehler sucht, umso dümmer ist er…
        Vielen dank nochmal für die Anleitung – hat alles eigentlich tadellos funktioniert!

  4. HalliHallo,

    kann mir jemand eine Einkaufliste für dieses Projekt schreiben?
    Pi ist bereits vorhanden :)

    Bzw ein Bild des fertigen Endprodukts würde mich auch weiterbringen…

    Vielen Dank im Voraus.

    Gruß
    Peter

  5. Hallo guten Abend.

    Ich habe mir heute dieses ds1307 modul zugelegt.
    leider habe ich ein komisches problem damit.
    i2cdetect findet das modul
    ich kann die uhr stellen und mit hwclock auslesen
    aber das oben beschriebene c programm bricht ab mit :
    „fehler beim schreiben der daten“.
    eine erneutes i2cdetect zeigt plötzlich an der adresse 0x86 2 „UU“
    offensichtlich ist das ding „busy“ ?

    warum das ?,

    evtl hat jmnd. ne erklärung.
    besten dank

    • Hey,

      wenn du die RTC als Hardwareclock einbindest, ist die durch den Kernel reserviert und du kannst nicht mehr drauf zugreifen.

      Gruß
      Daniel

  6. Hallo,

    vielleicht hat jemand eine Idee grad für mich…
    Selbst ich habe die Uhr nun am Laufen. Hurra.

    Ein paar Neustarts und irgendwie hing meine date-Ausgabe immer mehr hinterher…
    Und so, wie es nun aussieht, scheint die Uhr nicht beim Neustart gestartet zu werden.
    Ich gehe dann wieder ins Terminal und

    sudo hwclock

    bringt diese „Cannot access the Hardware Clock via any known method.“-Meldung.
    dann wieder

    sudo bash
    echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device

    dann läuft sie auch wieder

    sudo hwclock

    beweist es mir…
    Jemand eine brilliante Idee was ich übersehen habe?
    in der /etc/rc.local habe ich

    echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device

    eingetragen und die Rechte habe ich in 777 geändert!?!?

    Würd mich freuen über DEN Tip des Tages ;-)
    Ja, ich bin fast am verzweifeln…

    Grüße aus dem Speckgürtel

    Olli

    • Hallo Olli,

      hast du mal geschaut ob es beim Booten eine Fehlermeldung am Anfang gibt? Die sollte noch vor dem Login Prompt auf dem Bildschirm erscheinen.

      Gruß
      Daniel

      • Auch eine Möglichkeit. Stimmt.
        Was da immer durchläuft seh ich nicht. Der Raspi hat noch nie einen Monitor gesehen. Habe ihn von Anfang an headless installiert und betrieben und gehe wenn über ssh oder vnc rauf.
        Muß ich mal meine Röhre rausholen und ranhängen ;-) – ich schau mal und berichte. – ODER kann man die Ausgaben, die er am Anfang ausgibt irgendwo nachsehen ggf. exportieren? Muß mal sehen, ob ich sowas wie eine sys.log finde, das wär weitaus einfacher… – ich meld mich, wenn der Raspi bei mir steht.

        • Hey,

          was mir gerade einfällt….verwendest du ein 512er Raspi?
          Dann ändere mal das „i2c-0“ in dem Befehl in „i2c-1“.

          Gruß
          Daniel

          • Das ist ein 512er und geändert war es auch schon. Muß sehen, ob ich mich heut Abend ransetzen kann. Bin ganz juckelig. Hab heute nach 4 Wochen des Wartens mein Display vom Chinamann bekommen. Das werd ich mal ranhängen und dann werd ich mal sehen, ob da Meldungen durchrauschen, die Aufschluß geben, was das Nichtladen anbelangt. Darauf ist noch nichtmal ein vergurktes, fast neues System drauf momentan – daher kann ich es mir nicht ganz erklären, warum von 1000den Leuten immer nur ich die Probleme anziehe…

  7. Hey,

    ich habe ein kleines Problem und zwar kommt bei mir die Meldung, dass das i2c Modul nicht geladen werden kann. Habe bis jetzt die ganze Zeit rumprobiert, bin aber auf keine Lösung gekommen. Ich frage mich schon die ganze Zeit, woran es liegen kann…

    Viele Grüße,

    Marc

      • Hey,

        das Programm bricht schon danach ab:

        if ((File = open(Device, O_RDWR)) < 0)
        {
        printf("I²C Modul kann nicht geladen werden!\n");
        return -1;
        }

        Ich habe allerdings sonst keine Fehlermeldungen bekommen.

        Gruß,
        Marc

        • Hey Marc,

          ersetz mal das „/dev/i2c-0“ durch „/dev/i2c-1“. Du hast sicher eine 512MB Variante. Da wird ein anderer I²C Verwendet. Dieses Programm habe ich auf einem 256MB Pi geschrieben.

          Gruß
          Daniel

  8. Ich muss trotzdem einmal fragen. Wozu benötigt man so etwas? Mein Raspi (B)zeigt die Uhrzeit schon ohne zusätzliche Hardware?!?! Oder hab ich hier nen Denkfehler.

    • Hallo Andre,

      das stimmt. Dein Raspi bekommt die Uhrzeit bei angeschlossenem LAN über das NTP-Protokoll.
      Das funktioniert aber nur, wenn der Raspi durch einen NTP-Server (z.B. Router) eine Uhrzeit bekommt.
      Wenn dein Raspi irgendwo autark steht (z.B. auf dem Schreibtisch ohne LAN-Anbindung) oder du keinen Router hast, dann wird bei jedem Booten die Uhrzeit auf den 1.1.1970 gesetzt.
      Wenn du die RTC hingegen als Uhr einbindest, wird diese einmal eingestellt und der Raspi behält auch nach dem Ausschalten die Uhrzeit, da diese in der RTC gespeichert und weiter gezählt wird.
      Beim booten holt sich das Betriebssystem dann direkt aus der Uhr die aktuelle Uhrzeit raus und nutzt diese.

      Ich hoffe das hilft dir weiter :)
      Ansonsten einfach weiter fragen!

      Gruß
      Daniel

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Time limit is exhausted. Please reload CAPTCHA.