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:

Advertisements

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.

ds3231

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.

DS3231 interface

& 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.

seven segment with shift register

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.

LM35 for our digital clock
LM35

Circuit Diagram of our Digital Clock with DS3231:

complete 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.

digital clock with ds3231

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:

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:

Loading

MKDas

Mithun K. Das; B. Sc. in EEE from KUET; Head of R&D @ M's Lab Engineering Solution. "This is my personal blog. I post articles on different subjects related to electronics in the easiest way so that everything becomes easy for all, especially for beginners. If you have any questions, feel free to ask through the contact us page." Thanks.

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.

R K Hamy · 29/07/2021 at 4:37 pm

Thanks for the excellent project

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

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.

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *