Arduino NANO:    RTC-Echtzeituhr PCF8583
 

 
Schaltungsdiagramm für die Echtzeituhr PCF8583 am I2C-Bus des NANO-Boards
 
Echtzeituhren zählen das Datum und die Zeit unabhängig von dem Steuerrechner an dem sie angeschlossen sind. In der Regel besitzen sie eine eigene Spannungsversorgung, so dass sie auch dann arbeiten, wenn der Steuerrechner abgeschaltet ist. Der Steuerrechner kann die Zeit dann nach seinem Start abfragen. Im vorliegenden Fall werden zwei Programme zum Lesen und Beschreiben für eine Echtzeituhr auf der Basis des I2C-Bausteins PCF 8583 vorgestellt.
Datenblatt PCF 8583
1. Programm zum Lesen der Uhr
2.Programm zum Setzen von Zeit und Datum
Bemerkungen:
1. In Ermangelung einer Tastatur bzw. eines Display geben beide Programmteile ihre Daten zum seriellen Monitor der Entwicklungsumgebung aus.
 
2. Der Baustein PCF 8583 besitzt weitere Möglichkeiten, wie eine Alarm-Kontrolle. Auch kann er als Impulszähler verwendet werden. Hier wird nur seine Arbeit als Uhr im 24Std-Modus betrachtet. Ebenso wird er am Ende eines Jahres seine in freien RAM-Speichern abgelegte Jahresangabe nicht fortführen. Hierzu wären weitere Programmteile notwendig, die beim Datum 31.12.xxxx um 00:00:00 Uhr erkennen, dass ein Jahr abgelaufen ist, um dann auch die JahreskennNummer neu zu berechnen. Mit letzterer kann der Baustein festgestellen, ob das vorliegende Jahr ein Schaltjahr ist, der Februar dementsrechend 29 Tage besitzt. Bei den anderen Monaten erkennt der PCF 8583 deren Länge ohne Eingriff.
 

1. Programm zum Lesen der Echtzeituhr PCF8583
/*
Echtzeituhr PCF 8583 – Zeit und Datum lesen
===========================================
Das Programm lies im Abstand von 1 Sekunde den Tagesnamen, das Datum und die Zeit der Echtzeituhr und
gibt diese Werte zum seriellen Monitor der Entwicklungsumgebung aus.
Ohne Initialisierung des Control-Bytes arbeitet der Chip PCF 8583 als Uhr im 24std-Format.
Seine 8Bit I2C-Adresse 0xA0 wird bei einer Verkürzung auf 7Bit zu 0x50
   
*/
   
#include <Wire.h>                          // Einbinden der Wire-Bibliothek des Arduino
byte I2CadrRTC = 0xA0>>1;                  // Basisadresse der RTC PCF8583, da Pin A0=GND
   
void setup() {
Serial.begin (9600);                       // serielle Übertragungsrate 9600 Baud
Wire.begin ();                             // Initialisierung der Wire-Bibliothek
Wire.setClock (100000L);                   // I2C-Bus mit 100kHz
set_Control(0x00);                         // Control-Byte der Uhr setzen
}
   
void loop() {
   delay (1000);                           // nach jeder Sekunde
   read_Date();                            // .. das Datum und
   read_Clock();                           // .. die Zeit lesen
}
   
// den Tagesnamen und das Datum von der Uhr holen
// ----------------------------------------------
void read_Date() {
   byte tag, mon, jah, jal, tagNr, jaNr;
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x05);                      // interne Adresse 0x05 setzen
   Wire.endTransmission ();
   
   Wire.beginTransmission (I2CadrRTC);
   Wire.requestFrom (I2CadrRTC, 2);        // Master will 2 DatenBytes ab Adresse 0x05 lesen
   if (Wire.available ()) {                // .. wenn die Daten verfügbar sind
      tag = Wire.read ();                  // .. tag, mit Jahresnumme Bit 8-7
      mon = Wire.read ();                  // .. mon, mit Tagesnummer Bit 8-6
   }
   Wire.endTransmission();
   
   tagNr = mon >> 5;                       // Tagesnumme isoliert
   jaNr = tag >> 6;                        // Jahresnummer isoliert
   tag = tag & B00111111;                  // gepackter BCD-Wert Tag Bit 0-6
   mon = mon & B00011111;                  // gepackter BCD-Wert Mnat Bit 0-5
   
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x10);                      // interne Adresse 0x10 setzen
   Wire.endTransmission ();
   
   Wire.beginTransmission (I2CadrRTC);
   Wire.requestFrom (I2CadrRTC, 2);        // Master will 2 DatenBytes ab Adresse 0x10 lesen
   if (Wire.available ()) {                // .. wenn die Daten verfügbar sind
      jah = Wire.read ();                  // .. Jahr BCD-HighByte lesen
      jal = Wire.read ();                  // .. Jahr BCD-LowByte lesen
   }
   Wire.endTransmission();
   
   Serial.print ("Datum: ");               // Ausgabe zum seriellen Monitor
   Serial.print (tagName (tagNr));
   Serial.print (BCDtoASCII (tag));
   Serial.print (".");
   Serial.print (BCDtoASCII (mon));
   Serial.print (".");
   Serial.print (BCDtoASCII (jah));
   Serial.print (BCDtoASCII (jal));
}
   
// die TagesNummer in einen Tagesnamen wandeln
// -------------------------------------------
String tagName(byte tagNr) {
   String tag;
   if (tagNr == 0) tag = "So.";
   if (tagNr == 1) tag = "Mo.";
   if (tagNr == 2) tag = "Di.";
   if (tagNr == 3) tag = "Mi.";
   if (tagNr == 4) tag = "Do.";
   if (tagNr == 5) tag = "Fr.";
   if (tagNr == 6) tag = "Sa.";
   return tag;
}
   
// die Zeit Stunde,Minute und Sekunde von der Uhr holen
// ----------------------------------------------------
void read_Clock() {
   byte se, mi, st;
   
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x02);                      // interne Adresse 0x02 vom Slave setzen
   Wire.endTransmission ();
   
   Wire.beginTransmission (I2CadrRTC);
   Wire.requestFrom (I2CadrRTC, 3);        // Master will 3 DatenBytes ab 0x02 lesen
   if (Wire.available ()) {                // .. wenn die Daten verfügbar sind
      se = Wire.read ();                   // .. BCD Sekunde
      mi = Wire.read ();                   // .. BCD Minute
      st = Wire.read ();                   // .. BCD Stunde
   }
   Wire.endTransmission();
   
   Serial.print (" - Zeit : ");            // Ausgabe zum seriellen Monitor
   Serial.print(BCDtoASCII(st));
   Serial.print (":");
   Serial.print(BCDtoASCII(mi));
   Serial.print (":");
   Serial.println(BCDtoASCII(se));
}
   
// gepacktes BCD_byte in einen String mit zwei ASCII-Zeichen wandeln
// -----------------------------------------------------------------
String BCDtoASCII (byte BCD) {
   String zahl = "00";
   zahl[0] = (BCD >> 4)+0x30;
   zahl[1] = (BCD & 0x0F)+0x30;
   return zahl;
}
   
// setzt das Control-Register 0x00
// -------------------------------
void set_Control(byte set) {
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x00);                      // 0x00 Control-Register
   Wire.write (set);                       // .. zu setzender Wert
   Wire.endTransmission ();
}
   

 

2. Programm zum Setzen von Zeit und Datum bei der Echtzeituhr PCF8583
/*
Echtzeituhr PCF 8583 - Zeit einstellen
======================================
In Ermangelung einer Tastatur werden die gewünschte Zeit und das Datum in diesem Programm eingetragen,
welches diese Daten dann zur Echtzeituhr überträgt. Das Jahr wird 4stellig als gepackte BCD-Angabe in
den zwei freien RAM-Speichern 0x10 und 0x11 gespeichert. Die Jahreskennzahl 0-3 wird vom Programm
berechnet. Die Kennzahl des Tages tagNr= 0..6 muß angegeben werden.
 
Ohne Initialisierung des Control-Bytes arbeitet der Chip PCF 8583 als Uhr im 24std-Format.
Seine 8Bit I2C-Adresse 0xA0 wird bei einer Verkürzung auf 7Bit zu 0x50
*/
   
// Achtung: Die zu übertragenden Werte müssen hier wie gezeigt eingetragen werden. Es findet im Programm
//          keine Kontrolle der Gültigkeit statt.
// ------------------------------------------------------------------------------------------------------
String zeit = "02:29:07";                  // Die zu übertragende Startzeit der Uhr
                                           // .. die Werte müssen 2stellig sein ..
                                           // Da die Übertragung des Programms und dessen Start
                                           // etwas Zeit benötigen, sollte ein Vorlauf von etwa
                                           // 7 Sekunden eingeplant werden.
   
String datum = "22.04.2018";               // Die zu übertragenden Datumsangaben
                                           // .. die Werte müssen 2 bzw. 4stellig sein ..
byte tagNr = 0;                            // .. Nummer des Tages:
                                           // .. 0=So, 1=Mo, 2=Di, 3=Mi, 4=Do, 5=Fr, 6=Sa
