Real Time Clocks (RTCs) are used in many applications where it is necessary to keep track of time and date information.

Real Time Clocks (RTCs) are used in many applications where it is necessary to keep track of time and date information. The common solution of adding a dedicated RTC device to a board adds to the BOM cost and also increases the board space. A better, cost-effective alternative is to implement the RTC functionality into a microcontroller PIC18F4550 that performs other useful tasks as well. The Design is a complete I2C and RTC solution that includes all the firmware and hardware necessary to implement an RTC with full clock and calendar functionality along with interfacing microcontroller PIC18F4550.


RTC Registers and Non-volatile User RAM

The addresses of the RTC registers and the NVRAM. The RTC registers store the current time and date information. These registers are maintained in the ‘F300 device internal volatile RAM. The RTC also provides a 56-byte User NVRAM that is stored in the ‘F300 device internal Flash memory. This can be used as a general-purpose non-volatile storage area. The current values of the Flash NVRAM are always mirrored in a 56-byte array in RAM. This is because the ‘F300 Flash memory can be erased only in 512-byte.

So, writing one or more bytes to the NVRAM is a three-step process—the bytes are first written to the RAM array, the Flash page is erased, and the 56-byte array is copied from RAM to Flash memory One useful application of this NVRAM is to store the time and date information just before a power failure.

The stored information can later be used for debugging or some other purpose. To do this, the power supply line should be monitored and as soon as a dip is detected, the time and date information should be read from the RTC Registers and written to the NVRAM. This should be managed by an external device and is not automatically done by the RTC firmware.

I2C Physical Protocol

When the master (your controller) wishes to talk to a slave (our CMPS03 for example) it begins by issuing a start sequence on the I2C bus. A start sequence is one of two special sequences defined for the I2C bus, the other being the stop sequence. The start sequence and stop sequence are special in that these are the only places where the SDA (data line) is allowed to change while the SCL (clock line) is high. When data is being transferred, SDA must remain stable and not change whilst SCL is high. The start and stop sequences mark the beginning and end of a transaction with the slave device.

Data is transferred in sequences of 8 bits. The bits are placed on the SDA line starting with the MSB (Most Significant Bit). The SCL line is then pulsed high, then low. Remember that the chip cannot really drive the line high, it simply "let’s go" of it and the resistor actually pulls it high. For every 8 bits transferred, the device receiving the data sends back an acknowledge bit, so there are actually 9 SCL clock pulses to transfer each 8 bit byte of data. If the receiving device sends back a low ACK bit, then it has received the data and is ready to accept another byte. If it sends back a high then it is indicating it cannot accept any further data and the master should terminate the transfer by sending a stop sequence.

I2C Device Addressing

All I2C addresses are either 7 bits or 10 bits. The use of 10 bit addresses is rare and is not covered here. All of our modules and the common chips you will use will have 7 bit addresses. This means that you can have up to 128 devices on the I2C bus, since a 7bit number can be from 0 to 127. When sending out the 7 bit address, we still always send 8 bits. The extra bit is used to inform the slave if the master is writing to it or reading from it. If the bit is zero the master is writing to the slave. If the bit is 1 the master is reading from the slave. The 7 bit address is placed in the upper 7 bits of the byte and the Read/Write (R/W) bit is in the LSB (Least Significant Bit).

The placement of the 7 bit address in the upper 7 bits of the byte is a source of confusion for the newcomer. It means that to write to address 21, you must actually send out 42 which is 21 moved over by 1 bit. It is probably easier to think of the I2C bus addresses as 8 bit addresses, with even addresses as write only, and the odd addresses as the read address for the same device. To take our CMPS03 for example, this is at address 0xC0. You would uses 0xC0 to write to the CMPS03 and 0xC1 to read from it. So the read/write bit just makes it an odd/even address.

Procedure for writing time and date

• Initiate a START condition.

• Transmit the RTC address 0xD0 with LSB 0 for write mode.

• Transmit the address of the register to write(for example 0x00 for write to second register). This transfer sets the register pointer inside the DS1307 RTC

• Transmit the seconds value in BCD format. Following the procedure of writing to the seconds register, the pointer in the RTC will automatically increment. The next data will be written in the 0x01 location which is minute register.

• Transmit until year register is written.

• Terminate the communication with a STOP condition.

DS1307 write sequence

Procedure for reading the time and date

• Initiate a START condition

• Transmit RTC address with LSB 1 (0xD1) to access the RTC in read mode.

• Now RTC will transmit data from its registers. It should be taken care that it will output data in the register whose address is currently stored in the pointer. If user needs to read a particular register, first the RTC should be accessed in write mode and the desired register address should be written to pointer. Now again RTC should be addressed in read mode after a RESTART or a STOP followed by a START.

• Each receiving byte should be acknowledged by the master to receive the next byte.

