Kampis Elektroecke

Eine DS1307 als Uhr verwenden

Raspberry Pi

Hier zeige ich, wie der Raspberry Pi mit einer DS1307 I2C Real-Time Clock verbunden werden kann, um diese z. B. als Zeitgeber für den Raspberry Pi zu verwenden.

Beschaltung der DS1307:

Die RTC wird entsprechend der Beispielschaltung aus dem Datenblatt beschaltet:

Wichtig ist, dass die Pull-Up Widerstände für den I2C an 3,3 V gelegt werden, damit der I2C Bus einen Pegel von 3,3 V führt. Alle notwendigen Anschlüsse befinden sich an dem Steckverbinder P1 des Raspberry Pi.

Das C-Programm:

Die Funktion

char BCD2D(char Wert)
{
	return (Wert % 16 + 10 * (Wert / 16));
}

dient dazu um die BCD-codierten Werte der DS1307 in eine Dezimalzahl umzuwandeln. Die Funktionsweise soll durch das nachfolgende Beispiel erklärt werden:

Die DS1307 gibt z. B. die Zahl 10 (dezimal) zurück. Diese Zahl entspricht als BCD-Code dem Byte 0001 0000 (beim BCD-Code wird jede Ziffer einer Zahl separat in einen Binärcode umgewandelt), 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):

10 % 16 = 10

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

10 * (10/16) = 0

Aus diesen beiden Werten wird nun die Summe gebildet:

0 + 10 = 10

Und diese Summe wird als Dezimalzahl aus der Funktion übergeben.

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

if((File = open(Device, O_RDWR)) < 0)
{
	printf("Unable to open I2C...!\n");
	return -1;
}

die I2C-Schnittstelle Device geöffnet wird. Wenn die Schnittstelle erfolgreich geöffnet wurde, wird der Variable File eine Zahl zugewiesen, mit der die geöffnete Schnittstelle identifiziert werden kann…

Sollte aus irgendeinem Grund die Schnittstelle nicht erfolgreich geöffnet werden können, sprich es wird ein Wert zurück gegeben der kleiner als 0x00 ist, wird ein Fehler ausgegeben.Der nächste Schritt ist das setzen und überprüfen der I2C Adresse. Dies geschieht mit folgender Abfrage:

if(ioctl(File, I2C_SLAVE, RTC) < 0 )
{
	printf("No device found...\n");
	exit(1);
}

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 0x00 ist.

Nun ist die Verbindung zwischen Raspberry Pi und der DS1307 hergestellt und es kann damit begonnen werden Daten auszutauschen. Als allererstes wird die Startadresse der DS1307 auf 0x00 gesetzt:

Date[0] = 0x00;

if(write(File, Date, 1) != 1)
{
	printf("Write error!\n");
	return -1;
}

Zuerst wird die erste Stelle des Arrays Date auf 0x00 gesetzt und anschließend wird der Wert mittels der write Funktion über die geöffnete Schnittstelle in die DS1307 geschrieben.

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

if(read(File, Date, Buffer) != Buffer)
{
	printf("Read error!\n");
	return -1;
}

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 wird die Funktion BCD2D verwendet, welche am Anfang des Artikels vorgestellt wurde.

for(i = 0; i < Buffer; i++)
{
	Date[i] = BCD2D(Date[i]);
}

Die for-Schleife wird so lange durchlaufen, bis der Wert von Buffer 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:

switch(Date[3])
{
	case 1:
		printf("Sonntag");
		break;
	case 2:
		printf("Montag");
		break;
	case 3:
		printf("Dienstag");
		break;
	case 4:
		printf("Mittwoch");
		break;
	case 5:
		printf("Donnerstag");
		break;
	case 6:
		printf("Freitag");
		break;
	case 7:
		printf("Samstag");
		break;
}

So wird jedem Zahlenwert ein Wochentag zugeordnet. Bevor das Programm verwendet werden kann, muss die aktuelle Uhrzeit in die DS1307 geschrieben werden (z. B. mittels Konsole):

$ sudo i2cset -y 0 0x68 0x00

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

$ sudo i2cset -y 0 0x68 0x00
$ sudo i2cset -y 0 0x68 0x30
$ sudo i2cset -y 0 0x68 0x14
$ sudo i2cset -y 0 0x68 0x05
$ sudo i2cset -y 0 0x68 0x01
$ sudo i2cset -y 0 0x68 0x11
$ sudo i2cset -y 0 0x68 0x12

Nachdem das Programm mit dem Befehl

$ gcc DS1307.c -o DS1307

kompiliert und mittels

./DS1307

gestartet wurde sieht die Ausgabe so aus:

DS1307:Raspi

Die RTC als Uhr einbinden:

Da die RTC nun funktioniert, kann diese als Zeitgeber für den Raspberry Pi genutzt werden.
Dazu wird zuerst ein neues System-Device angelegt:

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

Anschließend kann mit dem Befehl

$ hwclock

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

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

Diese Zeit berücksichtigt aber noch nicht die aktuelle Zeitzone. Die Zeitzone kann über das Menü raspi-config oder mittels Konsolenbefehl geändert werden:

$ sudo dpkg-reconfigure tzdata

Anschließend erscheint folgendes Fenster:

Die Eingabe wird mittels Ok bestätigt und nach einem Reboot wird bei der Zeitausgabe über

$ hwclock

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 kann die Einbindung der DS1307 als Systemuhr über zwei Zeilen in der Datei /etc/rc.local automatisiert werden:

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

Das komplette C-Programm kann von meinem Repository heruntergeladen werden.

Zurück zum I2C

31 Kommentare

  1. „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

    1. 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

      1. 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 ;)

        1. 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

  2. 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?

    1. 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

      1. 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!

  3. 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

  4. 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

    1. Hey,

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

      Gruß
      Daniel

  5. 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

    1. 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

      1. 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.

          1. 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…

  6. 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

      1. 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

        1. 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

  7. 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.

    1. 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