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