Saturday, August 26, 2017

Simple real time clock and calendar using DS3231 and PIC16F877A


Interfacing PIC16F877A with DS3231
The DS3231 is a low cost , extremely accurate real time clock with a built-in crystal oscillator. The characteristics of the DS3231 make it one of the best choices for real time clock chips.
This project shows how to build is simple real time clock and calendar (RTCC) using PIC16F877A microcontroller and DS3231.
The DS3231 uses I2C protocol to interface with the master device which is in our example the PIC16F877A which has one I2C module.
The I2C protocol uses only two lines: SCL (Serial Clock) and SDA (Serial Data) which are in the PIC16F877A RC3 and RC4 respectively.

Hardware Required:
  • PIC16F877A microcontroller
  • 16x2 LCD screen
  • 8 MHz crystal
  • 2 x 22pF ceramic capacitor
  • 10K ohm resistor
  • 2 x push button
  • 5V supply source
  • Breadboard
  • Jumper wires
DS3231 board contains the following components:
  • DS3231 (or DS3232) RTC - datasheet
  •  2 x 10K ohm resistors
  • 0.1uF ceramic capacitor
  • 3V coin cell battery
Simple real time clock and calendar using DS3231 and PIC16F877A circuit:
Real time clock / calendar using PIC16F877A microcontroller and DS3231 RTC circuit
The 16x2 LCD has 7 data lines which are connected to pins RD0~6, the DS3231 SCL pin is connected to pin RC3 (#18) and SDA is connected to pin RC4 (#23) of the PIC16F877A.
In the circuit there are 2 push buttons (B1 & B2) connected to pin RB0 and pin RB1, the two push buttons are used to set the time as well as the calendar parameters. The button B1 selects the parameter and B1 increments the selected parameter as shown in the videos below.

The 3V cell battery is used as a backup to keep time and date running in case of main power failure. The circuit can work without this battery but its pin (#14) has to be grounded.
In this example the PIC16F877A runs with 8MHz crystal oscillator.
Simple real time clock and calendar using DS3231 and PIC16F877A CCS C code:
The following C code was tested with CCS PIC C compiler version 5.051.
The hardware I2C module of the PIC16F877A is initialized and configured using the function below with a speed of 100KHz:
#use I2C(master, I2C1, FAST = 100000)
master: set the microcontroller to the master mode
I2C1: use first I2C module.
The DS3231 works with BCD format only and to convert the BCD to decimal and vise versa I used the following functions (example for minute variable):
minute = (minute >> 4) * 10 + (minute & 0x0F);                                  // Convert BCD to decimal
minute = ((minute / 10) << 4) + (minute % 10);                                   // Convert decimal to BCD
The complete C code is the one below.
/* Simple real time clock/calendar (RTCC) using PIC16F877A & DS3231 (DS3232) CCS C code.
   Read DS3231 RTC datasheet to understand the code!
   Time & date parameters can be set using two push buttons connected to RB0 & RB1.
   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 <16F877A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP                       
#use delay(clock = 8MHz)
#use fast_io(D)
#include <lcd.c>
#use I2C(master, I2C1, FAST = 100000)

char time[]     = "TIME:  :  :  ";
char calendar[] = "DATE:  /  /20  ";
int8  i, second, minute, hour, date, month, year;
void DS3231_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 blink(){
  int8 j = 0;
  while(j < 10 && input(PIN_B0) && input(PIN_B1)){
    j++;
    delay_ms(25);
  }
}
unsigned int8 edit(parameter, xx, yy){
  while(!input(PIN_B0));                         // Wait until button RB0 is released
  while(TRUE){
    while(!input(PIN_B1)){                       // If button RB1 is pressed
      parameter++;
      if(i == 0 && parameter > 23)               // If hours > 23 ==> hours = 0
        parameter = 0;
      if(i == 1 && parameter > 59)               // If minutes > 59 ==> minutes = 0
        parameter = 0;
      if(i == 2 && parameter > 31)               // If date > 31 ==> date = 1
        parameter = 1;
      if(i == 3 && parameter > 12)               // If month > 12 ==> month = 1
        parameter = 1;
      if(i == 4 && parameter > 99)               // If year > 99 ==> year = 0
        parameter = 0;
      lcd_gotoxy(xx, yy);
      printf(lcd_putc,"%02u", parameter);        // Display parameter
      delay_ms(200);                             // Wait 200ms
    }
    lcd_gotoxy(xx, yy);
    lcd_putc("  ");
    blink();
    lcd_gotoxy(xx, yy);                          // Display two spaces
    printf(lcd_putc,"%02u", parameter);          // Display parameter
    blink();
    if(!input(PIN_B0)){                          // If button RB0 is pressed
      i++;                                       // Increament 'i' for the next parameter
      return parameter;                          // Return parameter value and exit
    }
  }
}
void main(){
  set_tris_d(0);
  port_b_pullups(TRUE);                          // Enable PORTB internal pull-ups
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // LCD clear
  while(TRUE){
    if(!input(PIN_B0)){                          // If RB0 button is pressed
      i = 0;
      hour = edit(hour, 6, 1);
      minute = edit(minute, 9, 1);
      date = edit(date, 6, 2);
      month = edit(month, 9, 2);
      year = edit(year, 14, 2);
      // 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
      // Write data to DS3231 RTC
      i2c_start();                               // Start I2C protocol
      i2c_write(0xD0);                           // DS3231 address
      i2c_write(0);
      i2c_write(0);                              // Reset sesonds and start oscillator
      i2c_write(minute);                         // Write minute value to DS3231
      i2c_write(hour);                           // Write hour value to DS3231
      i2c_write(1);                              // Write day value (not used)
      i2c_write(date);                           // Write date value to DS3231
      i2c_write(month);                          // Write month value to DS3231
      i2c_write(year);                           // Write year value to DS3231
      delay_ms(200);                             // Wait 200ms
    }
    i2c_start();                                  // Start I2C protocol
    i2c_write(0xD0);                              // DS3231 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
    i2c_read(1);                                  // Read day from register 3 (not used)
    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
    DS3231_display();                             // Diaplay time & calendar
    delay_ms(50);
  }
}
Simple real time clock and calendar using DS3231 and PIC16F877A video:
The following video shows a hardware circuit of the example.


and the second video shows the simulation of the example using Proteus.


PIC16F877A + DS3231 simulation file download.