Wednesday, July 1, 2015

DS1307 RTC Interfacing with AVR microcontroller

In this tutorial we will learn How to interface RTC DS1307 with AVR microcontroller.
We are using Atmega8 for the demo.


Real Time Clock DS1307 Interfacing with ATmega8


GENERAL DESCRIPTION
The DS1307 serial real-time clock (RTC) is a low-power, full binary-coded decimal (BCD)
clock/calendar plus 56 bytes of NV SRAM. Address and data are transferred serially through
an I2C™, bidirectional bus. The clock/calendar provides seconds, minutes, hours, day, date,
month, and year information. The end of the month date is automatically adjusted for months
with fewer than 31 days, including corrections for leap year. The clock operates in either the 24-
hour or 12-hour format with AM/PM indicator. The DS1307 has a built-in power-sense circuit
that detects power failures and automatically switches to the battery supply.

FEATURES

  • Real-Time Clock (RTC) Counts Seconds, Minutes, Hours, Date of the Month, Month,
  • Day of the week, and Year with Leap-Year Compensation Valid Up to 2100
  • 56-Byte, Battery-Backed, Nonvolatile (NV) RAM for Data Storage
  • I2C Serial Interface
  • Programmable Square-Wave Output Signal 
  • Automatic Power-Fail Detect and Switch Circuitry
  • Consumes Less than 500nA in Battery-Backup Mode with Oscillator Running
  • Optional Industrial Temperature Range: -40°C to +85°C
  • Available in 8-Pin DIP or SO
Pin Diagram of RTC DS1307:

DS1307 Pin Diagram
PIN DESCRIPTION:

X1 and X2 (pin 1,2): Connections for Standard 32.768kHz Quartz Crystal. The internal oscillator circuitry is designed for operation with a crystal having a specified load capacitance (CL) of 12.5pF. X1 is the input to the oscillator and can optionally be connected to an external 32.768kHz oscillator. The output of the internal oscillator, X2, is floated if an external oscillator is connected to X1.

VBAT (pin 3): Backup Supply Input for Any Standard 3V Lithium Cell or Other Energy Source. Battery voltage must be held between the minimum and maximum limits for proper operation. Diodes in series between the battery and the VBAT pin may prevent proper operation. If a backup supply is not required, VBAT may be grounded. The nominal power-fail trip point (VPF) voltage at which access to the RTC and user RAM is denied is set by the internal circuitry as 1.25 x VBAT nominal. A lithium battery with 48mAhr or greater will back up the DS1307 for more than 10 years in the absence of power at +25°C.

GND (pin 4): Ground

SDA (pin 5): Serial Data Input/Output. SDA is the data input/output for the I2C serial interface. The SDA pin is open drain and requires an external pullup resistor.

SCL (pin 6): Serial Clock Input. SCL is the clock input for the I2C interface and is used to synchronize data movement on the serial interface. 

SWQ/OUT (pin 7): Square Wave/Output Driver. When enabled, the SQWE bit set to 1, the SQW/OUT pin outputs one of four square-wave frequencies (1Hz, 4kHz, 8kHz, 32kHz). The SQW/OUT pin is open drain and requires an external pullup resistor. SQW/OUT operates with either VCC or VBAT applied. 

VCC (pin 8): Primary Power Supply. When voltage is applied within normal limits, the device is fully accessible and data can be written and read. When a backup supply is connected to the device and VCC is below VTP, read and writes are inhibited. However, the timekeeping function continues unaffected by the lower input voltage.

Understanding I2C Timing Diagram:


1. In this diagram you will understand that how I2C Start is given ? I2C Start is given by keeping SCL High
and making SDA Line from High to Low.
2. Similarly I2C Stop Condition is generated by making SDA Line Low to High transition.

Time Keeper Register:
DS1307 Registers


The DS1307 may operate in the following two modes:
1. Slave Receiver Mode (Write Mode): Serial data and clock are received through SDA and SCL. After each byte is received an acknowledge bit is transmitted. START and STOP conditions are recognized as the beginning and end of a serial transfer. Hardware performs address recognition after reception of the slave address and direction bit (see Figure 5). The slave address byte is the first byte received after the master generates the START condition. The slave address byte contains the 7-bit DS1307 address, which is 1101000, followed by the direction bit (R/W), which for a write is 0. After receiving and decoding the slave address byte, the DS1307 outputs an acknowledge on SDA. After the DS1307 acknowledges the slave address + write bit, the master transmits a word address to the DS1307. This sets the register pointer on the DS1307, with the DS1307 acknowledging the transfer. The master can then transmit zero or more bytes of data with the DS1307 acknowledging each byte received. The register pointer automatically increments after each data byte are written. The master will generate a STOP condition to terminate the data write.

Data Write Cycle:
DS1307 Data Write Cycle


2. Slave Transmitter Mode (Read Mode): The first byte is received and handled as in the slave receiver mode. However, in this mode, the direction bit will indicate that the transfer direction is reversed. The DS1307 transmits serial data on SDA while the serial clock is input on SCL. START and STOP conditions are recognized as the beginning and end of a serial transfer. The slave address byte is the first byte received after the START condition is generated by the master. The slave address byte contains the 7-bit DS1307 address, which is 1101000, followed by the direction bit (R/W), which is 1 for a read. After receiving and decoding the slave address the DS1307 outputs an acknowledge on SDA. The DS1307 then begins to transmit data starting with the register address pointed to by the register pointer. If the register pointer is not written to before the initiation of a read mode the first address that is read is the last one stored in the register pointer. The register pointer automatically increments after each byte are read. The DS1307 must receive a Not Acknowledge to end a read. 

Data Read Cycle:
DS1307 Data Read Cycle


Typical Circuit Connections of DS1307:
Circuit Connections of DS1307

AVR Studio C Code:

//=================================================================
/* DS1307 Real Time Clock Software           */
/* 2nd Dec 2014                              */
/* Copyright 2015 Circuits4You.com           */ 
/* WWW - http://blog.circuits4you.com        */
/* Email - info@circuits4you.com             */
 
/* LCD Pin-5(R/W) must be connected to ground*/
//=================================================================

//Note: 1. Define Clock in Configuration Opetions
//      2. Define RTC Connections in i2c.h
#include <avr/io.h>
#include <string.h>
#include "delay.h"
#include "i2c.h"

#define E   PD7
#define RS   PB0

void display(char string[16]);
void displaybyte(char D);
void dispinit(void);
void epulse(void);
void delay_ms(unsigned int de);
void line1();
void line2();


void SetTime(char HH,char MM, char SS, char ampm);
char GetHH();
char GetMM();
char GetSS();

void SetDate(char DD,char MM, char YY);
char GetDD();
char GetMonth();
char GetYY();

void DisplayDateTime();

int Read_RTC(char add);
int Write_RTC(char add,char data1);


char mystr[8];
int temp;
//=================================================================
//        Main Function
//=================================================================
int main(void)
{
 int set;

 DDRB = 0xE1;  //Set LCD Port Direction   
 DDRD = 0xE0;
 PORTB = 0x06; //Pull up for switches

  delay_ms(500);  //Initiaize LCD
  dispinit();
  delay_ms(200);
  while(1)
  {
  //Change Date/Time
  if((PINB & 0x02)==0x00) //Time Set Switch is pressed
  {        
   SetTime(11,11,11, 1);   
  }

  if((PINB & 0x04)==0x00) //Date Switch is pressed
  {
   SetDate(21,12,12);   
  }
  DisplayDateTime();

  }
}

//=================================================================
//        LCD Display Initialization Function
//=================================================================
void dispinit(void)
{
 int count;
 char init[]={0x43,0x03,0x03,0x02,0x28,0x01,0x0C,0x06,0x02,0x02};
  
 PORTB &= ~(1<<RS);           // RS=0
 for (count = 0; count <= 9; count++)
  {
 displaybyte(init[count]);
  }
 PORTB |= 1<<RS;    //RS=1
}


//=================================================================
//        Enable Pulse Function
//=================================================================
void epulse(void)
{
 PORTD |= 1<<E;
  delay_ms(10); //Adjust delay if required
 PORTD &= ~(1<<E);
 delay_ms(10); //Adjust delay if required
}


