Interfacing RTC DS3231 with 8051 Microcontroller

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.

Interfacing RTC DS3231 with 8051 Microcontroller using I2c
Connection Diagram

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.

BitNameSetting
0A1IE0
1A2IE0
2INTCN0
3RS10
4RS20
5CONV0
6BBSQW0
7EOSC0

#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’.

BitNameSetting
0A1F0
1A2F0
2BSY0
3EN32kHz1
400
500
600
7OSF0

#3 Set the Date and Time using Time keeping registers

 RTC Registers in DS3231

RTC Address0x68
Read AddressD1
Write AddressD0
Date 0x04
Month0x05
Year0x06
Second0x00
Minute0x01
Hour0x02

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

  1. Extract the integer.
  2. Add 0x30 to convert into ASCII.
  3. Store the ASCII character in the array.
  4. Divide the integer to until zero.
  5. 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.

  1. Start the I2C communication using start condition.
  2. Write the RTC address.
  3. Read the acknowledgement.
  4. Write the register write address.
  5. Read the data from the RTC.
  6. Read the acknowledegment
  7. Write no acknowledgement.
  8. 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

Interfacing RTC DS3231 with 8051 Microcontroller - Schematic

Applications of DS3231

  1. Used in GPS for getting date, time, latitude, longitude, position, height
  2. Used in servers for implementing network timing protocol
  3. Used in Smart power meters for tracking the date and time of current bills.
  4. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *