Digital Clock using PIC Microcontroller and the DS1307 Real Time Clock Circuit Diagram

A Digital Clock can be made easily by using PIC Microcontroller and the DS1307 real time clock with an LCD or seven segment display.
The DS1307 is a low power serial real time clock/calender from Maxim Integrated with full binary coded decimal (BCD) clock/calendar plus 56 bytes of Non Volatile Static Random Access Memory. 
Data and Address are transferred serially through a bidirectional I2C bus. The RTC provides year, month, date, hour, minute and second information. The end date of months is automatically adjusted for months fewer than 31 days including leap year compensation up to year 2100. It can operate either in 24-hour format or 12-hour format with AM/PM indicator. 
DS1307 comes with built-in power sensing circuit which senses power failures and automatically switches to back up supply. Timekeeping operation continues while the part operates from the backup supply. 
The DS1307 RTC uses an external 32.768kHz Crystal Oscillator and it does not requires any external resistors or capacitors to operate.

The PIC 18F2620 has a built-in I2C (Inter-Integrated Circuit) bus, any suitable PIC Microcontroller with I2C bus can be used as well for this project.
Although a digital clock can be built without an external real time clock chip, by using only internal PIC timer, The DS1307 RTC and simillar chips makes the software easier as it takes care of all calendar functions, adjustment of for months with fewer than 31 days, accounting for leap years and other functions which could have make the code more complex. The other advantage of using the DS1307, it comes with built-in power sensing circuit which senses power failures and automatically switches to back up supply so it can keep the real time information when the main circuit loses power. 
Before continuing, be sure to read through the interfacing the DS1307 real time clock with PIC microcontroller article.  

 Circuit diagram

As shown on the circuit diagram above, the DS1307 is connected to PORT C of the PIC which has built in I2C bus (pin 14 and 15 of the 18F2620). Connect two pull up resistors (R1 and R2) for the bus to work. 
Connect a 3V backup battery (B1) to pin VBAT (pin 3) of the DS1307 to backup the device data but if this is not required, this pin can be grounded.
On our circuit, we are using internal oscillator of the PIC and the MCLR is disabled. If an external oscillator is needed, it can be connected to pins 9 and 10 and if the MCLR is needed to reset the PIC, it can be connected to positive supply via a 10K resisitor.
Three push buttons connected to PORT C as well are used to set the date and time, once the SET button is pressed, the device enters the setup mode. Pressing the UP button will increment the the Hour and pressing the DOWN button will decrement.
Pressing the SET button again will move the cursor to Minutes, pressing again to seconds than to day and so on.
A 16 x 2 lines LCD display is connected to PORT B. refer to the Interfacing LCD Display with PIC microcontroller article to learn more.
The I2C debugger is shown on the circuit for simulation purpose only.

MPLAB XC8 Code

MPLAB XC8 compiler is used to write the code.
MPLAB XC8 provides built-in libraries for I2C devices which make it easy to read/write date and time information to/from the DS1307 chip. 
Registers containing the date and time information can be obtained by implementing a START and followed by device identification address. Then each registers can be accessed sequentially by using its address until a STOP condition is executed. 
Device Address of the DS1307 is 0x68 = 1101000 (page 12 of datasheet). 
More details can be found from the DS1307 Datasheet.
The PIC18 Peripheral Library help document found inside the installation folder of CX8 compiler contains the full description of the functions and macros to write or read from the I2C bus.

DS1307 RTC is fully Binary Coded Decimal (BCD) clock/calender. So the data read from DS1307 should be converted to required format according to our needs and data to be written to DS1307 should be in BCD format as well.
MPLAB CX8 Library functions for Interfacing LCD With PIC Microcontroller need Character or String Data. So data to be displayed in the LCD Screen should be converted to Character. 
Addition and Subtraction cannot be directly applied on BCD when updating the time and date. The functions to update data should convert it first from BCD to Binary and vice versa when needed.
The BCD2Binary() function convert BCD to Binary so we can do some basic calculations and the Binary2BCD() function convert Binary to BCD so we can write to the DS1307 Register in BCD.