//=================================================================
//        Send Single Byte to LCD Display Function
//=================================================================
void displaybyte(char D)
{
//D4=PD6
//D5=PD5
//D6=PB7
//D7=PB6
 //data is in Temp Register
  char K1;
  K1=D;
  K1=K1 & 0xF0;
  K1=K1 >> 4;  //Send MSB
  
  PORTD &= 0x9F;  //Clear data pins 
  PORTB &= 0x3F;
  
  if((K1 & 0x01)==0x01){PORTD |= (1<<PD6);}
  if((K1 & 0x02)==0x02){PORTD |= (1<<PD5);}
  if((K1 & 0x04)==0x04){PORTB |= (1<<PB7);}
  if((K1 & 0x08)==0x08){PORTB |= (1<<PB6);}

 epulse();

  K1=D;
  K1=K1 & 0x0F;  //Send LSB
  PORTD &= 0x9F;  //Clear data pins 
  PORTB &= 0x3F;

  if((K1 & 0x01)==0x01){PORTD |= (1<<PD6);}
  if((K1 & 0x02)==0x02){PORTD |= (1<<PD5);}
  if((K1 & 0x04)==0x04){PORTB |= (1<<PB7);}
  if((K1 & 0x08)==0x08){PORTB |= (1<<PB6);}
 epulse();
}

//=================================================================
//        Display Line on LCD at desired location Function
//=================================================================
void display(char string[16])
{
 int len,count;

 PORTB |= (1<<RS);           // RS=1 Data Mode
  len = strlen(string);

   for (count=0;count<len;count++)
  {
    displaybyte(string[count]);
 }
}

void line1()
{
        PORTB &= ~(1<<RS);           // RS=0 Command Mode
  displaybyte(0x80);  //Move Coursor to Line 1
  PORTB |= (1<<RS);           // RS=1 Data Mode
}
void line2()
{
        PORTB &= ~(1<<RS);           // RS=0 Command Mode
  displaybyte(0xC0);  //Move Coursor to Line 2
  PORTB |= (1<<RS);           // RS=1 Data Mode
}
//=================================================================
//        Delay Function
//=================================================================
void delay_ms(unsigned int de)
{
unsigned int rr,rr1;
   for (rr=0;rr<de;rr++)
   {
  
  for(rr1=0;rr1<30;rr1++)   //395
  {
   asm("nop");
  }
   
   }
}
//=================================================================
//                    RTC1307_READ_WRITE                         //
//=================================================================
int Read_RTC(char add)
{
 int temp1;
 I2C_START_TX(0b11010000);
 i2c_transmit(add);
 i2c_start();
 I2C_START_RX(0b11010000);
 temp1 = i2c_receive(0);
 i2c_stop();
 return(temp1);
}

int Write_RTC(char add,char data1)
{
 I2C_START_TX(0b11010000); //device add. 
    i2c_transmit(add); //Reg. add.
 i2c_transmit(data1);
 i2c_stop();
return 0;
}

