RTC-Modul DS3231

Echtzeit-Modul DS3231 (RTC-Module)

Mit dem RTC-Timer Chip PCF8563 und dem RTC-Modul DS1307 (auch: hier) wurden schon zwei Möglichkeiten besprochen, Uhrzeit und Datum auszulesen und zu speichern. Das hier besprochene Modul DS3231 ist eine Alternative.

Features/Unterschiede

Der DS1307 wird mit einen externen 32kHz-Schwingquarz betrieben und ist durch äußerliche Temperaturänderungen teils erheblichen Schwankungen in seiner Genauigkeit unterlegen (mehrere Minuten pro Monat). Dagegen ist der DS3231 wesentlich präziser, denn er besitzt einen sogenannten TCXO. Dies ist ein temperaturkompensierter Quarz-Oszillator, der immer dann zum Einsatz kommt, wenn eine besonders hohe Temperaturstabilität benötigt wird. Damit wird die Ungenauigkeit des Moduls um höchstens wenige Minuten pro Jahr reduziert.
Es gibt auch noch den DS3232, der fast baugleich mit dem DS3231 ist, jedoch ein batteriegebuffertes SRAM mit 256 Byte besitzt.

Features laut Hersteller

Betriebsspannung: 3,3V bis 5,5V
RTC-Chip DS3231 (bzw. DS3232)
Genauigkeit: ±2ppm (0°C bis 40°C)
±3,5ppm (-40°C bis 85°C)
EEPROM: AT24C32 (32768 Bit)
Schnittstelle: I²C (<400kHz @5V)
Sonstiges:
  • Zwei separate, konfigurierbare Wecker/Alarme
  • Ausfallsicherheit durch Knopfbatterie (LIR2032)
  • I²C-Kaskadierung möglich (Addresse: 0x68)
  • Stromverbrauch bei 5V/VCC: 3,7mA
  • Stromverbrauch bei Batteriebetrieb: 1µA bis 3µA

Preisvergleich (Stand: 02.01.2023)

DS1307-Modul ca. 0,50 EUR
PCF8563-Modul ca. 0,80 EUR
PCF8563-Chip ca. 0,20 EUR
DS3231-Modul ca. 2,00 EUR

Anschlüsse

Schaltplan des DS3231-Moduls
Abb.: Schaltplan des DS3231-Moduls (Quelle: circuitdigest.com)
DS3231-Pin Bedeutung Arduino Uno/Nano
GND GND
VCC Spannungsversorgung des Moduls. 5V
SDA Data-Eingang/Ausgang A4
SCL Clock-Signal A5
SQW (auch: INT/SQW) "Square Wave"-Ausgang: Ausgabe eine genaues Rechtecksignals z.B.: D2
32K An diesem Pin liegt ein 32kHz-Signal an, welches man z.B. mit einem Oszilloskop prüfen kann. -

Batterie-/Akkubetrieb

Detailansicht des DS3231-Boards
Abb.: Detailansicht des DS3231-Boards

Das DS3231-Modul hat -wie viele andere RTC-Module- eine Ladefunktion für eine wiederaufladbare Knopfzelle (LIR2032), damit das Datum und die Uhrzeit auch bei Stromunterbrechung nicht verloren gehen. Wenn die Diode und der vorgeschaltete Widerstand (102 = 1kΩ) vorhanden sind (siehe obiges Foto), dann ist diese Ladeautomatik vorhanden.
Manchmal sind werkseitig allerdings CR2032-Knopfbatterien verbaut, die nicht zum Aufladen geeignet sind und sich u.U. aufblähen und zerstört werden können. Man sollte deshalb nur geeignete Akkus verwenden.
Darüber hinaus ist die Ladelogik so aufgebaut, dass mehr als 4,2V anliegen können (bis zu 4,8V), welches die Akkus schädigen kann.
Sicherheitshalber kann die Ladelogik durch das Entfernen der Diode und/oder des Vorwiderstandes ausgeschaltet werden.

Verwendete Bauteile

EEPROM verwenden