int BCD2Binary(int a)             //Convert BCD to Binary so we can do some basic calculations
 {
     int r,t;
     t = a & 0x0F;
     r = t;
     a = 0xF0 & a;
     t = a >> 4;
     t = 0x0F & t;
     r = t*10 + r;
     return r;
 }
 
    int Binary2BCD(int a)             //Convert Binary to BCD so we can write to the DS1307 Register in BCD
 {
      int t1, t2;
      t1 = a%10;
      t1 = t1 & 0x0F;
      a = a/10;
      t2 = a%10;
      t2 = 0x0F & t2;
      t2 = t2 << 4;
      t2 = 0xF0 & t2;
      t1 = t1 | t2; return t1;
  }

Functions that are used for reading (read_ds1307()) and writing (write_ds1307()) data from DS1307 are explained in great detail in the article Interfacing the DS1307 with PIC Microcontroller please refer it. 

The define MSB() and the define LSB() are used to convert BCD to Character.

#define MSB(x) ((x>>4)+ '0')         //Display Most Significant Bit of BCD number
#define LSB(x) ((x & 0x0F) + '0')   //Display Least Significant Bit of BCD number

Hour, Minute, Second, Date, Month and Year data are stored in DS1307 in separate 8-bit registers in BCD format as shown in the DS1307 Datasheet on page 8. We read the data of these registers to access time/date. The define MSB() converts most significant 4 bits to corresponding character and the define LSB()  converts least significant 4 bits to corresponding character.  When writing to the hour register, Bit 6 is used to select the 24-hour or 12-hour mode selection bit. When this bit is made high (1), 12-hour mode is selected and Bit 5 will represent AM/PM (Logic High represents PM).

Full Code:

Important:

If you are using XC8 v1.35, the Peripheral Libraries which include the LCD and other peripherals like ADC, SPI, I2C libraries are no longer included in the installation file as with previous versions. You can either write your own LCD library and use MPLAB code configurator to configure the other peripherals or the easiest is to download the Peripheral Libraries as a separate download and install them,. You can download them at microchip website under the MPLAB XC Compilers downloads tab. They are now called PIC18 Legacy Peripheral Libraries.

Download: PIC18 Legacy Peripheral Libraries

//
/*
 * File:   RTC.c
 * Author: Student Companion SA: www.studentcompanion.co.za
 * Real Time Clock Using DS1307 on I2C Bus
 * To simulate in Proteus, The I2C debugger must be connected on the I2C Bus.
 */
 
#include "RTC.h"
#include <stdio.h>
#include <stdlib.h>
#include <delays.h>
#define MSB(x) ((x>>4)+ '0')                    //Display Most Significant Bit of BCD number
#define LSB(x) ((x & 0x0F) + '0')               //Display Least Significant Bit of BCD number
 
void DelayFor18TCY(void);          //Delay for 18 cycles.
void DelayPORXLCD(void);          //Delay for 15 ms.
void DelayXLCD(void);            //Delay for 5 ms.
void XLCD_init(void);            //Initialize the LCD display
unsigned short read_ds1307(unsigned short addressReg);     //Function to read from the DS1307
void write_ds1307(unsigned short addressReg,unsigned short w_dataReg);      //Function to write to the DS1307
int Binary2BCD(int aa);            //Convert Binary to BCD
int BCD2Binary(int aa);             //Convert BCD to Binary
 
 
//*** Global variables ****
char time[] = "00:00:00 PM";
char date[] = "00-00-00";
 
int second;
int minute;
int hour;
int hr;
int day;
int dday;
int month;
int year;
int ap;
 
unsigned short set_count = 0;
short set;
 
