Wednesday, September 6, 2017

Interfacing PIC16F84 with DS3231 RTC


This post shows how to make a real time clock and calendar using PIC16F84 and DS3231 RTC.
The DS3231 uses I2C protocol to interface with the master device which is in this example the PIC16F84A MCU. In this project software I2C is used because the PIC16F84A MCU has no hardware I2C module.
A hardware circuit for DS3231 RTC and PIC16F84A
Hardware Required:
  • PIC16F84A microcontroller
  • 1602 LCD screen
  • 8MHz crystal oscillator
  • 2 x 22pF ceramic capacitor
  • 10K ohm variable resistor
  • 3 x 10K ohm resistor
  • 2 x push button
  • 5V supply source
  • Breadboard
  • Jumper wires
DS3231 board contains the following components:
  • DS3231 RTC - datasheet
  • 2 x 4.7K ohm resistors
  • 0.1uF ceramic capacitor
  • 3V coin cell battery
Interfacing PIC16F84 with DS3231 RTC circuit:
Interfacing PIC16F84A with DS3231 real time clock-calendar circuit
In the circuit there are 2 push buttons (B1 & B2) connected to pin RA2 and pin RA3, the two push buttons are used to set the time as well as the calendar parameters (minutes, hours, date, month and year). Button B1 selects the parameter and B2 increments the selected parameter.
Interfacing PIC16F84 with DS3231 RTC C code:
The C code below was tested with CCS PIC C compiler version 5.051.
I used the function below to initialize the software I2C where pin RA0 and pin RA1 are used for SDA and SCL lines respectively (PIC16F84A is used as master device):
#use I2C(MASTER, SDA=PIN_A0, SCL=PIN_A1, FAST=100000)
The DS3231 works with BCD format only and to convert the BCD to decimal and vise versa I used the 2 functions below. Before displaying (after reading from DS3231), the data have to be converted from BCD to decimal, and before writing to the DS3231 (after editing the parameters) the data have to be converted from decimal to BCD:
int8 bcd_to_decimal(number)
int8 decimal_to_bcd(number)
Each function returns the converted value of the variable number.
void DS3231_display() : displays time and calendar data, before displaying time and calendar data are converted from BCD format to decimal format using the function bcd_to_decimal(number) .
int8 edit(x, y, parameter) : I used this function to edit time calendar parameters (minutes, hours, date, month and year). I used a variable named i to distinguish between the parameters:
i = 0, 1 : time hours and minutes respectively
i = 2, 3, 4: calendar date, month and year respectively
After the edit of time and calendar, the data have to be converted back to BCD format using the function decimal_to_bcd(number) and written to the DS3231.
void blink() : this small function works as a delay except that it is interrupted by the buttons B1 (connected to RA2) and B2 (connected to RA3). When called and without pressing any button the total time is 10 x 25ms = 250ms. With this function we can see the blinking of the selected parameter with a frequency of 2Hz. So a delay of 250ms comes after the print of the selected parameter and after that delay a 2 spaces is printed which makes the parameter disappears from the LCD and another 250ms delay comes after the print of the 2 spaces.
The complete C code is the one below.
// Real time clock - calendar with DS3231 and PIC16F84A CCS C code.
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

//LCD module connections
#define LCD_RS_PIN     PIN_B0
#define LCD_RW_PIN     PIN_B1
#define LCD_ENABLE_PIN PIN_B2
#define LCD_DATA4      PIN_B3
#define LCD_DATA5      PIN_B4
#define LCD_DATA6      PIN_B5
#define LCD_DATA7      PIN_B6
//End LCD module connections

#include <16F84A.h>
#fuses HS,NOWDT,PUT,NOPROTECT
#use delay(clock = 8000000)
#use fast_io(A)
#use fast_io(B)
#include <lcd.c>
#use I2C(MASTER, SDA=PIN_A0, SCL=PIN_A1, FAST=100000)


int8 i, second, minute, hour, date, month, year;
int8 bcd_to_decimal(number){                     // Convert BCD to decimal function
  return((number >> 4) * 10 + (number & 0x0F));
}
int8 decimal_to_bcd(number){                     // Convert decimal to BCD function
  return(((number / 10) << 4) + (number % 10));
}
void ds3231_display(){
  second = bcd_to_decimal(second);
  minute = bcd_to_decimal(minute);
  hour   = bcd_to_decimal(hour);
  date   = bcd_to_decimal(date);
  month  = bcd_to_decimal(month);
  year   = bcd_to_decimal(year);
  lcd_gotoxy(13, 1);   
  printf(lcd_putc,"%02u", second);               // Display seconds
  lcd_gotoxy(10, 1);   
  printf(lcd_putc,"%02u", minute);               // Display minutes
  lcd_gotoxy(7, 1);   
  printf(lcd_putc,"%02u", hour);                 // Display hours
  lcd_gotoxy(7, 2);   
  printf(lcd_putc,"%02u", date);                 // Display date
  lcd_gotoxy(10, 2);   
  printf(lcd_putc,"%02u", month);                // Display month
  lcd_gotoxy(15, 2);   
  printf(lcd_putc,"%02u", year);                 // Display year
}
void blink(){
  int8 j = 0;
  while(j < 10 && input(PIN_A2) && input(PIN_A3)){
    j++;
    delay_ms(25);
  }
}
unsigned int8 edit(x, y, parameter){
  while(!input(PIN_A2));                         // Wait until button RB0 is released
  while(TRUE){
    while(!input(PIN_A3)){                       // 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(x, y);
      printf(lcd_putc,"%02u", parameter);        // Display parameter
      delay_ms(200);                             // Wait 200ms
    }
    lcd_gotoxy(x, y);
    lcd_putc("  ");                              // Display two spaces
    blink();
    lcd_gotoxy(x, y);
    printf(lcd_putc,"%02u", parameter);          // Display parameter
    blink();
    if(!input(PIN_A2)){                          // If button RA2 is pressed
      i++;                                       // Increament 'i' for the next parameter
      return parameter;                          // Return parameter value and exit
    }
  }
}
void main(){
  lcd_init();                                    // Initialize LCD module
  lcd_putc('\f');                                // LCD clear
  lcd_gotoxy(1, 1);  lcd_putc("TIME:   :  :");
  lcd_gotoxy(1, 2);  lcd_putc("DATE:   /  /20");
  while(TRUE){
    if(!input(PIN_A2)){                          // If RB0 button is pressed
      i = 0;
      hour   = edit(7, 1, hour);
      minute = edit(10, 1, minute);
      date   = edit(7, 2, date);
      month  = edit(10, 2, month);
      year   = edit(15, 2, year);
      // Convert decimal to BCD
      minute = decimal_to_bcd(minute);
      hour   = decimal_to_bcd(hour);
      date   = decimal_to_bcd(date);
      month  = decimal_to_bcd(month);
      year   = decimal_to_bcd(year);
      // End conversion
      // Write data to DS3231 RTC
      i2c_start();                               // Start I2C protocol
      i2c_write(0xD0);                           // DS3231 address
      i2c_write(0);                              // Send register address
      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);
  }
}
Simulation video:
The video below shows the project simulation using Proteus.