Arduino Mikrocontroller Tutorials - Module

RTC - Real Time Clock (Bsp. DS1307)

 Bild RTC Modul DS1307

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
Adresse Speicheraufbau Info
0x07 BU00S00RR U=1 Leerlaufpegel des SQW Pins HIGH, U=0 Leerlaufpegel LOW
S=1 Taktsignal eingeschalten, S=0 Taktsignal ausgeschalten
RR: 00=1Hz, 01=4,096Hz, 10=8,192Hz, 11=32,768Hz

Frei verfügbarer Speicherbereich

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(); }