Interfacing PIC18F4550 with DS3231 RTC
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 RTC - datasheet
- 2 x 4.7K ohm resistors
- 0.1uF ceramic capacitor
- 3V coin cell battery
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).