Auf dem Board ist auch der EEPROM AT24CS32 verbaut (obiges Foto unten links), der mit 32 kBit einiges an Daten speichern kann, z.B. eigene Messwerte von Sensoren und dazugehöriges Datum/Uhrzeit.
Auf den AT24CS32 kann per I²C zugegriffen werden und besitzt eine konfigurierbare Adresse, einstellbar über die Jumper A0, A1 und A2. Je nach Kombination, welche Jumper geschlossen sind, können die Adressen von 0x50 bis 0x56 eingestellt werden. Werksseitig sind alle Jumper offen und damit ist die I²C-Addresse´0x57.
Im folgenden Sketch verwenden wir einen ähnlichen Code wie in dem Beitrag EEPROM 24C32A und Arduino:

#include <Wire.h>

#define EEPROM_ADDRESS 0x57

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

   uint16_t memAddr = 2;
   uint8_t  memData = 22;

   printEepromData(memAddr);

   writeEeprom(memAddr, memData);
   delay(5);

   printEepromData(memAddr);
}

void loop() {}

void printEepromData(uint16_t memAddr)
{
   Serial.print(memAddr);
   Serial.print(": ");
   Serial.println(readEeprom(memAddr));
}

void writeEeprom(uint16_t memReg, uint8_t memData)
{
   Wire.beginTransmission(EEPROM_ADDRESS);
   // need to send the MSB(Most significant bits) first, so shift address to the right eight bits.
   Wire.write(memReg >> 8);
   Wire.write(memReg & 0xFF);
   Wire.write(memData);
   Wire.endTransmission();
}

uint8_t readEeprom(uint16_t memReg)
{
   Wire.beginTransmission(EEPROM_ADDRESS);
   Wire.write(memReg >> 8);
   Wire.write(memReg & 0xFF);
   Wire.endTransmission();

   delay(5);

   Wire.requestFrom(EEPROM_ADDRESS, 1);
   uint8_t inData = 0;
   while (Wire.available()) {
      inData = Wire.read();
   }

   return inData;
}

Man sieht in der folgenden Grafik, dass in der ersten Zeile der Ursprungswert des EEPROMs der Speicheraddresse 2 den Wert 48 hat und dann nach dem Beschreiben auf den Wert 22 geändert wurde, so wie im obigen Sketch beschrieben.

Ausgabe auf der seriellen Konsole der Arduino-IDE

Taktsignal ausgeben

Der DS3231 kann an seinem Pin SQW ein recht genaues Taktsignal ausgeben, wobei man die Frequenzen 1Hz, 1024Hz, 4096Hz oder 8192Hz einstellen kann. Dies geschieht über das "Control Register". (siehe Datenblatt)
Zusätzlich steht am Pin 32K noch ein stabiles Signal mit der Frequenz von 32768Hz zur Verfügung.
Der folgende Sketch stellt das Taktsignal auf 1Hz:

#include "Wire.h"

#define I2C_ADDR 0x68

#define SQW_REG_1HZ    B01100011
#define SQW_REG_1024HZ B01101011
#define SQW_REG_4096HZ B01110011
#define SQW_REG_8192HZ B01111011

void setup() {
   Wire.begin();
   setSignal(SQW_REG_1HZ);
}

void loop() {}

void setSignal(int reg) {
   Wire.beginTransmission(I2C_ADDR);
   Wire.write(0x0e);
   Wire.write(reg);
   Wire.endTransmission();
}

Nach dem Hochladen sollte an Pin SQW ein Taktsignal von 1Hz anliegen. Dies kann man z.B. mit dem Oszilloskop oder mit einer LED (blinkt) überprüfen.

Datum und Uhrzeit auslesen

#include "Wire.h"

#define I2C_ADDR 0x68

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

void loop() {
   printDateTime();
   delay(1000);
}

// Prints the current date/time set in the RTC module to the serial monitor
void printDateTime() {
   Wire.beginTransmission(I2C_ADDR);
   Wire.write(0x00);
   Wire.endTransmission();
   Wire.requestFrom(I2C_ADDR, 7);

   byte nowSeconds = bcdToDec(Wire.read());
   byte nowMinute = bcdToDec(Wire.read());
   byte nowHour = bcdToDec(Wire.read() & 0b111111);
   byte nowWeekDay = bcdToDec(Wire.read());
   byte nowMonthDay = bcdToDec(Wire.read());
   byte nowMonth = bcdToDec(Wire.read());
   byte nowYear = bcdToDec(Wire.read());

   char data[20] = "";
   sprintf(data, "20%02d-%02d-%02d %02d:%02d:%02d", nowYear, nowMonth, nowMonthDay, nowHour, nowMinute, nowSeconds);
   Serial.print("Current datetime: ");
   Serial.println(data);
}

