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 (or DS3232) RTC - datasheet
- 2 x 10K ohm resistors
- 0.1uF ceramic capacitor
- 3V coin cell battery
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.