void main(void)
{
   OSCCON=0x76;                 //Configure internal oscillator
   XLCD_init();                  // Initialize LCD
   OpenI2C(MASTER, SLEW_OFF);// Initialize I2C module
    SSPADD = 45;             //100kHz Baud clock @8MHz = 19
                             //SSPADD = (FOSC/ Bit Rate) /4 - 1
    //check for bus idle condition in multi master communication
    IdleI2C();
    StartI2C();
    while ( SSPCON2bits.SEN );
    
    putrsXLCD("Time:");
    SetDDRamAddr(0x40);
    putrsXLCD("Date:");
    do{
        //Read Time and Date, when simulating with proteus, it will pick up the system Clock
        //from your PC and might display the AM if your PC time is in 24Hrs mode
        hour= read_ds1307(0x02);   //Read Hour
        hr = hour & 0b00011111;
        ap = hour & 0b00100000;
        minute= read_ds1307(0x01);   //Read minutes
        second= read_ds1307(0x00);   //Read seconds
        dday= read_ds1307(0x03);   //Read The day of the week (sunday to saturday)
        day= read_ds1307(0x04);   //Read The day of the week (1st to 31st)
        month= read_ds1307(0x05);   //Read month
        year= read_ds1307(0x06);   //Read year
 
        time[0]=MSB(hr);
        time[1]=LSB(hr);
        time[3]=MSB(minute);
        time[4]=LSB(minute);
        time[6]=MSB(second);
        time[7]=LSB(second);
        date[0]=MSB(day);
        date[1]=LSB(day);
        date[3]=MSB(month);
        date[4]=LSB(month);
        date[6]=MSB(year);
        date[7]=LSB(year);
        if(ap)
      {
         time[9] = 'P';
         time[10] = 'M';
      }
      else
      {
         time[9] = 'A';
         time[10] = 'M';
      }
        //Display Time and Date in 12Hrs Mode (with PM or AM)
        SetDDRamAddr(0x05);
        putrsXLCD(time);
        SetDDRamAddr(0x45);
        putrsXLCD(date);
 
        //Write Time and Date:
        //1. To set the time, press the SET pushbutton once to select the Hour.
        //2. Press the UP button to increament the Hour value or DOWN Button to decreament.
        //3. Press the SET Button again to move to the right to modify the minute.
        //4. Note when SET is in second position, pressing UP or Down will only reset the second value to 00
        //5. Continue doing the same to set the date as well.
        //6. Pressing the SET Button for the 7th time will exit the SET up mode
        set = 0;
     if(PORTCbits.RC0 == 0)
     {
         for(int c=0;c<=20;c++)__delay_ms(5); //wait for 100ms
         if(PORTCbits.RC0 == 0)
         {
             set_count++;
             if(set_count >= 7)
             {
                set_count = 0;
             }
         }
     }
        if(set_count)
     {
        if(PORTCbits.RC1 == 0)
        {
          for(int c=0;c<=20;c++)__delay_ms(5); //wait for 100ms
          if(PORTCbits.RC1 == 0)
              set = 1;
        }
 
        if(PORTCbits.RC2 == 0)
        {
         for(int c=0;c<=20;c++)__delay_ms(5); //wait for 100ms
          if(PORTCbits.RC2 == 0)
              set = -1;
        }
        if(set_count && set)
        {
          switch(set_count)
          {
            case 1:
                    hour = BCD2Binary(hour);
                    hour = hour + set;
                    hour = Binary2BCD(hour);
                    if((hour & 0x1F) >= 0x13)
                    {
                      hour = hour & 0b11100001;
                      hour = hour ^ 0x20;
                    }
                    else if((hour & 0x1F) <= 0x00)
                    {
                      hour = hour | 0b00010010;
                      hour = hour ^ 0x20;
                    }
                    write_ds1307(2, hour); //write hour
                    break;
            case 2:
                     minute = BCD2Binary(minute);
                     minute = minute + set;
                     if(minute >= 60)
                        minute = 0;
                     if(minute < 0)
                        minute = 59;
                     minute = Binary2BCD(minute);
                     write_ds1307(1, minute); //write min
                     break;
            case 3:
                    if(abs(set))
                      write_ds1307(0,0x00); //Reset second to 0 sec. and start Oscillator
                    break;
            case 4:
                     day = BCD2Binary(day);
                     day = day + set;
                     day = Binary2BCD(day);
                     if(day >= 0x32)
                        day = 1;
                     if(day <= 0)
                        day = 0x31;
                     write_ds1307(4, day); // write date 17
                     break;
            case 5:
                    month = BCD2Binary(month);
                    month = month + set;
                    month = Binary2BCD(month);
                    if(month > 0x12)
                      month = 1;
                    if(month <= 0)
                      month = 0x12;
                    write_ds1307(5,month); // write month 6 June
                    break;
            case 6:
                    year = BCD2Binary(year);
                    year = year + set;
                    year = Binary2BCD(year);
                    if(year <= -1)
                       year = 0x99;
                    if(year >= 0x50)
                       year = 0;
                    write_ds1307(6, year); // write year
                    break;
          }
        }
     }
 
 
    } while(1);
              
}
unsigned short read_ds1307(unsigned short address)  //call this function to read date and time
                                                    //from the date and time registers.
{
   char r_data;
  StartI2C();                  // Start condition I2C on bus
    IdleI2C();
     WriteI2C( 0xD0 );            // addresses the chip
         IdleI2C();
  WriteI2C( address );            // write register address
         IdleI2C();
    StopI2C();                   // Stop condition I2C on bus
 
     RestartI2C();                  // Start condition I2C on bus
         IdleI2C();
     WriteI2C( 0xD1 );            // addresses the chip with a read bit
         IdleI2C();
     r_data = ReadI2C();          // read the value from the RTC and store in result
         IdleI2C();
   NotAckI2C();                 // Not Acknowledge condition.
        IdleI2C();
     StopI2C();                   // Stop condition I2C on bus
     return (r_data);
  }
void write_ds1307(unsigned short address,unsigned short w_data) //call this function to write date and time
                                                    //to the date and time registers.
{
    StartI2C();                  // Start condition I2C on bus
    IdleI2C();
     WriteI2C( 0xD0 );            // addresses the chip
         IdleI2C();
  WriteI2C( address );            // write register address
  IdleI2C();
  WriteI2C( w_data );            // write register address
  IdleI2C();
   StopI2C();                   // Stop condition I2C on bus
 
  }
 int Binary2BCD(int a) //Convert Binary to BCD so we can write to the DS1307 Register in BCD
{
   int t1, t2;
   t1 = a%10;
   t1 = t1 & 0x0F;
   a = a/10;
   t2 = a%10;
   t2 = 0x0F & t2;
   t2 = t2 << 4;
   t2 = 0xF0 & t2;
   t1 = t1 | t2;
   return t1;
}
 
 
int BCD2Binary(int a) //Convert BCD to Binary so we can do some basic calculations
{
   int r,t;
   t = a & 0x0F;
   r = t;
   a = 0xF0 & a;
   t = a >> 4;
   t = 0x0F & t;
   r = t*10 + r;
   return r;
}
 
void XLCD_init(void){                //Initialize LCD display
    OpenXLCD(FOUR_BIT&LINES_5X7);
    while(BusyXLCD());
    WriteCmdXLCD(0x06); // move cursor right, don?t shift display
    WriteCmdXLCD(0x0C); //turn display on without cursor
    putrsXLCD("Real Time Clock");
    SetDDRamAddr(0x44);
    putrsXLCD("Starting");
     for(int c=0;c<=20;c++)__delay_ms(60);
    SetDDRamAddr(0x44);
    putrsXLCD("Starting.");
     for(int c=0;c<=20;c++)__delay_ms(50);
    SetDDRamAddr(0x44);
    putrsXLCD("Starting..");
     for(int c=0;c<=20;c++)__delay_ms(50);
    SetDDRamAddr(0x44);
    putrsXLCD("Starting...");
     for(int c=0;c<=20;c++)__delay_ms(50);
    WriteCmdXLCD(0x01); //Clear Screen
    WriteCmdXLCD(0x02); //Go Home
}
void DelayFor18TCY( void ){
Delay10TCYx(20);  //18 cycles delay
}
//*****************************************
void DelayPORXLCD (void){
Delay1KTCYx(15); // Delay of 15ms
return;
}//End DelayPORXLCD
//*****************************************
void DelayXLCD (void){
Delay1KTCYx(5); // Delay of 5ms
return;
}

You can download the full project files (MPLAB XC8 source code and Proteus Schematic design) below here. All the files are zipped, you will need to unzip them (Download a free version of the Winzip utility to unzip files). 

Download Digital Clock Header  

Download Digital Clock HEX  

Download Digital Clock Main C  

Download Digital Clock Proteus