// Converts a decimal (Base-10) integer to BCD (Binary-coded decimal)
int decToBcd(int value) {
   return ((value / 10 * 16) + (value % 10));
}

// Converts a BCD (Binary-coded decimal) to decimal (Base-10) integer
int bcdToDec(int value) {
   return ((value / 16 * 10) + (value % 16));
}

Man sieht im folgenden Bild, dass zwar Datum und Uhrzeit auf der seriellen Konsole der Arduino-IDE ausgegeben werden, jedoch quasi bei 0 begonnen wird. Das liegt daran, dass die Uhrzeit immer wieder von vorne beginnt zu zählen, wenn das Modul vom Strom getrennt wurde (daher der optionale Batterie-Betrieb).

Ausgabe auf der seriellen Konsole

Datum und Uhrzeit einstellen

Hier wird wiederum der Sketch zum Einstellen von Datum und Uhrzeit verwendet, wie vom DS1307 verwendet.

Alarm stellen

Dieses Experiment soll einen Alarm des DS3231 stellen und bei dessen Auslösung den Wert eines Sensors (hier: DHT22) auslesen und auf die serielle Konsole ausgeben. Hier wir die Library DS1337/DS3231 RTC und Adafruit DHT-Sensor verwendet.
Wichtig: Der Pin SQW muss mit einem Interrupt-fähigen Pin (hier: INT0 = D2) verbunden sein.

#include <DS3231.h>
#include <Wire.h>
#include <DHT.h>

#define DHTTYPE DHT22
#define PIN_DHT22 3

DS3231 rtc;
DHT dht(PIN_DHT22, DHTTYPE);
volatile boolean alarm = false;

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

   dht.begin();

   rtc.init();
   rtc.clearFlags();
   rtc.setTickMode(DS1337_NO_TICKS);

   // set date to "2023-01-02"
   rtc.setDate(23, 1, 2);

   // set time to "13:51"
   rtc.setTime(13, 51);

   // set alarm for every minute
   rtc.clearAlarm();
   rtc.setAlarm(13, 52, 0);
   rtc.setAlarmMode(DS1337_ALARM_ON_SECOND);
   rtc.enableAlarm();

   pinMode(2, INPUT);
   attachInterrupt(0, onAlarm, FALLING);
}

void loop() {
   if (alarm == true) {
      Serial.print(rtc.getDate().getDateString());
      Serial.print(" ");
      Serial.print(rtc.getDate().getTimeString());
      Serial.print(" - ");
      Serial.print("Humidity: ");
      Serial.print(dht.readHumidity());
      Serial.print("%, Temperature: ");
      Serial.print(dht.readTemperature());
      Serial.println("°C");
      rtc.clearAlarm();
      alarm = false;
   }
}

void onAlarm() {
   noInterrupts();
   alarm = true;
   interrupts();
}

Nach dem Hochladen wird nun jede Minute ein Alarm durch den DS3231 ausgelöst, welcher im Sketch eine Messung mit dem DHT22 durchführt. Somit können Messungen jede Minute, aber auch in anderen Taktungen (je nach Alarm) ausgelöst werden.

Ausgabe auf der seriellen Konsole

Strom-Verbrauch

Detailansicht des DS3231-Boards

Sobald das DS3231-Modul mit Spannung versorgt wird, leuchtet auch eine rote Status-LED permanent auf. Dies kann in manchen Anwendungen störend wirken oder einfach auch zuviel Strom verbrauchen:
3,65 mA (mit LED) ↔ 0,63 mA (ohne LED)
Daher kann in diesen Fällen die LED und/oder der Vorwiderstand (wie im obigen Foto gezeigt) entfernt werden, ohne die Funktionalität des Moduls zu beeinträchtigen.

zurück