Wednesday, August 30, 2017

Real time clock & calendar with PIC18F4550 and DS3231


Interfacing PIC18F4550 with DS3231 RTC
PIC18F4550 microcontroller with DS3231 RTC hardware circuit
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 PIC18F4550 microcontroller and DS3231.
The DS3231 uses I2C protocol to interface with the master device which is in our example the PIC18F4550 which has one I2C module.
The I2C protocol uses only two lines: SCL (Serial Clock) and SDA (Serial Data) which are in the PIC18F4550 pin RB1 and pin RB0 respectively.
Hardware Required:
  • PIC18F4550 microcontroller
  • 1602 LCD screen
  • 10K ohm variable 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
Real time clock & calendar with PIC18F4550 and DS3231 circuit:
Interfacing PIC18F4550 with DS3231 RTC cirucit
The 1602 LCD has 7 data lines which are connected to pins RD0~6, the DS3231 SCL pin is connected to pin RB1 (#34) and SDA is connected to pin RB0 (#33) of the PIC18F4550 microcontroller.
In the circuit there are 2 push buttons (B1 & B2) connected to pin RB2 and pin RB3, the two push buttons are used to set the time as well as the calendar parameters (minutes, hours, date......). The button B1 selects the parameter and B2 increments the selected parameter.
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 project the PIC18F4550 uses its internal oscillator and MCLR pin function is disabled.
Real time clock & calendar with PIC18F4550 and DS3231 C code:
The C code below was tested with CCS C compiler version 5.051.
The hardware I2C module of the PIC18F4550 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
Code functions:
void DS3231_display() : this function prints the time and calendar on the 1602 LCD screen. Before the print it converts the all the wanted data from BCD format to decimal format.
int8 edit(parameter, x, y) : I used this function to edit time and calendar parameters. I used a variable named i to distinguish between the parameters:
i = 0, 1 : hours and minutes respectively
i = 2, 3, 4: date, month and year respectively.
After the edit of time/calendar the data have to be converted back to BCD format and written to the DS3231 (it had been converted from BCD format to decimal format by the function void DS3231_display() ).
void blink() : this small function works as a delay except that it is interrupted by the buttons B1 (connected to RB2) and B2 (connected to RB3). 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 (RTCC) using PIC18F4550 & DS3231 CCS C code.
   Read DS3231 RTC datasheet to understand the code!
   Time & date parameters can be set using two push buttons connected to RB2 & RB3.
   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 <18F4550.h>
#fuses NOMCLR, INTRC_IO, NOWDT,NOPROTECT,NOLVP
#use delay(clock = 8000000)
#include <lcd.c>
#use fast_io(B)
#use fast_io(D)
#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_B2) && input(PIN_B3)){
    j++;
    delay_ms(25);
  }
}
int8 edit(parameter, x, y){
  while(!input(PIN_B2));                         // Wait until button RB2 released
  while(TRUE){
    while(!input(PIN_B3)){                       // If button RB3 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("  ");
    blink();
    lcd_gotoxy(x, y);                            // Print two spaces
    printf(lcd_putc,"%02u", parameter);          // Print parameter
    blink();
    if(!input(PIN_B2)){                          // If button RB2 is pressed
      i++;                                       // Increament 'i' for the next parameter
      return parameter;                          // Return parameter value and exit
    }
  }
}
void main(){
  setup_oscillator(OSC_8MHZ);                    // Set internal oscillator to 8MHz
  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_B2)){                          // If RB2 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);                              // Send register address (time seconds register)
      i2c_write(0);                              // Reset sesonds and start oscillator
      i2c_write(minute);                         // Write minutes value to DS3231
      i2c_write(hour);                           // Write hours 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 (time seconds register)
    i2c_start();                                 // Restart I2C
    i2c_write(0xD1);                             // Initialize data read
    second = i2c_read(1);                        // Read seconds from register 0
    minute = i2c_read(1);                        // Read minutes from register 1
    hour   = i2c_read(1);                        // Read hours 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);                                // Wait 50ms
  }
}

The project should work as shown in the video below (in the video the used mcu is PIC16F877A).