This post shows how to make a simple real time clock using DS3231 and PIC16F887 microcontroller where time and date are displayed on 20×4 LCD screen.
MikroC PRO for PIC compiler is used in this example.
General Description (from the DS3231 datasheet):
The DS3231 is a low-cost, extremely accurate I2C real-time clock (RTC) with an integrated temperature compensated crystal oscillator (TCXO) and crystal.
The device incorporates a battery input, and maintains accurate timekeeping when main power to the device is interrupted. The integration of the crystal resonator enhances the long-term accuracy of the device as well as reduces the piece-part count in a manufacturing line.
The DS3231 RTC maintains seconds, minutes, hours, day, date, month, and year information. The date at the end of the month is automatically adjusted for months with fewer than 31 days, including corrections for leap year.
The following image shows a typical operating circuit of the DS3231:
DS3231 RTC Library (driver) for mikroC compiler:
To simplify project C code, I wrote a small library for the DS3231 RTC. Library functions are listed below.
This driver supports software I2C and hardware I2C (more faster). It will use the software I2C (using mikroC built-in soft I2C library) if the following line is defined in the main code:
#define DS3231_SOFT_I2C
Otherwise the driver will automatically use hardware I2C1 module, but it will use hardware I2C2 module if the line below is defined in the main code:
#define DS3231_I2C2
void RTC_Set(RTC_Time *time_t): this function sets time and date (writes time and date to the DS3231) where the variable time_t of type struct (structure) collects the following integer variables:
seconds, minutes, hours, dow, day, month and year.
Where dow is day of the week (Monday, Tuesday …) and day is month day.
seconds and minutes range: 0 – 59
hours range: 0 – 23 (24-hour format)
dow range: 1 – 7 (1 equals Sunday, 2 equals Monday …)
day range: 1 – 31 (depends on month)
month range: 1 – 12 (1 equals January)
year range: 0 – 99.
RTC_Time *RTC_Get(): reads time and date from the DS3231, this function returns them as a variable of type struct (same as the previous function).
void Alarm1_Set(RTC_Time *time_t, al1 _config): sets alarm1 of the DS3231 where the variable time_t of type struct and _config is alarm1 configuration which may be one of the following:
ONCE_PER_SECOND —> alarm every 1 second
SECONDS_MATCH —> alarm when seconds match
MINUTES_SECONDS_MATCH —> alarm when minutes and seconds match
HOURS_MINUTES_SECONDS_MATCH —> alarm when hours, minutes and seconds match
DATE_HOURS_MINUTES_SECONDS_MATCH —> alarm when date (month day), hours, minutes and seconds match
DAY_HOURS_MINUTES_SECONDS_MATCH —> alarm when day (week day), hours, minutes and seconds match.
RTC_Time *Alarm1_Get(): reads time and date of alarm1, this function returns them as a variable of type struct.
void Alarm1_Enable(): enables alarm1 (writes 1 to A1IE bit).
void Alarm1_Disable(): disables alarm1 (writes 0 to A1IE bit).
uint8_t Alarm1_IF_Check(): returns 1 (TRUE) if alarm1 occurred, otherwise it returns 0 (FALSE).
void Alarm1_IF_Reset(): resets alarm1 flag bit (writes 0 to A1IF bit).
uint8_t Alarm1_Status(): returns 1 if alarm1 is enabled and 0 if disabled (reads A1IE bit).
void Alarm2_Set(RTC_Time *time_t, al2 _config): sets alarm2 of the DS3231 where the variable time_t of type struct and _config is alarm2 configuration which may be one of the following:
ONCE_PER_MINUTE —> alarm every 1 minute
MINUTES_MATCH —> alarm when minutes match
HOURS_MINUTES_MATCH —> alarm when hours and minutes match
DATE_HOURS_MINUTES_MATCH —> alarm when date (month day), hours and minutes match
DAY_HOURS_MINUTES_MATCH —> alarm when day (week day), hours and minutes match.
RTC_Time *Alarm2_Get(): reads time and date of alarm2, this function returns them as a variable of type struct.
void Alarm2_Enable(): enables alarm2 (writes 1 to A2IE bit).
void Alarm2_Disable(): disables alarm2 (writes 0 to A2IE bit).
uint8_t Alarm2_IF_Check(): returns 1 if alarm2 occurred, otherwise it returns 0.
void Alarm2_IF_Reset(): resets alarm2 flag bit (writes 0 to A2IF bit).
uint8_t Alarm2_Status(): returns 1 if alarm2 is enabled and 0 if disabled (reads A2IE bit).
void IntSqw_Set(INT_SQW _config): this function configures INT/SQW pin of the DS3231 where _config may be one of the following:
OUT_OFF —> output is off (internally attached to ground)
OUT_INT —> output is active low interrupt when alarm (alarm1 or alarm2)
OUT_1Hz —> output is a square wave with frequency of 1Hz
OUT_1024Hz —> output is a square wave with frequency of 1024Hz
OUT_4096Hz —> output is a square wave with frequency of 4096Hz
OUT_8192Hz —> output is a square wave with frequency of 8192Hz
void Enable_32kHZ(): enables 32kHz output (32kHz pin is open drain).
void Disable_32kHZ(): disables 32kHz output (internally attached to ground).
OSC_Start(): enables the DS3231 oscillator (writes 0 to ESOC bit).
OSC_Stop(): disables the DS3231 oscillator (writes 1 to ESOC bit).
int16_t Get_Temperature(): returns chip temperature. Temperature is stored in hundredths C (output value of “3125” equals 31.25 °C).
uint8_t RTC_Read_Reg(uint8_t reg_address): this function returns the value stored in register of address reg_address.
void RTC_Write_Reg(uint8_t reg_address, uint8_t reg_value): writes reg_value to register of address reg_address.
DS3231 Library for mikroC compiler download:
Driver source file download link is below, installation of this driver is easy, just add it to project folder. Driver full name (with extension) is: DS3231.c.
DS3231 mikroC library
DS3231 RTC Library Example:
This is a small example that shows how to use this library.
In this example PIC16F887 microcontroller is used, hardware circuit diagram is shown below.
All the grounded terminals are connected together.
SDA and SCL pins of the DS3231 board are respectively connected to SDA (RC4) and SCL (RC3) pins of the PIC16F887 microcontroller.
The 20×4 LCD screen (4 rows and 20 columns) is used to display time and date where:
RS —> pin RD0
E —> pin RD1
D4 —> pin RD2
D5 —> pin RD3
D6 —> pin RD4
D7 —> pin RD5
VSS, RW, D0, D1, D2, D3 and K are connected to circuit ground
VEE to the variable resistor (or potentiometer) output
VDD to +5V and A to +5V through 330 ohm resistor.
VEE pin is used to control the contrast of the LCD. A (anode) and K (cathode) are the back light LED pins.
In this project the PIC16F887 microcontroller uses its internal oscillator (@ 8MHz) and MCLR pin is configured as input.
C code:
The C code below is for mikroC (PRO for PIC) compiler, it was tested with version 7.2.0.
To be able to compile project C code with no error, a driver (library) for the DS3231 RTC is required, download link is above.
After you download the driver file which named DS3231.c, add it to your project folder.
Full mikroC code:
Configuration words (for PIC16F887 microcontroller):
CONFIG1 = 0x2CD4
CONFIG2 = 0x0700
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | /************************************************************************************** Interfacing PIC16F887 microcontroller with DS3231 RTC. Time, date and chip temperature are displayed on 20x4 LCD. C Code for mikroC PRO for PIC compiler. Internal oscillator used @ 8MHz Configuration words: CONFIG1 = 0x2CD4 CONFIG2 = 0x0700 This is a free software with NO WARRANTY. http://simple-circuit.com/ ***************************************************************************************/ // LCD module connections sbit LCD_RS at RD0_bit; sbit LCD_EN at RD1_bit; sbit LCD_D4 at RD2_bit; sbit LCD_D5 at RD3_bit; sbit LCD_D6 at RD4_bit; sbit LCD_D7 at RD5_bit; sbit LCD_RS_Direction at TRISD0_bit; sbit LCD_EN_Direction at TRISD1_bit; sbit LCD_D4_Direction at TRISD2_bit; sbit LCD_D5_Direction at TRISD3_bit; sbit LCD_D6_Direction at TRISD4_bit; sbit LCD_D7_Direction at TRISD5_bit; // end LCD module connections #include <DS3231.c> // include DS3231 RTC driver source file int chip_temp; // DS3231 library variable declaration RTC_Time *mytime; // function for printing time and date on LCD void rtc_print() { char txt[17]; // print day of the week switch(mytime->dow) { case SUNDAY : LCD_Out(1, 5, " SUNDAY "); break; case MONDAY : LCD_Out(1, 5, " MONDAY "); break; case TUESDAY : LCD_Out(1, 5, " TUESDAY "); break; case WEDNESDAY: LCD_Out(1, 5, "WEDNESDAY"); break; case THURSDAY : LCD_Out(1, 5, "THURSDAY "); break; case FRIDAY : LCD_Out(1, 5, " FRIDAY "); break; default : LCD_Out(1, 5, "SATURDAY "); } // print time sprinti(txt, "TIME: %02u:%02u:%02u", (int)mytime->hours, (int)mytime->minutes, (int)mytime->seconds); LCD_Out(2, 1, txt); // print date sprinti(txt, "DATE: %02u-%02u-20%02u", (int)mytime->day, (int)mytime->month, (int)mytime->year); LCD_Out(3, 1, txt); // print chip temperature if (chip_temp < 0) // if temperature is negative sprinti(txt, "TEMP:-%02u.%02u%cC", abs(chip_temp) / 100, abs(chip_temp) % 100, (int)223); else sprinti(txt, "TEMP: %02u.%02u%cC", chip_temp / 100, chip_temp % 100, (int)223); LCD_Out(4, 1, txt); } // main function void main() { OSCCON = 0x70; // set internal oscillator to 8MHz delay_ms(1000); // wait a second I2C1_Init(100000); // initialize I2C communication Lcd_Init(); // initialize LCD module Lcd_Cmd(_LCD_CURSOR_OFF); // cursor off Lcd_Cmd(_LCD_CLEAR); // clear LCD // read current time and date from the RTC chip mytime = RTC_Get(); // print them rtc_print(); delay_ms(1000); // wait a second // set RTC time to 21:08:47 (hh:mm:ss) and date to 03-01-19 (dd-mm-yy) mytime->hours = 21; mytime->minutes = 8; mytime->seconds = 47; mytime->dow = THURSDAY; mytime->day = 3; mytime->month = JANUARY; mytime->year = 19; // write time and date to the RTC chip RTC_Set(mytime); // enable SQW output with frequency of 1Hz IntSqw_Set(OUT_1Hz); while(1) { // read current time and date from the RTC chip mytime = RTC_Get(); // read chip temperature chip_temp = Get_Temperature(); // print all data rtc_print(); delay_ms(200); // wait 200 ms } } // end of code. |
Proteus simulation as well as hardware circuit of the example should give the result shown below (RTC starts updating immediately).
Note that Proteus simulation circuit is not the same as real hardware circuit, project hardware circuit is shown above.
PIC Projects where DS3231 RTC driver is used:
Discover more from Simple Circuit
Subscribe to get the latest posts sent to your email.
Please advise if software i2c is selected , how we select the scl and sda pines. Thanks
I am unable to include ds3231.c source file in the project and thus I am unable to compile the code. please help me out.