Thursday, July 6, 2017

DS1307 Interfacing with PIC16F887



This article shows how to build a simple real time clock/calendar using PIC16F887 and DS1307 RTC chip.
The DS1307 is an 8-pin integrated circuit uses I2C communication protocol to communicate with master device which is in our case the PIC16F887 microcontroller.
The PIC16F887 microcontroller has an I2C (IIC) module with two pins are used for data transfer. These are the RC3/SCK/SCL pin, which is the clock (SCL), and the RC4/SDI/SDA pin, which is the data (SDA).
The DS1307 can count seconds, minutes, hours, day, date, month and year with leap-year up to year 2100.
The DS1307 receives and transfers data (clock data and calendar data) as BCD format, so after receiving data we have to convert these data into decimal data, and before writing data to the DS1307 we have to convert this data from decimal to BCD format. For example we have the BCD number 33, converting this number into decimal gives 21.
In the CCS C code I used the following line to covert a number 'x' from BCD to decimal:
x = (x >> 4) * 10 + (x & 0x0F);
And to go from decimal to BCD I used this line:
x = ((x / 10) << 4) + (x % 10);
Hardware Required:
  • PIC16F887 Microcontroller
  • DS1307 RTC (datasheet)
  • 16x2 LCD screen
  • 32.768 KHz crystal
  • 10K potentiometer or variable resistor
  • 2 x 10K ohm resistors
  • 3V Coin cell battery
  • 0.1µF Ceramic capacitor (optional)
  • 5V Power supply source
  • Breadboard
  • Jumper wires
