In this article, we are going to make a complete digital clock with DS3231 and a PIC microcontroller. The RTC ds3231 is extremely accurate as a clock. We are going to use this RTC for our Clock come calendar with temperature and showing these values on Seven-Segment displays. This article is a tutorial but it can be converted into a product with some minor changes. So let’s start our Digital Clock.
Disclaimer:
Handling electricity carries inherent risks. It’s essential to have the appropriate skills to manage it safely. Proceed at your own risk, as the author disclaims responsibility for any misuse, harm, or errors. All content on this website is unique and copyrighted; please avoid unauthorized copying. While most articles are open-source for your benefit, feel free to use the knowledge provided. If you find our resources helpful, consider purchasing available materials to support our work.
For assistance or guidance, leave a comment below; the author is committed to helping. Some articles may contain affiliate links that support the author with a commission at no additional cost to you. Thank you for your understanding and support.
Table of Contents
Why DS3231?
DS3231 is a RTC from maxim. This RTC is extremely accurate. The oscillator is kept inside and calibrated. That is why the IC can deliver extreme accuracy.
Key features of RTC ds3231:
- Highly Accurate RTC With Integrated MEMS Resonator Completely Manages All Timekeeping Functions
- Complete Clock Calendar Functionality Including Seconds, Minutes, Hours, Day, Date, Month, and Year, with Leap-Year Compensation Up to Year 2100
- Timekeeping Accuracy ±5ppm (±0.432 Second/Day)
- Two Time-of-Day Alarms
- 1Hz and 32.768kHz Outputs
- Digital Temp Sensor with ±3°C Accuracy
Interfacing ds3231 with microcontroller:
Interfacing is similar to DS1307. Pull-Up resistors with SCL and SDA pins which are connected to the microcontroller pins. Besides this, there is an Open-Drain SQW pulse output. We can use this pin to pulse a pair of LEDs. Transistors are used to amplify the current capacity as the output from the DS3231 is not enough for LEDs.
& the LEDs are:
You may read these articles too: Digital Clock using a PIC microcontroller & RTC DS1307
& Digital Clock with PIC16F676 & Seven Segment Display
Time & Date display in our digital clock with ds3231:
To show the Time and Date, we are using seven-segment displays. Like the previous article, we are using shift registers here. You know the benefits of using shift registers for this type of multi-display project. If you don’t know, I’ll suggest reading this article first.
Temperature sensing:
DS3231 has a built-in temperature sensor. But that is not accurate like LM35. Here in our project, we are going to use an LM35 temperature sensor. But I’ll discuss about the way you can read the temperature data from ds3231M in this article too.
Circuit Diagram of our Digital Clock with DS3231:
There is no complex part in this circuit. Download for high-resolution view.
Circuit operation:
There are 14 shift registers to drive 14 segments. All these shift registers are connected in cascade mode. The data is being sent from the microcontroller to the first shift register U2. Then the data is serially shifted to the right to U17. Here we did not use any resistors between the shift register and seven-segment. But in practical (PCB) you should use resistors (330R) to drive the seven segments. If the segment is large, use ULN2003A or ULN2803 drivers. Using a transistor will work too but it will make the circuit like a bird’s nest.
Besides the displays, there are 4 buttons for the edit function. The RTC DS3231 is driving 2 LEDs beside its communication through I2C with the MCU. For that, two transistors are used. You can use these small SMD versions as the current to drive the LEDs are not high at all but the IC can’t provide current to drive LEDs.
Programming:
Programming of our Digital Clock with DS3231 is a little bit extra compared to our previous article. But don’t worry, I’ll explain them all serially.
Shift registers to drive displays:
First thing first. We need to drive all the 14 shift registers to show the values on 14 different displays. That is why we need 14 sequel clock shifts and one latch clock.
// PIN declaration of shift registers sbit shift_latch at RB7_bit; sbit shift_sck at RB5_bit; sbit shift_data at RB6_bit; sbit shift_latch_Dirrection at TRISB7_bit; sbit shift_sck_Dirrection at TRISB5_bit; sbit shift_data_Dirrection at TRISB6_bit; // End of declearation void WriteByteToShift(unsigned Byte) { unsigned char BitCount = 0; for(BitCount=0;BitCount<8;BitCount++) { shift_data = (((Byte>>BitCount)&0x01)!=0); shift_sck = 0; Delay_us(30); shift_sck = 1; Delay_us(10); } } Latch_display() { shift_latch = 1; Delay_us(20); shift_latch = 0; Delay_us(10); } unsigned short mask(int num) { switch (num) { case 0 : return 0b11111100;// 0 case 1 : return 0b01100000;// 1 case 2 : return 0b11011010;// 2 case 3 : return 0b11110010;// 3 case 4 : return 0b01100110;// 4 case 5 : return 0b10110110;// 5 case 6 : return 0b10111110;// 6 case 7 : return 0b11100000;// 7 case 8 : return 0b11111110;// 8 case 9 : return 0b11110110;// 9 case 10: return 0x00;//null } } void disp_time(int hr, int min, int sec, int day, int month, int year, int temp) { WriteByteToShift(mask(temp%10u)); WriteByteToShift(mask(temp/10u)); WriteByteToShift(mask(year%10u)); WriteByteToShift(mask(year/10u)); WriteByteToShift(mask(month%10u)); WriteByteToShift(mask(month/10u)); WriteByteToShift(mask(day%10u)); WriteByteToShift(mask(day/10u)); WriteByteToShift(mask(sec%10u)); WriteByteToShift(mask(sec/10u)); WriteByteToShift(mask(min%10u)); WriteByteToShift(mask(min/10u)); WriteByteToShift(mask(hr%10u)); WriteByteToShift(mask(hr/10u)); Latch_display(); }
These functions are common to the previous one. All you see the extra is inside the disp_time() function. Here, all 14 sets of data are being transferred. Then one latch. That’s it.
You can read the previous article to understand this one: Digital Clock with PIC16F676 & Seven Segment Display
After this function, we can call this display function to print the data on the segments using this command.
disp_time(hour,minute,second,day,month,year,temperature);
Reading the RTC:
After the display function, we need to read the RTC. There is no such special difference in reading data from DS3231 compared to DS1307.
// Software I2C connections sbit Soft_I2C_Scl at RC3_bit; sbit Soft_I2C_Sda at RC4_bit; sbit Soft_I2C_Scl_Direction at TRISC3_bit; sbit Soft_I2C_Sda_Direction at TRISC4_bit; // End Software I2C connections int second,minute,hour; int hr,day,dday,month,year,ap,app; unsigned short read_ds3231(unsigned short address) { unsigned short r_data; Soft_I2C_Start(); Soft_I2C_Write(0xD0); Soft_I2C_Write(address); Soft_I2C_Start(); Soft_I2C_Write(0xD1); //0x68 followed by 1 --> 0xD1 r_data=Soft_I2C_Read(0); Soft_I2C_Stop(); return(r_data); } void write_ds3231(unsigned short address,unsigned short w_data) { Soft_I2C_Start(); // issue I2C start signal //address 0x68 followed by direction bit (0 for write, 1 for read) 0x68 followed by 0 --> 0xD0 Soft_I2C_Write(0xD0); // send byte via I2C (device address + W) Soft_I2C_Write(address); // send byte (address of DS1307 location) Soft_I2C_Write(w_data); // send data (data to be written) Soft_I2C_Stop(); // issue I2C stop signal } void write_ds3231_oscillator() { Soft_I2C_Start(); // issue I2C start signal Soft_I2C_Write(0x68);//address of DS3231 Soft_I2C_Write(0x0E); // send byte to the control register Soft_I2C_Write(0x00); // write the command bit Soft_I2C_Stop(); // issue I2C stop signal } char time[3],date[3]; void get_time() { second = read_ds3231(0); minute = read_ds3231(1); hour = read_ds3231(2); day = read_ds3231(4); month = read_ds3231(5); year = read_ds3231(6); second = Bcd2Dec(second); minute = Bcd2Dec(minute); hour = Bcd2Dec(hour); day = Bcd2Dec(day); month = Bcd2Dec(month); year = Bcd2Dec(year); if(hour>12) { hour-=12; ap = 1; AM = 0; PM = 1; } else { ap = 0; AM = 1; PM = 0; } time[0] = hour; time[1] = minute; time[2] = second; date[0] = day; date[1] = month; date[2] = year; }
Except for the oscillator activation part, all other function is same as the DS1307.
void write_ds3231_oscillator() { Soft_I2C_Start(); // issue I2C start signal Soft_I2C_Write(0x68);//address of DS3231 Soft_I2C_Write(0x0E); // send byte to the control register Soft_I2C_Write(0x00); // write the command bit Soft_I2C_Stop(); // issue I2C stop signal }
This write_ds3231_oscillator() function simply sending a command to the RTC to configure the SQW as 1Hz output.
And to read the temperature from DS3231 you can use this function:
Temperature reading from DS3231:
unsigned int tmp_ds=0; unsigned int t_msb=0,t_lsb=0; void read_temperature() { Soft_I2C_Start(); // issue I2C start signal Soft_I2C_Write(0xD0);//address of DS3231 Soft_I2C_Write(0x11); Soft_I2C_Start(); Soft_I2C_Write(0xD1); t_msb = Soft_I2C_Read(1); t_lsb = Soft_I2C_Read(0); tmp_ds = t_msb << 8 | (t_lsb & 0xC0);//Shift upper byte, add lower tmp_ds = (int)tmp_ds/256;//scale Soft_I2C_Stop(); }
The temperature is in floating-point value. I used seven segment displays to show the values where I did not keep any floating-point display. That is why I took only the integer part. You can use floating value to print in LCD or use it for other purposes.
We can use this temperature data for our clock, but for more accuracy, we used LM35 in our project digital clock with ds3231.
Parts used in this project:
- PIC16F76 microcontroller
- 74HC595 shift register
- Seven segment display
- DS3231 RTC
- LM35 temp. sensor
- Passive components
Temperature sensing from LM35:
LM35 is a very common temperature sensor. Output is 10mV/℃. We used this function to read the data. Note that we are using 8bit ADCs.
unsigned int temperature=0; int i=0; void Get_temp() { temperature = 0; for(i=0;i<5;i++) { temperature += 2*ADC_Get_Sample(0)-1; Delay_ms(20); } temperature/=5; Delay_ms(20); }
Now, our programming parts are almost completed. We can gather all the parts into the main code.
mikroC code for the main function:
/******************************************************************************* Program for all in one Digital Clock with DS3231 Program Written by_ Engr. Mithun K. Das MCU:PIC16F73; X-Tal:8MHz; compiler: mikroC pro for PIC v7.6.0 Date: 24-07-2021; email: mithun060@gmail.com *******************************************************************************/ #define AM RC0_bit #define PM RC1_bit #define menu RB1_bit #define plus RB2_bit #define minus RB3_bit #define back RB5_bit #include "Shift_register_display.h" #include "DS3231_function.h" unsigned int temperature=0; int i=0; void Get_temp() { temperature = 0; for(i=0;i<5;i++) { temperature += 2*ADC_Get_Sample(0)-1; Delay_ms(20); } temperature/=5; Delay_ms(20); } bit display_mode; short display_page=0; bit old_state; void main() { TRISA = 0xFF;//all output TRISC = 0x18;//RC3&4 input TRISB = 0x1F; PORTA = 0x00; PORTB = 0x00; PORTC = 0x00; ADC_Init(); Soft_I2C_Init(); display_mode = 0;//run mode display_page = 0;//normal t&d //for the first time setting /*hour = 20;//in 24 hr mode minute = 5; day = 24; month = 7; year = 21; write_ds1307(2, Dec2Bcd(hour)); write_ds1307(1, Dec2Bcd(minute)); write_ds1307(0,0x00); write_ds1307(4, Dec2Bcd(day)); write_ds1307(5,Dec2Bcd(month)); write_ds1307(6, Dec2Bcd(year)); write_ds1307_oscillator();//for 1Hz output at SQW pin*/ while(1) { if(!menu && old_state) { Delay_ms(200); if(!menu) { old_state = 0; display_mode = 1;//activate edit mode if(display_page<6)display_page++; else display_page = 1; } } if(menu)old_state = 1; if(!back && display_mode) { Delay_ms(300); if(!back) { display_mode = 0;//run mode display_page = 0;//reset page } } //Get_temp(); read_temperature(); get_time(); if(display_mode) { if(display_page==1) { hour = Bcd2Dec(hour); if(!plus) { if(hour<23)hour++; else hour = 0; } if(!minus) { if(hour>0)hour--; else hour = 23; } write_ds3231(2, Dec2Bcd(hour)); } if(display_page==2) { minute = Bcd2Dec(minute); if(!plus) { if(minute<59)minute++; else minute = 0; } if(!minus) { if(minute>0)minute--; else minute = 59; } write_ds3231(1, Dec2Bcd(minute)); } if(display_page==3) { write_ds3231(0,0x00); //Reset second to 0 sec. and start Oscillator } if(display_page==4) { day = Bcd2Dec(day); if(!plus) { if(day<31)day++; else day = 0; } if(!minus) { if(day>0)day--; else day = 31; } write_ds3231(4, Dec2Bcd(day)); } if(display_page==5) { month = Bcd2Dec(month); if(!plus) { if(month<12)month++; else month = 0; } if(!minus) { if(month>0)month--; else month = 12; } write_ds3231(5, Dec2Bcd(month)); } if(display_page==6) { year = Bcd2Dec(year); if(!plus) { if(year<99)year++; else year = 0; } if(!minus) { if(year>0)year--; else year = 0; } write_ds3231(6, Dec2Bcd(year)); } disp_edit_time(hour,minute,second,day,month,year,temperature,display_page); Delay_ms(200); } else { disp_time(hour,minute,second,day,month,year,tmp_ds); } }//while(1) }//void main() //end
Here, two operation mode is kept. One is for normal operation and another is for the edit function. I converted the data from BCD to Decimal and Decimal to BCD when required to edit the time & date values. And the disp_edit_time() function is used to toggle the edit value.
The header files are here:
Header file 1:
//Shift_register_display.h // PIN declaration of shift registers sbit shift_latch at RB7_bit; sbit shift_sck at RB5_bit; sbit shift_data at RB6_bit; sbit shift_latch_Dirrection at TRISB7_bit; sbit shift_sck_Dirrection at TRISB5_bit; sbit shift_data_Dirrection at TRISB6_bit; // End of declearation void WriteByteToShift(unsigned Byte) { unsigned char BitCount = 0; for(BitCount=0;BitCount<8;BitCount++) { shift_data = (((Byte>>BitCount)&0x01)!=0); shift_sck = 0; Delay_us(30); shift_sck = 1; Delay_us(10); } } Latch_display() { shift_latch = 1; Delay_us(20); shift_latch = 0; Delay_us(10); } unsigned short mask(int num) { switch (num) { case 0 : return 0b11111100;// 0 case 1 : return 0b01100000;// 1 case 2 : return 0b11011010;// 2 case 3 : return 0b11110010;// 3 case 4 : return 0b01100110;// 4 case 5 : return 0b10110110;// 5 case 6 : return 0b10111110;// 6 case 7 : return 0b11100000;// 7 case 8 : return 0b11111110;// 8 case 9 : return 0b11110110;// 9 case 10: return 0x00;//null } } void disp_time(int hr, int min, int sec, int day, int month, int year, int temp) { WriteByteToShift(mask(temp%10u)); WriteByteToShift(mask(temp/10u)); WriteByteToShift(mask(year%10u)); WriteByteToShift(mask(year/10u)); WriteByteToShift(mask(month%10u)); WriteByteToShift(mask(month/10u)); WriteByteToShift(mask(day%10u)); WriteByteToShift(mask(day/10u)); WriteByteToShift(mask(sec%10u)); WriteByteToShift(mask(sec/10u)); WriteByteToShift(mask(min%10u)); WriteByteToShift(mask(min/10u)); WriteByteToShift(mask(hr%10u)); WriteByteToShift(mask(hr/10u)); Latch_display(); }
It is not possible to show the result in simulation as the proteus is working very slow to simulate this circuit. I suggest you make the project real and then test it.
Header file 2:
//DS3231_function.h // Software I2C connections sbit Soft_I2C_Scl at RC3_bit; sbit Soft_I2C_Sda at RC4_bit; sbit Soft_I2C_Scl_Direction at TRISC3_bit; sbit Soft_I2C_Sda_Direction at TRISC4_bit; // End Software I2C connections int second,minute,hour; int hr,day,dday,month,year,ap,app; unsigned short read_ds3231(unsigned short address) { unsigned short r_data; Soft_I2C_Start(); Soft_I2C_Write(0xD0); Soft_I2C_Write(address); Soft_I2C_Start(); Soft_I2C_Write(0xD1); //0x68 followed by 1 --> 0xD1 r_data=Soft_I2C_Read(0); Soft_I2C_Stop(); return(r_data); } void write_ds3231(unsigned short address,unsigned short w_data) { Soft_I2C_Start(); // issue I2C start signal //address 0x68 followed by direction bit (0 for write, 1 for read) 0x68 followed by 0 --> 0xD0 Soft_I2C_Write(0xD0); // send byte via I2C (device address + W) Soft_I2C_Write(address); // send byte (address of DS1307 location) Soft_I2C_Write(w_data); // send data (data to be written) Soft_I2C_Stop(); // issue I2C stop signal } void write_ds3231_oscillator() { Soft_I2C_Start(); // issue I2C start signal Soft_I2C_Write(0xD0);//address of DS3231 Soft_I2C_Write(0x0E); // send byte to the control register Soft_I2C_Write(0x00); // write the command bit Soft_I2C_Stop(); // issue I2C stop signal } unsigned int tmp_ds=0; unsigned int t_msb=0,t_lsb=0; void read_temperature() { Soft_I2C_Start(); // issue I2C start signal Soft_I2C_Write(0xD0);//address of DS3231 Soft_I2C_Write(0x11); Soft_I2C_Start(); Soft_I2C_Write(0xD1); t_msb = Soft_I2C_Read(1); t_lsb = Soft_I2C_Read(0); tmp_ds = t_msb << 8 | (t_lsb & 0xC0);//Shift upper byte, add lower tmp_ds = (int)tmp_ds/256;//scale Soft_I2C_Stop(); } char time[3],date[3]; void get_time() { second = read_ds3231(0); minute = read_ds3231(1); hour = read_ds3231(2); day = read_ds3231(4); month = read_ds3231(5); year = read_ds3231(6); second = Bcd2Dec(second); minute = Bcd2Dec(minute); hour = Bcd2Dec(hour); day = Bcd2Dec(day); month = Bcd2Dec(month); year = Bcd2Dec(year); if(hour>12) { hour-=12; ap = 1; AM = 0; PM = 1; } else { ap = 0; AM = 1; PM = 0; } time[0] = hour; time[1] = minute; time[2] = second; date[0] = day; date[1] = month; date[2] = year; }
So all the codes are here. You can use all these parts to make a project in mikroC pro for PIC v7.6.0 or higher. I always use a valid license. You should use it too.
You can buy the compiled files from here.
Simulation result:
Proteus can not run this circuit at real speed. That is why the circuit did not give us a real-speed performance. That is why I ordered the PCB from my PCB partner. Once it arrives, I’ll make the circuit in real-time. Till then, please forgive me for checking this video as the simulation result.
PCB:
Conclusion:
It was a long circuit operation compared to a simple clock. The design is done in a productive way. With some minor changes, this project can be turned into a real product. And I’m doing it here. You can try it too. If you need any help with this don’t forget to comment below or directly contact me. I hope this project will help you a lot to make your own digital clock. Thank you.
For Professional Designs or Help:
24 Comments
Joe Rotello · 25/07/2021 at 5:19 pm
Interesting…yet, even with high accuracy crystal time-base / MPU control, these clocks usually wind up not being very accurate, too “easily” losing / gaining time, etc.
If there was an option to “lock” to the “atomic clock” frequency and thus periodically “calibrate” time, these types of clocks would be very useful to a great many more people, and a great deal more accurate as well, especially over time.
MKDas · 25/07/2021 at 5:29 pm
Thank you.
R K Hamy · 29/07/2021 at 4:37 pm
Thanks for the excellent project
MKDas · 29/07/2021 at 4:55 pm
You are welcome
Md.Araf hossain · 03/09/2021 at 11:56 pm
osadaron project. ati ami toiri krbo. and kmn holo r result ki seta apnke dekhabo.
“”””but din gular nam satha add kora thakle jeno pori-purnota lav peto . “‘”‘
plz help me. code ar part onk gula.
kontar por konta hobe ? ak page a sajiya dila vlo hoto.
MKDas · 04/09/2021 at 12:19 pm
etai ag e complete koren. tahole update korte parben sohoj e.
Samsuddin Ahmed · 04/03/2024 at 11:44 pm
Please give me hex file.
mohd azeem · 25/08/2022 at 11:18 am
Dear Sir,
i am Trying this Project Digital Clock with DS3231 & PIC micro-controller but i am seeing one error in main code error is undeclared identifier
MKDas · 25/08/2022 at 8:24 pm
which line or word is undeclared, check that.
mohd azeem · 25/08/2022 at 11:28 am
Digital Clock with DS3231 & PIC microcontroller but sowing one error undeclared identifier ‘disp_edit_time’ in the experssion
please help me
MKDas · 25/08/2022 at 8:24 pm
Check for that function, or there is another code that needs to save in another tab.
mohd azeem · 25/08/2022 at 4:40 pm
I am trying to compile this project code Digital Clock with DS3231 & PIC microcontroller but mikroc compiler stile showing error undeclared identifier ‘
disp_edit_time
MKDas · 25/08/2022 at 8:26 pm
save the heder file correctly.
mohd azeem · 31/08/2022 at 4:12 pm
thanks sir, i will try.
MOHD AZEEM · 19/09/2022 at 12:48 pm
I am trying again to compile this project code Digital Clock with DS3231 & PIC microcontroller but still same problem please help sir,
mikroc compiler stile showing error undeclared identifier ‘
disp_edit_time
MKDas · 20/09/2022 at 10:57 am
You need to create disp_edit_time header file. which is given as code.
DM BEDSAM · 13/01/2023 at 12:08 pm
Sir here code is different. I do not understand which will happen after which. It would be nice to have the codes together. help me
MKDas · 13/01/2023 at 3:48 pm
You should learn c / mikroC first.
DM BEDSAM · 13/01/2023 at 7:14 pm
Sir Can you give me the full code to my gmail. I understand mikroC very little
MKDas · 13/01/2023 at 9:13 pm
Code is published.
Raihan · 07/08/2023 at 2:41 pm
Sir why this Error ??
global variable expected after at but [ RB7_bit ] found shift_register_display.h
MKDas · 12/08/2023 at 6:15 pm
‘cz you have not included that .h file.
Samsuddin Ahmed · 13/12/2023 at 3:29 pm
plz give me Hex code.
MKDas · 14/12/2023 at 8:42 pm
added