Real-Time Clock
In this project, you'll learn how to connect and control the DS3231 real-time clock module with the Arduino.
The DS3231 IC is an extremely accurate real-time clock (RTC) module that keeps the date, time and alarm setting, and also has a built-in temperature sensor.
In addition to the DS3231 RTC chip, the module comes with an AT24C32 EEPROM chip for storing data permanently,
and backup battery to maintain the time even when the module is not connected to a power source.
Both the DS3231 and the AT24C32 use the I2C protocol. The I2C protocol uses two signals, SDA and SCL, for communication.
This protocol has been implemented in the wire.h library so that you do not need to know the details of how the I2C protocol works.
The datasheet for the DS3231 can be found here,
and the datasheet for the AT24C32 can be found here.
DS3231 RTC
- The 7-bit I2C device address for the DS3231 RTC is 1101000 (0x68). Therefore,
- the read address is 11010001 (0xD1), and the
- the write address is 11010000 (0xD0).
AT24C32 EEPROM
- The 7-bit I2C device address for the AT24C32 EEPROM is 1010111 (0x57). Therefore,
- the read address is 10101111 (0xAF), and the
- the write address is 10101110 (0xAE).
| Parts needed:
- Arduino
- Real-time clock module DS3231
- Wires
- Breadboard
|
|
1
| Making the connections
The DS3231 requires four connections: SDA, SCL, VCC and GND
- Connect SDA to analog pin A4
- Connect SCL to analog pin A5
- Connect the VCC to 5V.
- Connect the GND to GND.
|
2
| Copy the program on the right into your Arduino IDE
You need to set the real-time clock the first time you use this module. To set the clock:
- uncomment the call to the setDate() function in the setup() routine, and
- change the seven numbers in the parameter for the setDate() function to the correct time and date.
The seven numbers in order are:
1) seconds (0 to 59)
2) minute (0 to 59)
3) hour (24 hour clock from 0 to 23)
4) weekday (1=Sunday to 7=Saturday)
5) day (1 to 31)
6) month (1 to 12)
7) year (two digit)
The module has a battery backup so you do not need to set it again even if you disconnect power from it.
|
// Copyright May 2014 Enoch Hwang
// the I2C Wire.h library requires these two connections:
// SDA - A4
// SCL - A5
#include "Wire.h"
/*
* DS3231 I2C address is:
* 1101000 R/W'
* = 0x68
*
* The Wire.h library will append the R/W' bit.
* So we only specify the first 7 bits
*/
#define DS3231_I2C_ID 0x68 //I2C address for the DS3231 RTC chip
void setup() {
Serial.begin(9600);
Wire.begin();
//the parameters are: Second, Minute, Hour, WeekDay, Day, Month, Year
//the following sets: second=45, minute=59, hour=3, weekday=Wednesday, day=29, month=April, year=2017
setDate(45, 59, 3, 4, 29, 4, 17); // 24 hour format; Sun=1
}
void loop() {
readDate();
readTemperature();
delay(1000);
}
void setDate(byte Second, byte Minute, byte Hour, byte WeekDay, byte Day, byte Month, byte Year) {
Wire.beginTransmission(DS3231_I2C_ID);
Wire.write(0x0); // register address for the time and date
Wire.write(dec2bcd(Second));
Wire.write(dec2bcd(Minute));
// Hour must be in 24h format
bool h12 = 1; // set 12 hour mode
if(h12){ // 12 hour mode
if(Hour >= 12){
Hour = dec2bcd(Hour-12) | 0b01100000; // bit5(PM)=1; bit6(12 hour)=1
}else{
Hour = dec2bcd(Hour) & 0b11011111 | 0b01000000; // bit5(PM)=0; bit6(12 hour)=1
}
}else{ // 24 hour mode
Hour = dec2bcd(Hour) & 0b10111111;
}
Wire.write(Hour);
Wire.write(dec2bcd(WeekDay));
Wire.write(dec2bcd(Day));
Wire.write(dec2bcd(Month));
Wire.write(dec2bcd(Year));
Wire.endTransmission();
}
void readDate() {
Wire.beginTransmission(DS3231_I2C_ID);
Wire.write(0x0); // register address for the time and date
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ID, 7); // get 7 bytes
byte Second = bcd2dec(Wire.read() & 0b1111111);
byte Minute = bcd2dec(Wire.read() & 0b1111111);
byte Hour = Wire.read();
bool h12 = Hour & 0b01000000;
bool PM;
if (h12) { // 12 hour clock
PM = Hour & 0b00100000;
Hour = bcd2dec(Hour & 0b00011111);
} else { // 24 hour clock
Hour = bcd2dec(Hour & 0b00111111);
}
byte WeekDay = bcd2dec(Wire.read()); // 1=Sunday
byte Day = bcd2dec(Wire.read());
byte Month = bcd2dec(Wire.read());
byte Year = bcd2dec(Wire.read());
const char* days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
Serial.print(days[WeekDay-1]);
Serial.print(" ");
Serial.print(Month);
Serial.print("/");
Serial.print(Day);
Serial.print("/20");
Serial.print(Year);
Serial.print(" ");
Serial.print(Hour);
Serial.print(":");
Serial.print(Minute);
Serial.print(":");
if (Second < 10) Serial.print("0");
Serial.print(Second);
if (PM) Serial.print("P");
else Serial.print("A");
}
void readTemperature() {
Wire.beginTransmission(DS3231_I2C_ID);
Wire.write(0x11); // register address for the temperature
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ID, 2); // get 2 bytes
int MSB = Wire.read(); // 2's complement int portion
int LSB = Wire.read(); // fraction portion
float temperature = MSB & 0x7F; // do 2's moth on MSB
temperature = temperature + (LSB >> 6) * 0.25; // only care about bits 7 and 8
Serial.print(" ");
Serial.print(temperature);
Serial.println("C");
}
// Convert binary coded decimal (BCD) to nrmal decimal numbers
byte bcd2dec(byte val) {
return ((val/16*10) + (val%16));
}
// Convert normal decimal number to binary coded decimal
byte dec2bcd(byte val) {
return ((val/10*16) + (val%10));
}
|
3
| Upload and run the program to first set the clock.
|
4
| Comment out the call to the setDate() function in the setup() routine so that the clock will not be set to the same time each time the program runs.
|
5
| Upload and run the program.
|
6
| Open up the serial monitor and you should see the time and temperature displayed on the monitor.
|
7
| Here's the code to write and read from the AT24C32 EEPROM IC.
|
// Copyright May 2014 Enoch Hwang
// the I2C Wire.h library requires these two connections:
// SDA - A4
// SCL - A5
#include "Wire.h"
/*
* AT24C32 I2C address (on the DS3231 board) is:
* 1010111 R/W'
* = 0x57
* AT24C32 I2C address (on the DS1307 board) is:
* 1010000 R/W'
* = 0x50
* The last three bits of the address for the AT24C32 is A2,A1,A0
* which is hardware configured.
* On the DS3231 board, it is 111,
* and on the DS1307 board it is 000.
*
* The Wire.h library will append the R/W' bit.
* So we only specify the first 7 bits
*/
#define AT24C32_I2C_ID 0x57 //I2C address for the AT24C32 EEPROM chip on the DS3231 RTC board
//#define AT24C32_I2C_ID 0x50 //I2C address for the AT24C32 EEPROM chip on the DS1307 RTC board
void setup() {
Serial.begin(9600);
Wire.begin();
writeEEPROM(0,98); // Write 98 to EEPROM location 0
byte c = readEEPROM(0); // Read from location 0
Serial.print("Byte read from EEPROM location 0 is ");
Serial.println(c);
}
void loop() {
}
void writeEEPROM(int address, byte data) {
Wire.beginTransmission(AT24C32_I2C_ID);
// send memory address to write
// the memory address consists of 12 bits (2^12 = 4096 = 32K bytes)
Wire.write(address >> 8); // Address MSB = upper 4 bits
Wire.write(address & 0xff); // Address LSB = last 8 bits
// write data
Wire.write(data);
Wire.endTransmission();
delay(5); // need this delay after a write before a read
}
byte readEEPROM(int address) {
// send address to read
Wire.beginTransmission(AT24C32_I2C_ID);
Wire.write(address >> 8); // Address MSB = upper 4 bits
Wire.write(address & 0xff); // Address LSB = last 8 bits
Wire.endTransmission();
// request 1 byte to read
Wire.requestFrom(AT24C32_I2C_ID, 1);
return Wire.read();
}
|
|
|
|