DS1307 Interfacing with PIC16F887 circuit:
The circuit diagram of the example is given below.
PIC16F887 and DS1307 RTC circuit
In this example the PIC16F887 MCU uses its internal oscillator and MCLR pin function is disabled.
SCL pin of PIC16F887 (pin number 18) is connected to the SCL pin of the DS1307 (pin number 6) and SDA pin of PIC16F887 (pin number 23) is connected to the SDA pin of the DS1307 (pin number 5).
The 3V cell battery is used to keep the time running if the main power is off.
The two resistors R1 & R2 are pull-up resistors, they are necessary for the I2C protocol.
In the circuit there are two buttons to set time and calendar. The button B1 selects time or calendar parameter (minutes, hours, date, month and year) and B2 increments the selected parameter.
DS1307 Interfacing with PIC16F887 CCS PIC C compiler code:
The code below can display time and calendar where their parameters such as minutes using two push buttons.
This code is tested with version 5.051.
/* Real time clock using PIC16F887 & DS1307 CCS C code
   Read DS1307 RTC datasheet to understand the code!
   Internal oscillator used @ 8MHz
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

//LCD module connections
#define LCD_RS_PIN PIN_D0
#define LCD_RW_PIN PIN_D1
#define LCD_ENABLE_PIN PIN_D2
#define LCD_DATA4 PIN_D3
#define LCD_DATA5 PIN_D4
#define LCD_DATA6 PIN_D5
#define LCD_DATA7 PIN_D6
//End LCD module connections

#include <16F887.h>
#fuses NOMCLR NOBROWNOUT NOLVP INTRC_IO
#use delay(clock = 8MHz)
#include <lcd.c>
#use fast_io(B)
#use I2C(master, I2C1, FAST = 100000)

char time[] = "TIME:  :  :  ";
char calendar[] = "DATE:  /  /20  ";
unsigned int8  second, minute, hour, date, month, year, day;
void ds1307_display(){
  // Convert BCD to decimal
  second = (second >> 4) * 10 + (second & 0x0F);
  minute = (minute >> 4) * 10 + (minute & 0x0F);
  hour = (hour >> 4) * 10 + (hour & 0x0F);
  date = (date >> 4) * 10 + (date & 0x0F);
  month = (month >> 4) * 10 + (month & 0x0F);
  year = (year >> 4) * 10 + (year & 0x0F);
  // End conversion
  time[12]     = second % 10  + 48;
  time[11]     = second / 10  + 48;
  time[9]      = minute % 10  + 48;
  time[8]      = minute / 10  + 48;
  time[6]      = hour % 10  + 48;
  time[5]      = hour / 10  + 48;
  calendar[14] = year % 10 + 48;
  calendar[13] = year / 10  + 48;
  calendar[9]  = month % 10 + 48;
  calendar[8]  = month / 10 + 48;
  calendar[6]  = date % 10 + 48;
  calendar[5]  = date / 10 + 48;
  lcd_gotoxy(1, 1);                              // Go to column 1 row 1
  printf(lcd_putc, time);                        // Display time
  lcd_gotoxy(1, 2);                              // Go to column 1 row 2
  printf(lcd_putc, calendar);                    // Display calendar
}
void ds1307_write(unsigned int8 address, data_){
  i2c_start();                                   // Start I2C protocol
  i2c_write(0xD0);                               // DS1307 address
  i2c_write(address);                            // Send register address
  i2c_write(data_);                              // Write data to the selected register
  i2c_stop();                                    // Stop I2C protocol
}
void main(){
  setup_oscillator(OSC_8MHZ);                    // Set internal oscillator to 8MHz
  port_b_pullups(3);                             // Enable internal pull-ups for RB0 & RB1
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // LCD clear
  while(TRUE){
   if(!input(PIN_B0)){                           // If RB0 button is pressed
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(5, 1);                            // Go to column 5 row 1
    lcd_putc("Minute:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))                          // If RB1 button is pressed
      minute++;                                  // Increment minutes
     if(minute > 59)
      minute = 0;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", minute);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Hour:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      hour++;
     if(hour > 23)
      hour = 0;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", hour);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Date:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      date++;
     if(date > 31)
      date = 1;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", date);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Month:");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      month++;
     if(month > 12)
      month = 1;
     lcd_gotoxy(8, 2);                           // Go to column 8 row 2
     printf(lcd_putc,"%02u", month);
     if(!input(PIN_B0))
      break;
     delay_ms(200);
    }
    lcd_putc('\f');                              // LCD clear
    lcd_gotoxy(6, 1);                            // Go to column 6 row 1
    lcd_putc("Year:");
    lcd_gotoxy(7, 2);                            // Go to column 7 row 1
    lcd_putc("20");
    delay_ms(200);
    while(TRUE){
     if(!input(PIN_B1))
      year++;
     if(year > 99)
      year = 0;
     lcd_gotoxy(9, 2);                           // Go to column 9 row 2
     printf(lcd_putc,"%02u", year);
     if(!input(PIN_B0)){
      // Convert decimal to BCD
      minute = ((minute / 10) << 4) + (minute % 10);
      hour = ((hour / 10) << 4) + (hour % 10);
      date = ((date / 10) << 4) + (date % 10);
      month = ((month / 10) << 4) + (month % 10);
      year = ((year / 10) << 4) + (year % 10);
      // End conversion
      ds1307_write(1, minute);                   // Write minute value to DS1307
      ds1307_write(2, hour);
      ds1307_write(4, date);
      ds1307_write(5, month);
      ds1307_write(6, year);
      ds1307_write(0, 0);                        //Reset seconds and start oscillator
      delay_ms(200);
      break;
     }
     delay_ms(200);
    }
   }
   i2c_start();                                  // Start I2C protocol
   i2c_write(0xD0);                              // DS1307 address
   i2c_write(0);                                 // Send register address
   i2c_start();                                  // Restart I2C
   i2c_write(0xD1);                              // Initialize data read
   second = i2c_read(1);                         // Read seconds from register 0
   minute = i2c_read(1);                         // Read minuts from register 1
   hour   = i2c_read(1);                         // Read hour from register 2
   day    = i2c_read(1);                         // Read day from register 3
   date   = i2c_read(1);                         // Read date from register 4
   month  = i2c_read(1);                         // Read month from register 5
   year   = i2c_read(0);                         // Read year from register 6
   i2c_stop();                                   // Stop I2C protocol
   ds1307_display();                             // Diaplay time & calendar
   delay_ms(50);
  }
}

The following video shows project simulation with Proteus:


Simulation file can be downloaded from this link:
DS1307 + PIC16F887 Simulation