Have you ever thought how your computer and smart gadgets displays time? This is done using an RTC (Real Time Clock). This tutorial explains DS3231 RTC interfacing with 8051 using I2C.
After the end of the tutorial, you will know how to write an embedded c code for DS3231 RTC using I2C protocol.
To show the date and time, I have connected both PC as well as LCD. In this article, I will explain how to display date and time on LCD using DS3231.
DS3231 RTC Hardware Connection
I have picked up 8051 microcontroller as master and DS3231 acting as a slave device.
Pull up resistors are connected to the SDA and SCL pins of RTC to the microcontroller.
It is recommended to use 10K ohms pull up resistance for the proper working of RTC.
The INT_RTC is left unconnected and it can be used to generate alarm interrupts for external or internal events.
To get started with Real Time Clock it is required to know the working of DS3231. Here are steps required to program the RTC.
Steps to Program DS3231
#1 Initialize the RTC by setting the Control Register
void Initialize_RTC (void) { Write_To__RTC(control_Register, 0x00); Write_To__RTC(Status_Register, 0x08); }
When all the bits in the control register are set to zero, the oscillator will run its clock until VBAT is applied.
But, it is not required for date and time. As temperature conversion is not required for displaying time and date, set the CONV=0.
Bit | Name | Setting |
---|---|---|
0 | A1IE | 0 |
1 | A2IE | 0 |
2 | INTCN | 0 |
3 | RS1 | 0 |
4 | RS2 | 0 |
5 | CONV | 0 |
6 | BBSQW | 0 |
7 | EOSC | 0 |
#2 Initialize the RTC by setting the Status Register
Status Register Byte:
This register monitors the status of time keeping registers such as date, month, year, minute, hour and second.
To enable this register set EN32kHz bit to ‘1’.
Bit | Name | Setting |
---|---|---|
0 | A1F | 0 |
1 | A2F | 0 |
2 | BSY | 0 |
3 | EN32kHz | 1 |
4 | 0 | 0 |
5 | 0 | 0 |
6 | 0 | 0 |
7 | OSF | 0 |
#3 Set the Date and Time using Time keeping registers
RTC Registers in DS3231
RTC Address | 0x68 |
Read Address | D1 |
Write Address | D0 |
Date | 0x04 |
Month | 0x05 |
Year | 0x06 |
Second | 0x00 |
Minute | 0x01 |
Hour | 0x02 |
Now, you can set the date and time parameters by using setTime and setDate functions.
The RTC has set with 0x60, 0x40 to enable hour format and AM/PM.
void setTime(unsigned char sethour, unsigned char setminute, unsigned char setsecond, short am_pm, short hour_twelve_twentyfour) { unsigned char temp = 0; Write_To__RTC(second, Convert_DECIMAL_BCD(setsecond)); Write_To__RTC(minute, Convert_DECIMAL_BCD(setminute)); switch(hour_twelve_twentyfour) { case 1: { switch(am_pm) { case 1: { temp = 0x60; break; } default: { temp = 0x40; break; } } Write_To__RTC(hour,((temp|(0x1F&(Convert_DECIMAL_BCD(sethour)))))); break; } default: { Write_To__RTC(hour, (0x3F & (Convert_DECIMAL_BCD(sethour)))); break; } } }
To set the day, date, month year use 04, 05, 06 address. The time and calendar registers are predefined in BCD format. So, we have to convert the Decimal format to BCD notation to write data to RTC.
void setDate(unsigned char setday, unsigned char setdate, unsigned char setmonth, unsigned char setyear) { Write_To__RTC(day, (Convert_DECIMAL_BCD(setday))); Write_To__RTC(date, (Convert_DECIMAL_BCD(setdate))); Write_To__RTC(month, (Convert_DECIMAL_BCD(setmonth))); Write_To__RTC(year, (Convert_DECIMAL_BCD(setyear))); }
After writing the data, we can read the timekeeping registers using following functions.
#4 Reading the date and Time
The calendar registers and timekeeping register in RTC gives BCD format. Hence, to read the RTC date and time, convert BCD format to decimal notation.
void Readtime(void) { unsigned char temp = 0,hour_twelve_twentyfour,t; RTCsec = Read_From_RTC(second); RTCsec = Convert_BCD_DECIMAL (RTCsec); RTCmin = Read_From_RTC (minute); RTCmin = Convert_BCD_DECIMAL (RTCmin); hour_twelve_twentyfour=0; //For 24 hour type switch (hour_twelve_twentyfour) { case 1: { temp = Read_From_RTC(hour); temp &= 0x20; t= (short)(temp>> 5); RTC_hour = (0x1F & Read_From_RTC(hour)); RTC_hour = Convert_BCD_DECIMAL(RTC_hour); break; } default: { RTC_hour = (0x3F & Read_From_RTC(hour)); RTC_hour = Convert_BCD_DECIMAL(RTC_hour); break; } } } void ReadDate(void) { RTCyear = Read_From_RTC(year); RTCyear = Convert_BCD_DECIMAL(RTCyear ); RTCmonth = (0x1F & Read_From_RTC(month)); RTCmonth = Convert_BCD_DECIMAL(RTCmonth ); RTCdate= (0x3F & Read_From_RTC(date)); RTCdate= Convert_BCD_DECIMAL(RTCdate); RTCday= (0x07 & Read_From_RTC(day)); RTCday= Convert_BCD_DECIMAL(RTCday); }
LCD Functions for RTC DS3231
You might struggle if you are working with LCD to display date and time. Because the time and date format of RTC is in integer format. The problem is the LCD displays only ASCII characters. Hence, I have written an integer to ASCII function for LCD.
Function to Convert an Integer to ASCII format
- Extract the integer.
- Add 0x30 to convert into ASCII.
- Store the ASCII character in the array.
- Divide the integer to until zero.
- Reverse the array to convert to original form.
void Show_Integer_Ascii(unsigned char lineno, unsigned char cursor_position, unsigned long value) { unsigned char a[11], b[11], i, m, n; if (value == 0 && cursor_position == 4) { goto loop1; } else if (value == 0) { b[0] = '0'; b[1] = '\0'; goto loop; } loop1: for (i = 0; i < 11; i++) { a[i] = (value % 10) + 0x30; value = value / 10; if (value == 0) break; } a[i + 1] = '\0'; n= 0; for (m = i; m >= 0; m--) { b[n] = a[m]; n++; if (m == 0) break; } b[n] = '\0'; loop: if (lineno == 1) Disp_Data(lineno, cursor_position, b); if (lineno == 2) Disp_Data(lineno, cursor_position, b); memset(b,0,sizeof(b)); }
Displaying Date on LCD
void Display_Date_LCD(void) { ReadDate(); Disp_Data(1, 7, "/"); if(RTCdate>=0 && RTCdate <=9) { Disp_Data (1, 5, "0"); Show_Integer_Ascii (1, 6, RTCdate); } else { Show_Integer_Ascii(1,5,RTCdate); } Disp_Data(1, 10, "/"); if(RTCmonth>=0 && RTCmonth <=9) { Disp_Data(1, 8, "0"); Show_Integer_Ascii(1,9,RTCmonth); } else { Show_Integer_Ascii(1,8,RTCmonth); } if(RTCyear>=0 && RTCyear <=9) { Disp_Data(1, 11, "0"); Show_Integer_Ascii(1,12,RTCyear); } else { Show_Integer_Ascii(1,11,RTCyear); } }
Displaying Time on LCD
void Readtime(void) { unsigned char temp = 0,hour_twelve_twentyfour,t; RTCsec = Read_From_RTC(second); RTCsec = Convert_BCD_DECIMAL(RTCsec); RTCmin = Read_From_RTC(minute); RTCmin = Convert_BCD_DECIMAL(RTCmin); hour_twelve_twentyfour=0; //For 24 hour type switch(hour_twelve_twentyfour) { case 1: { temp = Read_From_RTC(hour); temp &= 0x20; t= (short)(temp>> 5); RTC_hour = (0x1F & Read_From_RTC(hour)); RTC_hour = Convert_BCD_DECIMAL (RTC_hour); break; } default: { RTC_hour = (0x3F & Read_From_RTC(hour)); RTC_hour = Convert_BCD_DECIMAL (RTC_hour); break; } } }
C code for RTC DS3231
Here is the complete C code to read RTC date and time. I have used two pins of 8051 microcontroller to communicate with Real Time Clock using I2C.
#include<reg51.h> #include"header.h" void main() { lcd_init(); lcd_delay(2); Disp_Data(1,0, "DS3231 Interface"); Disp_Data(2,0, " By Codrey.com "); lcd_delay(10); Disp_Data(1,0, " "); Disp_Data(2,0, " "); Initialize_RTC( ); /*Initializing RTC DS3231*/ lcd_delay(2); setTime( 10,20,25,0,0); /*Set Hour,Minute,Second */ setDate( 2,16,05,18); /*Set Day,Date,Month,Year*/ /*Infinite WHILE Loop for reading date and time of RTC*/ Disp_Data(1,0, "DATE"); Disp_Data(2,0, "TIME"); ReadDate( ); //Gets the Day,month and year Display_Date_LCD( ); ReadTime(); /*Gets the time from DS3231*/ Display_Time_LCD( ); }
I2c Functions for RTC
Here are the steps to communicate with RTC using I2c.
- Start the I2C communication using start condition.
- Write the RTC address.
- Read the acknowledgement.
- Write the register write address.
- Read the data from the RTC.
- Read the acknowledegment
- Write no acknowledgement.
- Stop the i2c communication.
void Start_I2C(void) { SCL_DISABLE; SDA_ENABLE; SCL_ENABLE; SDA_DISABLE; } void Slave_Write(unsigned char byte) { unsigned char j,k; k = byte; for (j= 0; j < 8; j++) { SCL_DISABLE; SDA=(k&(0x80>>j))?1:0; /*Writing bit_by_bit*/ SCL_ENABLE; } } void Read_Ack(void) { SCL_DISABLE; SDA_ENABLE; SCL_ENABLE; while(SDA); } void Read_Ack(void) { SCL_DISABLE; SDA_ENABLE; SCL_ENABLE; while(SDA); } void No_Acknowledgement(void) { SCL_DISABLE; SDA_ENABLE; SCL_ENABLE; } unsigned char Read_I2C_Data(void) { unsigned char m,data_byte=0; for(m=0;m<8;m++) { SCL_DISABLE; SCL_ENABLE; if(SDA) data_byte|=0x80>>m; } return data_byte; } unsigned char Read_From_RTC(unsigned char Address) { unsigned char val= 0; Start_I2C(); Slave_Write (DS3231_Write); Read_Ack(); Slave_Write (Address); Read_Ack(); Start_I2C(); Slave_Write (DS3231_Read); Read_Ack(); val=Read_I2C_Data(); No_Acknowledgement( ); Stop_I2C(); return val; } void Write_To__RTC(unsigned char regaddress, unsigned char val) { unsigned char loop_time; Start_I2C(); Slave_Write(DS3231_Write); Read_Ack(); Slave_Write(regaddress); Read_Ack(); Slave_Write(val); Read_Ack(); Stop_I2C(); for(loop_time=0;loop_time<255;loop_time++); for(loop_time=0;loop_time<255;loop_time++); }
Proteus Simulation
Applications of DS3231
- Used in GPS for getting date, time, latitude, longitude, position, height
- Used in servers for implementing network timing protocol
- Used in Smart power meters for tracking the date and time of current bills.
- Used in telecommunication and networking applications.
Final Thoughts
RTC DS3231 is precise in maintaining accurate time with low drift. This makes it a perfect blend for timing applications.
DS3231 is highly accurate that manages date, time, year, month, day, and seconds.
Moreover, RTC is equipped with ±2ppm accuracy suitable for real time functions.
I hope you have understand how to interface DS3231 RTC with 8051 using I2C. As 8051 has no internal i2c, I have used bit banging method to generate clock and data for serial communication.
The entire code has been tested with 8051 microcontroller and it will work with any microcontroller with minor changes.