AdSense

Dienstag, 24. September 2013

Real Time Clock DS1307

(English Version) Manchmal benötigt man bei Arduino-Projekten die aktuelle Uhrzeit oder das aktuelle Datum - z.B. wenn man einen Datenlogger bauen möchte. Eine Möglichkeit wäre natürlich, Arduino-intern eine softwarebasierte Uhr zu programmieren. Das hat jedoch den Nachteil, dass es Rechenzeit verbraucht. Der viel größere Nachteil ist jedoch, dass die Uhrzeit verloren ist, wenn der Arduino von der Versorgungsspannung getrennt wird. Die Uhrzeit müsste also bei jeder Inbetriebnahme neu eingestellt werden. Eine Lösung bieten hier Real-Time-Clock-Module wie das "Tiny RTC". Es arbeitet mit einem DS1307-IC und hat eine Stützbatterie, läuft also auch dann weiter, wenn der Arduino nicht mehr unter Strom steht.

Der DS1307 wird über I2C angesprochen - der Anschluss des Moduls an den Arduino ist also denkbar einfach: einfach SDA und SCL mit den entsprechenden Pins am Arduino verbinden sowie die Betriebsspannung anlegen. Wichtig ist dabei, dass die Anschlüsse auf der rechten Seite (also der Seite mit den 7 Pins) verwendet werden. Die Pins auf der anderen Seite dienen zum Anschluss des EEPROM-Chips, der ebenfalls auf dem Modul verbaut ist.
Auf der rechten Seite gibt es noch einen Pin zum Zugriff auf die Batterie-Spannung (BAT) sowie den Pin SQ. Der IC kann ein Rechtecksignal erzeugen, das hier abgegriffen werden kann. Darauf werde ich aber nicht näher eingehen. Auf dem Modul kann außerdem ein Temperatursensor vom Typ DS18B20 aufgelötet werden. Auf den Sensor könnte mit dem letzten verbleibenden Pin DS zugegriffen werden. Aber auch hierauf werde ich in diesem Beitrag nicht weiter eingehen.

Der IC kann Datum und Uhrzeit (Jahr, Monat, Tag, Wochentag, Stunde, Minute, Sekunde) ausgeben. Die Werte werden in einem SRAM-Register gespeichert und können von dort über I2C ausgelesen werden. Das Register ist wie folgt aufgebaut:
In den Bits 0 bis 6 des Registers 0 sind die Sekunden gespeichert, das Bit 7 CH gibt an, ob die Uhr aktiv ist oder nicht. Wird das Bit auf 1 gesetzt, wird der Takt angehalten und die Uhrzeit wird nicht mehr weiter gezählt. Ist das Bit gelöscht, läuft der Takt und somit auch die Uhr weiter.
Die Bits 0 bis 6 des Registers 1 enthalten die Minuten.
Die Stunden werden im Register 2 gespeichert. Ist das Bit 6 des Registers gesetzt, werden die Stunden im 12h-Format angegeben, das Bit 5 gibt dabei AM(low)/PM(high) an. Ist Bit 6 gelöscht, können die Stunden (wie für Europäer gewohnt) im 24h-Format ausgelesen werden.
Register 3 enthält den aktuellen Wochentag - dabei entspricht 1 Sonntag, 2 Montag usw.
In den Registern 4 bis 6 werden Tag, Monat und Jahr gespeichert.
Register 7 dient zur Konfiguration des Rechtecksignal-Generators. Die restlichen Register können als batteriegestützer RAM verwendet und frei beschrieben werden.

Die Zahlen werden im BCD-Format (Binary Coded Decimal) ausgegeben. Dabei geben die Bits 0 bis 3 eines Bytes die Einer und die restlichen Bits die Zehner einer Zahl aus. Die Zahl 18 wäre somit also 0001'1000, die Zahl 34 entspräche 0011'0100 und so weiter.

Der DS1307 kann über die Adresse 0x68 im I2C-Bus angesprochen werden.

Bei der ersten Inbetriebnahme des Moduls muss die Uhrzeit eingestellt werden.
#include "Wire.h"

#define DS1307_ADDRESS 0x68

void setup(){
  Wire.begin();
    
  Serial.begin(9600);
  
  byte second =      0; //0-59
  byte minute =      44; //0-59
  byte hour =        22; //0-23
  byte weekDay =     1; //1-7
  byte monthDay =    22; //1-31
  byte month =       9; //1-12
  byte year  =       13; //0-99

  Wire.beginTransmission(DS1307_ADDRESS);
    
  Wire.write(0x00); //Pointer auf Register 0
  
  Wire.write(decToBcd(second));
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));
  Wire.write(decToBcd(weekDay));
  Wire.write(decToBcd(monthDay));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));

  Wire.endTransmission();
  
}

void loop(){
}

byte decToBcd(byte val){
//Dezimal in BCD konvertieren
  return ( (val/10*16) + (val%10) );
}

In den Variablen kann die aktuelle Uhrzeit bzw. das Datum eingestellt werden. Mit dem Befehl beginTransmission() wird dann die Übertragung zum Modul gestartet. Dzu wird der Pointer auf das Register 0 (das mit den Sekunden) gesetzt. Nun können Uhrzeit und Datum übertragen werden. Dazu werden die in den Variablen gespeicherten Werte zuerst vom Dezimal- ins BCD-Format konvertiert und dann mit write() ins Register des ICs geschrieben. Nach dem write()-Befehl springt der Arduino automatisch zum nächsten Register.

Ist der IC einmal richtig eingestellt, zählt er munter bis ans Ende aller Tage (bzw. bis ans Ende der Batterie) die Zeit hoch und kann jederzeit mit folgenden Befehlen ausgelesen werden:

#include "Wire.h"
#define DS1307_ADDRESS 0x68

void setup(){
  Wire.begin();
  Serial.begin(9600);
}

void loop(){
  
  //Pointer auf Register 0 setzen
  Wire.beginTransmission(DS1307_ADDRESS);
  Wire.write(0x00);
  Wire.endTransmission();

  //Zeit abfragen
  Wire.requestFrom(DS1307_ADDRESS, 7);

  int second = bcdToDec(Wire.read());
  int minute = bcdToDec(Wire.read());
  int hour = bcdToDec(Wire.read());
  int weekDay = bcdToDec(Wire.read());
  int monthDay = bcdToDec(Wire.read());
  int month = bcdToDec(Wire.read());
  int year = bcdToDec(Wire.read());

  //Datum ausgeben im Format dd.mm.yy - hh:mm:ss
  Serial.print(monthDay);
  Serial.print(".");
  Serial.print(month);
  Serial.print(".");
  Serial.print(year);
  Serial.print(" - ");
  Serial.print(hour);
  Serial.print(":");
  Serial.print(minute);
  Serial.print(":");
  Serial.println(second);
  delay(1000);
}

byte bcdToDec(byte val)  {
//BCD in Dezimal konvertieren
  return ( (val/16*10) + (val%16) );
}

In der loop() wird zuerst der Pointer auf das Register 0 gesetzt (wir wollen als erstes die Sekunden auslesen), dann werden mit dem Befehl requestFrom() aus diesem und den folgenden Registern 7 Bytes angefordert. Diese werden dann mit dem read()-Befehl empfangen, vom BCD- ins Dezimal-Format konvertiert und zum Schluss über die serielle Schnittstelle ausgegeben.

2 Kommentare: