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.
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.
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: |
|
DS1307-Modul | ca. 0,50 EUR |
---|---|
PCF8563-Modul | ca. 0,80 EUR |
PCF8563-Chip | ca. 0,20 EUR |
DS3231-Modul | ca. 2,00 EUR |
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. | - |
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.
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.
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.
#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).
Hier wird wiederum der Sketch zum Einstellen von Datum und Uhrzeit verwendet, wie vom DS1307 verwendet.
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.
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.