// ------------------------------------------------------------------------------------------------------
   
   
#include <Wire.h>                          // Einbinden der Wire-Bibliothek des Arduino
byte I2CadrRTC = 0xA0>>1;                  // Basisadresse der RTC PCF8583, da Pin A0=GND
   
void setup() {
   byte jaNr;
   Serial.begin (9600);                    // serielle Übertragungsrate 9600 Baud
   Wire.begin ();                          // Initialisierung der Wire-Bibliothek
   Wire.setClock (100000L);                // I2C-Bus mit 100kHz
   set_Control(0x00);                      // Control-Register setzen
   set_Clock(zeit);                        // zu setzende Zeit st, mi, se
   jaNr = set_Date (tagNr, datum);         // zu setzende Tagesnummer und Datum
   
   Serial.println ("Zeit und Datum gesetzt");
   Serial.print ("Tagesnummer-Datum: ");
   Serial.print (tagNr);
   Serial.print (" - ");
   Serial.println (datum);
   Serial.print ("Jahresnummer-Zeit: ");
   Serial.print (jaNr);
   Serial.print (" - ");
   Serial.print (zeit);
}
   
// wird die Funktion loop() entfernt, erscheint die Meldung:
// Fehler beim Kompilieren für das Board Arduino Nano
// ---------------------------------------------------------
void loop() {
    // nix
}
   
   
// Datumspeicher 0x05-0x06 setzen
// sowie die 4stellige Jahreszahl in 0x10-0x11
// -------------------------------------------
byte set_Date (byte tagNr, String datum) {
   if (tagNr > 6) tagNr = 6;               // falsche Tagesnummer, da gößer 6
   byte tag,mon,jah,jal;                   // werden aus der Datumsangabe berechnet
   byte jaNr;                              // wird aus der Jahreszahl berechnet
   
   // Jahreskennummer 0 bis 3 berechnen .. 0=Schaltjahr
   jaNr = ((datum[6] & 0x0F) * 1000 + (datum[7] & 0x0F) * 100 + (datum[8] & 0x0F) * 10 + (datum[9] & 0x0F)) % 4;
   
   // Bit 7,6 Jahresnummer - Bit 5,4 BCD, 10stellen Tag - Bit 3-0 1stellen Tag
   tag = (((datum[0] & 0x0F) << 4) + (datum[1] & 0x0F)) | (jaNr << 6);
   
   // Bit 7,6,5 TagNummer - Bit 4 BCD, 10stellen Monat - Bit 3-0 1stellen Monat
   mon = (((datum[3] & 0x0F) << 4) + (datum[4] & 0x0F)) | (tagNr << 5);
   
   // Jahresangabe zu 2 gepackten BCD-Zahlen wandeln
   jah = ((datum[6] & 0x0F) << 4) + (datum[7] & 0x0F);
   jal = ((datum[8] & 0x0F) << 4) + (datum[9] & 0x0F);
   
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x05);                      // interne Adressen 0x05 und 0x06 beschreiben
   Wire.write (tag);                       // .. 0x05 JahresNummer und BCD-Werte für Tag
   Wire.write (mon);                       // .. 0x06 TagesNummer und BCD-Werte für Monat
   Wire.endTransmission ();
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x10);                      // interne Adressen 0x10 und 0x11 im freien RAM
   Wire.write (jah);                       // .. BCD-Werte für das Jahr
   Wire.write (jal);
   Wire.endTransmission ();
   return jaNr;
}
   
// Uhrspeicher 0x02-0x04 setzen
// nur 24-StundenFormat
// ----------------------------
void set_Clock(String zeit) {
   byte st,mi,se;                          // Speicher der gepackten BCD-Werte von st,mi,se
   st= ((zeit[0] & 0x0F) << 4) + (zeit[1] & 0x0F);   // je zwei ASCII zu gepacktem BCD wandeln
   mi= ((zeit[3] & 0x0F) << 4) + (zeit[4] & 0x0F);
   se= ((zeit[6] & 0x0F) << 4) + (zeit[7] & 0x0F);
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x02);                      // interne Adresse 0x02,0x03 und 0x04 beschreiben
   Wire.write (se);                        // .. BCD-Werte für Sekunden
   Wire.write (mi);                        // .. BCD-Werte für Minuten
   Wire.write (st);                        // .. BCD-Werte für Stunden
   Wire.endTransmission ();
}
   
// setzt das Control-Register 0x00
// -------------------------------
void set_Control(byte set) {
   Wire.beginTransmission (I2CadrRTC);
   Wire.write (0x00);                      // 0x00 Control-Register
   Wire.write (set);                       // .. zu setzender Wert
   Wire.endTransmission ();
}