Der Zeit-Counter DS1307 ist hier mit einem Oszillator und ein paar Pullup-Widerständen auf einem Pinout-Modul
montiert. Der Counter zählt die Sekunden, Minuten, Stunden, Tage, Monate, Jahre und den Wochentag hoch. Die 7
Werte können aus 7 Speicherregister mit einer I2C Datenverbindung ausgelesen werden.
Unterschiedliche Monatslängen und Schaltjahre werden automatisch berücksichtigt. Im Pinout-Modul ist eine 3V
Batterie integriert, die die Stromversorgung für das Speichern und Weiterzählen übernimmt, sobald die 5 V
Spannungsversorgung abgeschaltet wird.
Uhrzeit auslesen
In der nachfolgenden Tabelle ist der Speicherbereich von 0x00 bis 0x06 abgebildet. Über I2C baut man zunächst
eine Verbindung zum RTC Slave mit der Adresse 0x68 auf. Anschließend schreibt man in das Slave Register auf welche
Speicheradresse der Pointer gesetzt werden soll. Nach dem setzten des Pointers wird die Verbindung beendet.
Im zweiten Schritt fordert man vom Slave 7 Byte Daten an. Beginnend vom vorher gesetzten Pointer. Nach dem Lesen
der Speicheradresse wird der internen Pointer automatisch immer eine Adresse höher gesetzt. Dadurch ist es möglich
direkt hintereinander 7 Byte auszulesen ohne jedes mal manuell die Speicheradresse anzugeben.
Wire.beginTransmission(0x68); // I2C-Kommunikation im Schreibmodus starten
Wire.write(0x00); // Start-Leseadresse setzen
Wire.endTransmission(); // I2C-Kommunikation beenden
Wire.requestFrom(0x68, 7); // Ausgehend von der Start-Leseadresse die nächste 7 Adressen auslesen
byte i = 0;
while (Wire.available()) {
zeit[i++] = Wire.read();
}
Adresse
Speicheraufbau
Info
0x00
BSZZZSSSS
Sekunden (0-59) SSSS: Zählt von 0-9 Sekunden (0000-1001) ZZZ: Zählt die 10'er Blöcke 0-5
(000-101) S: 0=Uhrzeit wird hochgezählt, 1= Uhr wird angehalten (dient zum Stellen der Uhrzeit)
0x01
B0ZZZMMMM
Minuten (0-59) MMMM: Zählt von 0-9 Minuten (0000-1001) ZZZ: Zählt die 10'er Blöcke 0-5 (000-101)
0x02
B0WAZSSSS
Stunden (0-23 bzw. 0-12) W:Auswahl ob von 0-24 (W=0) oder von 0-12 (W=1) gezählt wird A: wenn 0-12
Zählung (1=PM, 0=AM), bei 0-24 Zählung zusammen mit Z die Anzahl der 10'er Blöcke SSSS: Zählt die Stunden
von 0-9 (0000-1001)
0x03
B00000TTT
Wochentage (1-7) TTT: 0-7 (000-111)
0x04
B00ZZTTTT
Kalendertag (0 - max 31) ZZ: Zählt die 10'er Blöcke (00-11) TTTT: Zählt die Tage von 0-9 (0000-1001)
0x05
B000ZMMMM
Monate (0-12) Z: Zählt die 10'er Blöcke (0-1) MMMM: Zählt die Monate von 0-9 (0000-1001)
0x06
BZZZZJJJJ
Jahre (0-99) Z: Zählt die 10'er Blöcke (0000-1111) JJJJ: Zählt die Jahre von 0-9 (0000-1001)
Uhrzeit einstellen
Die Zeitangaben in den 7 Registern kann man nicht nur auslesen sondern auch neu beschrieben. Hier im Beispiel sind
alle 7 Werte in dem Array zeit[7] gespeichert. Als erstes bei der Übertragung wird wieder der Pointer auf die
Speicherzelle erwartet. Die nachfolgenden Bytes werden dann beginnen vom vorher gesetzten Pointer in den
Speicherzellen kopiert. Auch hier wird die Zieladresse nach jedem Schreibvorgang im FS1307 automatisch
inkrementiert.
Wire.beginTransmission(0x68);
Wire.write(0x00);
for (int i=0; i<8; i++) Wire.write(zeit[i]);
Wire.endTransmission();
Taktsignal ausgeben
Der DS1307 beinhaltet noch ein Pin (SQW/OUT) an welchem ein getaktetes Rechtecksignal (SQW=Square Wave) ausgegeben
werden kann. Einstellungen dazu werden im Register mit der Adresse 0x07 vorgenommen.
Wire.beginTransmission(0x68); // I2C-Kommunikation im Schreibmodus starten
Wire.write(0x07); // Speicheradresse übermitteln
Wire.write(B00010000); // Speicher 0x07 neu beschreiben
Wire.endTransmission(); // I2C-Kommunikation beenden
Der Chip verfügt über einen größeren Speicher als für die Uhrzeit und für die Einstellung des Rechtecksignals
benötigt wird. Der nicht belegt Speicher hat die Adressen 0x08- 0x3F. In diesen 56 Bereichen kann jeweils ein Byte
gespeichert werden. Auch hier wird der Pointer nach jedem Lesen oder Schreiben um einen Speicherzelle inkrementiert.
Achtung: Am Ende des Speichers (0x3F) wird auf den Anfang gesetzt (0x00).
Wire.beginTransmission(0x68); // I2C-Kommunikation im Schreibmodus starten
Wire.write(0x08); // Speicheradresse übermitteln
Wire.write(wert); // in Speicherzelle schreiben
Wire.endTransmission(); // I2C-Kommunikation beenden
Programmcode zum Einstellen und Ausgeben der Uhrzeit
#include "Wire.h"
//Array zum Speichern der Zeit (Sensorwerte)
byte zeit[7];
// umgerechnete Sensorwerte
byte sekunden = 5; // 0-59
byte minuten = 19; // 0-59
byte stunden = 18; // 0-24
byte wochentag = 4; // 1-7
byte kalendertag = 9; // 0-max31
byte monat = 12; // 0-12
byte jahr = 21; // 0-99
void setup() {
Wire.begin();
Serial.begin(9600);
/* Zum einstellen der Zeit werden die Werte in den globalen Variablen angegeben
und die Funktion ausgeführt. */
// zeitEinstellen();
}
void loop() {
zeitAnzeigen();
delay(1000);
}
Funktion: zeitAnzeigen()
void zeitAnzeigen() {
Wire.beginTransmission(0x68); // I2C-Kommunikation im Schreibmodus starten
Wire.write(0x00); // Start-Leseadresse setzen
Wire.endTransmission(); // I2C-Kommunikation beenden
Wire.requestFrom(0x68, 7); // Ausgehend von der Start-Leseadresse die nächste 7 Adressen auslesen
byte i = 0;
while (Wire.available()) {
zeit[i++] = Wire.read();
}
// Sekunden (Einzelschritte)
sekunden = zeit[0] & B01110000; // 10'er Sekundenblöcke freistellen
sekunden = sekunden >> 4; // 10'er Sekundenblöcke nach rechts schieben
sekunden = sekunden * 10; // Anzahl 10'er Sekundenblöcke mit 10 multiplizieren
sekunden = sekunden + (zeit[0] & B00001111); // Freigestellte Einzelsekunden hinzu addieren
// Minuten (Zusammengefasst)
minuten = ((zeit[1] & B01110000) >> 4) * 10 + (zeit[1] & B00001111);
// Stunden (nur 24h)
stunden = ((zeit[2] & B00110000) >> 4) * 10 + (zeit[2] & B00001111);
// Wochentage (1-7)
wochentag = zeit[3];
String wochentage[7]= {"Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag","Sonntag"};
String tag = wochentage[wochentag-1];
// Kalendertag
kalendertag = ((zeit[4] & B00110000) >> 4) * 10 + (zeit[4] & B00001111);
// Monat
monat = ((zeit[5] & B00010000) >> 4) * 10 + (zeit[5] & B00001111);
// Jahr
jahr = ((zeit[6] & B11110000) >> 4) * 10 + (zeit[6] & B00001111);
// Zeit anzeigen
Serial.print(tag); Serial.print(" den ");
Serial.print(kalendertag); Serial.print(".");
Serial.print(monat); Serial.print(".20");
Serial.print(jahr); Serial.print(" ");
Serial.print(stunden); if (minuten <10) Serial.print(":0"); else Serial.print(":");
Serial.print(minuten); if (sekunden <10) Serial.print(":0"); else Serial.print(":");
Serial.print(sekunden); Serial.println();
}
Funktion: zeitEinstellen()
void zeitEinstellen() {
/* Die in den globalen Variablen angegebenen Werte werden formatiert,
* so dass sie dem Speicherformat des IC's entsprechen. Am Ende werden
* die im Array zeit gespeicherten Werte an den IC übertragen.
*/
// Sekunden (immer auf 0 stellen)
zeit[0] = ((sekunden / 10) << 4) + sekunden % 10;
// Minuten
zeit[1] = ((minuten / 10) << 4) + minuten % 10;
// Stunden (24h Format)
zeit[2] = ((stunden / 10) << 4) + stunden % 10;
// Wochentag
zeit[3] = wochentag;
// Kalendertag
zeit[4] = ((kalendertag / 10) << 4) + kalendertag % 10;
// Monat
zeit[5] = ((monat / 10) << 4) + monat % 10;
// Jahr
zeit[6] = ((jahr / 10) << 4) + jahr % 10;
Wire.beginTransmission(0x68);
Wire.write(0x00);
for (int i=0; i<8; i++) Wire.write(zeit[i]);
Wire.endTransmission();
}