• After receiving the last byte master should send not-acknowledge (NACK) to the RTC.

• Terminate communication with a STOP condition in the bus.

I2C protocol allows multiple slave devices and master devices communicate with each other and it is used for short distance communications. It uses two signal wires to communicate with each other, SDA or data line and SCL or clock line.

SDA and SCL lines are connected to the corresponding pins in the microcontroller. I2C bus drivers are open drain so that they can’t drive corresponding signals high. This avoids bus contention when more than two devices communicate with each other and each device is trying different logic level (e.g: one device pulling high and other pulling low), thus avoiding chance to damage drivers. The signal lines have pull-up resistors to restore the signal state high when no device is using the line. DS1307 uses a 32.768 kHz quartz crystal as the oscillator. The pins X1 and X2 are used as the oscillator pins. There is a VBAT pin for connecting the battery. The battery will provide backup in the absence of power. A CR2032 battery will provide around 7 years power backup.


• Smart Energy

• Utility metering

• Ethernet


Proteus design for RTC interfacing with PIC

Orcad design for RTC interfacing with PIC

RTC interfacing with PIC

/*  Name     : main.c
 *  Purpose  : Source code for RTC Interfacing with PIC18F4550.
 *  Author   : Gemicates
 *  Date     : 2017-06-14
 *  Website  : www.gemicates.org
 *  Revision : None

#include <xc.h>                      // Header file for PIC18F4550
#define _XTAL_FREQ  12000000         // 12MHZ

/****Configuration bits****/
#pragma config PLLDIV   = 5          // PLL Prescaler Selection bits(20 MHz crystal on PICDEM FS USB board)
#pragma config CPUDIV   = OSC1_PLL2  // System Clock Postscaler Selection bits 
#pragma config USBDIV   = 2          // USB Clock Selection bit,Clock source from 96MHz PLL/2
#pragma config FOSC     = HSPLL_HS   // Oscillator Frequency Range
#pragma config FCMEN    = OFF        // Fail-Safe Clock Monitor Enable bit  
#pragma config IESO     = OFF        // Oscillator Switchover mode disabled
#pragma config PWRT     = OFF        // PWRT enabled
#pragma config BOR      = ON         // Brown out reset
#pragma config BORV     = 3          // Brown-out Reset Voltage bits
#pragma config VREGEN   = ON         // USB Voltage Regulator
#pragma config WDT      = OFF        // Watchdog Timer Enable bit(WDT disabled)
//#pragma config WDTPS    = 32768
#pragma config MCLRE    = ON         // MCLR Pin Enable bit(MCLR pin enabled)
#pragma config LPT1OSC  = OFF        // Low-Power Timer1 Oscillator Enable bit(Timer1 configured for higher power operation)
#pragma config PBADEN   = OFF        // PORTB A/D Enable bit( pins are configured as digital I/O on Reset)
#pragma config STVREN   = ON         // Stack Full/Underflow Reset Enable bit(Stack full/underflow will cause Reset)
#pragma config LVP      = OFF        // Single-Supply ICSP disabled
#pragma config XINST    = OFF        // Extended Instruction Set

/**********LCD Pin Configuration**********/
#define LCD_DATA_PINS PORTD          // To assign LCD data pins PORTD
#define RS PORTCbits.RC1             // To assign single pin(RC1) as output
#define RW PORTCbits.RC0             // To assign single pin(RC0) as output
#define EN PORTCbits.RC2             // To assign single pin(RC2) as output

/**********Function Prototypes**********/
void LCD_Init(void);
void LCD_Cmd(unsigned char);
void LCD_Data(unsigned char value);
void LCD_Str(const unsigned char *);
void USART_Init(void);
void USART_Write(unsigned char);
void USART_Write_Str(const unsigned char *);
void USART_Write_Str_Line(const unsigned char *);
void USART_Write_Int(int ,unsigned char);
unsigned char USARTReadByte();
void I2C_Init(void);
void I2C_Start(void);
void I2C_Restart(void);
void I2C_Stop(void);
void I2C_Wait(void);
void I2C_Send(unsigned char dat);
unsigned char I2C_Read(void);
unsigned char rtc1307_read(unsigned char address);
unsigned char BCD2UpperCh(unsigned char bcd);
unsigned char BCD2LowerCh(unsigned char bcd);

unsigned char sec,min,hour,date,month,year;
const unsigned char time[] = "TIME:";
const unsigned char date_format[] = "DATE:";
const unsigned char rtc[] = "RTC DS1307";

void main()
	TRISC = 0x80;	                      //Reception Pin as Input Pin, rest all as output Pin
	TRISD = 0x00;	                      //PORTD as output
	LCD_Init();                           //Initialize LCD Module	
	USART_Init();	                      //Initialize USART at 9600 BPS
	LCD_Cmd(0x84);                        //first line fourth character
	LCD_Str(rtc);                         //display rtc
	I2C_Init();	                      //Initialize I2C
	I2C_Start();                          //Start the I2C protocol
	I2C_Send(0xD0);                       //7-bit DS1307 address
	I2C_Send(0x80);	                      //CH = 1 Stop oscillator
	I2C_Send(0x00);	                      //Minute
	I2C_Send(0x06);                       //Hour
	I2C_Send(0x02);	                      //Sunday
	I2C_Send(0x28);	                      // 28 APRIL
	I2C_Send(0x04);                       //  4 April
	I2C_Send(0x95);	                      // 1995
	I2C_Stop();                           //Stop the I2C Protocol
	I2C_Start();                          //Have to start the Clock again
	I2C_Send(0xD0);                       //7-bit DS1307 address
	I2C_Send(0x00);                       //7 bit address start
	I2C_Send(0x00);	                      //start Clock and set the second hand to Zero
	LCD_Cmd(0x01);                        //Clear display screen
	while(1)                              //Infinite Loop For Reading Time and Date
		sec = rtc1307_read(0x00);     //Seconds address
		min = rtc1307_read(0x01);     //min address
		hour = rtc1307_read(0x02);    //hour address
		date = rtc1307_read(0x04);    //date address
		month = rtc1307_read(0x05);   //month address
		year = rtc1307_read(0x06);    //year address
		__delay_ms(1);                //delay of 1ms
		LCD_Cmd(0x80);                //Cursor to beginning of 1st row         
		LCD_Str(time);                //To display the time
		LCD_Data(BCD2UpperCh(hour));  //BCD format to Upper Clock halt bit(hour)
		LCD_Data(BCD2LowerCh(hour));  //BCD format to Lower Clock halt bit(hour)
		LCD_Data(':');                //To display ':'
		LCD_Data(BCD2UpperCh(min));   //BCD format to Upper Clock halt bit(min)
		LCD_Data(BCD2LowerCh(min));   //BCD format to Lower Clock halt bit(min)
		LCD_Data(':');                //To display ':'
		LCD_Data(BCD2UpperCh(sec));   //BCD format to Upper Clock halt bit(sec)
		LCD_Data(BCD2LowerCh(sec));   //BCD format to Lower Clock halt bit(sec)
		LCD_Cmd(0xC0);                //Cursor to beginning of 2nd row
		LCD_Str(date_format);         //To display the date 
		LCD_Data(BCD2UpperCh(date));  //BCD format to Upper Clock halt bit(date)
		LCD_Data(BCD2LowerCh(date));  //BCD format to Lower Clock halt bit(date)
		LCD_Data('/');                //To display '/'
		LCD_Data(BCD2UpperCh(month)); //BCD format to Upper Clock halt bit(month)
		LCD_Data(BCD2LowerCh(month)); //BCD format to Lower Clock halt bit(month)
		LCD_Data('/');                //To display '/'
		LCD_Data(BCD2UpperCh(year));  //BCD format to Upper Clock halt bit(year)
		LCD_Data(BCD2LowerCh(year));  //BCD format to Lower Clock halt bit(year)	
		__delay_ms(10);               //delay of 10ms


/****************RTC FUNCTIONS*****************/
unsigned char BCD2UpperCh(unsigned char bcd)
	unsigned char temp;
 	temp = bcd >> 4;
 	temp = temp | 0x30;                   //Lower 3-bits
unsigned char BCD2LowerCh(unsigned char bcd)
	unsigned char temp;
 	temp = bcd & 0x0F;                    //Making the Upper 4-bits
 	temp = temp | 0x30;

unsigned char rtc1307_read(unsigned char address)
	unsigned char temp;
	I2C_Send(address);                    //I2C function
	temp = I2C_Read();
	return temp;

/*****************LCD FUNCTIONS****************/
void LCD_Init()
	EN = 0;                              //Enable function
	__delay_ms(1);                       //delay of 1ms
	LCD_Cmd(0x38);                       //function set:8 bit,2nd line,5x7 dots
	__delay_ms(1);                       //delay of 1ms
	LCD_Cmd(0x0E);                       //Display On,Cursor On
	__delay_ms(1);                       //delay of 1ms
	LCD_Cmd(0x0C);	                     //Display On Cursor Off
	__delay_ms(1);                       //delay of 1ms
	LCD_Cmd(0x01);                       //Clear display screen
	__delay_ms(1);                       //delay of 1ms
	LCD_Cmd(0x06);                       //Entry mode
	__delay_ms(1);                       //delay of 1ms
	LCD_Cmd(0x80);	                     //Cursor to beginning of 1st row
	__delay_ms(1);                       //delay of 1ms

void LCD_Cmd(unsigned char value)            //LCD command function
	LCD_DATA_PINS = value;               //LCD data pins PORTD
	RS = 0;                              //Register select(command mode)
	RW = 0;                              //Read/Writ(Write operation)
	EN = 1;                              //Enable pin
	__delay_ms(1);                       //delay of 1ms
	EN = 0;	                             //Disable pin
void LCD_Data(unsigned char value)           //LCD data function
	LCD_DATA_PINS = value;               //LCD data pins PORTD
	RS = 1;                              //Register select(Data mode)
	RW = 0;                              //Read/Write(Write operation)
	EN = 1;                              //Enable pin
	__delay_ms(1);                       //delay of 1ms
	EN = 0;	                             //Disable pin

void LCD_Str(const unsigned char *str)       //LCD string
		str++;                       //Increment the Pointer

void USART_Init(void)                        // Serial Port UART
	SPBRG = 77;                          //Baud Rate = 9600 Bits per Second
	TXSTAbits.TXEN=1;                    //Enable Transmission by setting these Value's
	TXSTAbits.BRGH=0;                    //BAUDRATE is low
	RCSTAbits.SPEN=1;                    //Enable Reception by Setting these Value's
	RCSTAbits.CREN=1;	             //Enable Receiver (RX)


void USART_Write(unsigned char value)
	while(PIR1bits.TXIF == 0);           //Wait for TXREG Buffer to become available
	TXREG = value;                       //Transmitter register

void USART_Write_Str(const unsigned char *str)
		while(PIR1bits.TXIF == 0);   //Wait for TXREG Buffer to become available
		TXREG = *(str);              //Write Data

		str++;                       //Increment the Pointer

void USART_Write_Str_Line(const unsigned char *str)
	//Formely I used to write this

void USART_Write_Int(int val,unsigned char field_length)
	char str[5]={0,0,0,0,0};
	int i=4,j=0;
		USART_Write('-');	     //Write '-' sign for negative numbers.
		val=(val*(-1));		     //Make it positive.

	while(val)                           //Convert Number To String and pump over Tx Channel.
		while(str[j]==0) j++;
unsigned char USARTReadByte()
	while(!PIR1bits.RCIF);	             // EUSART Receive Interrupt Flag bit(Wait for a byte)
	return RCREG;                        //EUSART Receive Register

void I2C_Init()
	TRISB |= 0x03;                       //PORTB as SDA and SCL enable pin
	SSPSTAT |= 0x80;                     // MSSP STATUS REGISTER (SPI MODE) Slew Rate Disabled
	SSPADD = 119;                        //MSSP Address Register (SSPADD)
	SSPCON1 = 0b00101000;		     //Master mode
	SSPADD = 119;                        //MSSP Address Register (SSPADD)
	/*SSPEN = 1
	Enables the Serial Port and configures the SDA and SCL
	Pins as the Serial Pins
	SSPM3:SSPM0 --> 1000
	I2C Master Mode
	Clock = Fosc/4*(SSPADD+1)

void I2C_Start(void)
	SSPCON2bits.SEN = 1;                 //Start Condition Enable/Stretch Enable bit
	//SSPCON2 bit 0
	while (SSPCON2bits.SEN == 1)         //SEN =1 initiate the Start Condition on SDA and SCL Pins

void I2C_Restart(void)
	SSPCON2bits.RSEN = 1;                //SSPCON2 bit 1
	while (SSPCON2bits.RSEN == 1)        //RSEN = 1 initiate the Restart Condition
		continue;                    //Automatically Cleared by Hardware

void I2C_Stop(void)
	SSPCON2bits.PEN=1;                   // Initiate Stop condition on SDA and SCL pins.
	while(SSPCON2bits.PEN==1)            //Automatically cleared by hardware.

void I2C_Wait(void)
	while(SSPSTATbits.R_NOT_W == 1)
	if(SSPCON2bits.ACKSTAT == 1)         //If ACKSTAT bit is 0 Acknowledgment Received Successfully
	//Otherwise Not

void I2C_Send(unsigned char dat)
	SSPBUF = dat;                       // Move data to SSPBUF */
    while(SSPSTATbits.BF);                  // wait till complete data is sent from buffer */
    I2C_Wait();                             // wait for any pending transfer */

unsigned char I2C_Read(void)
	unsigned char temp;
    SSPCON2bits.RCEN = 1;                  // Enable data reception 
    while(SSPSTATbits.BF == 0)             // wait for buffer full 
    temp = SSPBUF;                         // Read serial buffer and store in temp register 
    I2C_Wait();                            // wait to check any pending transfer 
    SSPCON2bits.ACKDT=1;	           //send not acknowledge
	while(SSPCON2bits.ACKEN == 1) 
    return temp;                           //return the read data from bus */

Error message here!

Show Error message here!

Forgot your password?

Error message here!

Send OTP

Error message here!

Show Error message here!

Lost your password? Please enter your email address. You will receive a password you Need.

Send Error message here!

Back to log-in