//=======================================================================
//                           SET TIME
//=======================================================================
void SetTime(char HH,char MM, char SS, char ampm)
{
 sprintf(mystr,"%03d",SS);
 Write_RTC(0x00,((mystr[1] - 0x30) << 4) | (mystr[2] - 0x30));

 sprintf(mystr,"%03d",MM);
 Write_RTC(0x01,((mystr[1] - 0x30) << 4) | (mystr[2] - 0x30));


 sprintf(mystr,"%03d",HH);
 if(ampm == 1)
 {
  Write_RTC(0x02,((((mystr[1] - 0x30) << 4) | (mystr[2] - 0x30)) | 0x40) | 0x20);
    }
 else
 {
  Write_RTC(0x02,((((mystr[1] - 0x30) << 4) | (mystr[2] - 0x30)) | 0x40));
 }
}
//=======================================================================
char GetHH()
{
 return Read_RTC(0x02);
}
//=======================================================================
char GetMM()
{
 return (Read_RTC(0x01) & 0x7F);
}
//=======================================================================
char GetSS()
{
 return Read_RTC(0x00);
}
//=======================================================================
//                   SET DATE
//=======================================================================
void SetDate(char DD,char MM, char YY)
{
 sprintf(mystr,"%03d",DD);
 Write_RTC(0x04,((mystr[1] - 0x30) << 4)  | (mystr[2] - 0x30));

 sprintf(mystr,"%03d",MM);
 Write_RTC(0x05,((mystr[1] - 0x30) << 4) | (mystr[2] - 0x30));

 sprintf(mystr,"%03d",YY);
 Write_RTC(0x06,((mystr[1] - 0x30) << 4)  | (mystr[2] - 0x30));
}
//=======================================================================
char GetDD()
{
 return Read_RTC(0x04);
}
//=======================================================================
char GetMonth()
{
 char j;
 j=Read_RTC(0x05);
 j=(j & 0x0F) + ((j >> 4) * 10);
 return j; //12/11
}
//=======================================================================
char GetYY()
{
 char k;
 k=Read_RTC(0x06);
 k=(k & 0x0F) + ((k >> 4) * 10);
 return k;
}
//=======================================================================
//                    Display Date and Time
//=======================================================================
void DisplayDateTime()
{
  temp = Read_RTC(0x00);
  
  mystr[7]=48+(temp & 0b00001111);
  mystr[6]=48+((temp & 0b01110000)>>4);
  mystr[5]=':';
 
  temp = Read_RTC(0x01);
  
  mystr[4]=48+(temp & 0b00001111);
  mystr[3]=48+((temp & 0b01110000)>>4);
  mystr[2]=':';
 
  temp = Read_RTC(0x02);
  
  mystr[1]=48+(temp & 0b00001111);
  mystr[0]=48+((temp & 0b00010000)>>4);

  line1(); 
  display("Time:");
  
  displaybyte(mystr[0]);
  displaybyte(mystr[1]);
  displaybyte(mystr[2]);
  displaybyte(mystr[3]);
  displaybyte(mystr[4]);
  displaybyte(mystr[5]);
  displaybyte(mystr[6]);
  displaybyte(mystr[7]);

  temp = Read_RTC(0x02);
  temp = temp & 0x20;
  if(temp == 0x20)
  {
   display(" PM");
  }
  else
  {
   display(" AM");
  }
  temp = Read_RTC(0x06);
  
  mystr[7]=48+(temp & 0b00001111);
  mystr[6]=48+((temp & 0b01110000)>>4);
  mystr[5]=':';
 
  temp = Read_RTC(0x05);
  
  mystr[4]=48+(temp & 0b00001111);
  mystr[3]=48+((temp & 0b01110000)>>4);
  mystr[2]=':';
 
  temp = Read_RTC(0x04);
  
  mystr[1]=48+(temp & 0b00001111);
  mystr[0]=48+((temp & 0b00110000)>>4);
  
  line2();
  display("Date:");
  
  displaybyte(mystr[0]);
  displaybyte(mystr[1]);
  displaybyte(mystr[2]);
  displaybyte(mystr[3]);
  displaybyte(mystr[4]);
  displaybyte(mystr[5]);
  displaybyte(mystr[6]);
  displaybyte(mystr[7]);
}



www.circuits4you.com


6 comments:

  1. Please change following in void DisplayDateTime()

    temp = Read_RTC(0x02);

    mystr[1]=48+(temp & 0b00001111);
    mystr[0]=48+((temp & 0b00010000)>>4);

    to

    temp = Read_RTC(0x02);

    mystr[1]=48+(temp & 0b00001111);
    mystr[0]=48+((temp & 0b00110000)>>4);

    Since a bit is getting missed in hour extraction.

    ReplyDelete
  2. please help me, i studied in this tutorial : "DS1307 RTC Interfacing with AVR microcontroller ", but not work,, cause date and time not play, there is 00:00:00 AM and time 00:00:00 thankyou,,,

    ReplyDelete
    Replies
    1. Check that you got faulty 32KHz crystal oscillator,

      Delete
  3. and i use it in atmega16 it doesn't work

    ReplyDelete