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 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:
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)
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Β ito 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/ Β Β Β [email protected] */ //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).
Source :Β Real time clock & calendar with PIC18F4550 and DS3231