Menu

all about electronic and microcontrollers

Saturday, March 29, 2014

Real Time Clock [DS1307 with Set Functions] & DS18B20

Hello dear fellows, today I will share with you this amazing project which will highlights what can be done with DS1307.
The Real Time Clock (RTC) chip produced by Maxim is a popular and relatively low cost solution for creating a room clock.

Features:
  1. Real-Time Clock (RTC) Counts Seconds, Minutes, Hours, Date of the Month, Month, Day of the week, and Year with Leap-Year Compensation Valid Up to 2100.
  2. 56-Byte, Battery-Backed, General-Purpose RAM with Unlimited Writes.
  3.  I2C Serial Interface.
  4. Programmable Square-Wave Output Signal.
  5.  Automatic Power-Fail Detect and Switch Circuitry.
  6. Consumes Less than 500nA in Battery-Backup Mode with Oscillator Running.
  7.  Operating temperature range for commercial use only: 0°C to +70°C.
  8. Optional Industrial Temperature Range: -40°C to +85°C.
  9. Available in 8-Pin Plastic DIP or SO.
As can be seen from it's datasheet, the communications are achieved via I2C (designed by Philips) protocol.

Hardware setup:
In the current article I intend to display the clock information and room temperature, on the LCD with 2x16 characters. Also trough four buttons, I will try to be able to set all the times variable, stored, in DS1307 chip.
Physical realization is carried out on the breadboard with 2420 dots.





Trough this video I would like to let you know precisely, how are things going.


Below I present the timing diagram for I2C signal transmitted on pins 5-6 of the DS1307.

Data Transfer on I2C Serial Bus.

Pin Configurations.
The diagram block.


For more details, please study the DS1307 datasheet.

Circuit Diagram:
Difficulty level of the electronic scheme, is low. The microcontroller used is PIC16F876A.
S1 is the master reset button, R1 is the resistor of pull-up button.
Cristal quartz by 8 MHz, is used.
ICSP connector is used to program the microcontroller.
Trough R7 we can adjust the contrast for LCD with 2x16 characters.
R6 adjusts the current through the LED LCD (light intensity of it).
R2-R5, R8-R10 are resistors for pull-up.

I personally designed the physical pcb circuit for DS1307, through the five pins,  pcb communicate with the breadboard in this way (GND, SQW, SCL, SDA, 5VDC). In this project i didn't use SQW pin.


The electronic scheme is built in Eagle Cad , free version.

Software:
The program is written in mikroC Pro for PIC 2013 (version v6.0.0).
Below is my software version:
/*
'*******************************************************************************
'  Project name: Real Time Clock [DS1307 with Set Functions] & DS18B20
'  Description:
'          Trough the current experiment we wish to succed the next task:
'          Display on LCD 2x16 character the clock and room temperature.
'          Setting trough four buttons: the minutes, hours, date of the month,
'          month, day of the week, and year.
'
'          Our clock displays as shown below(but just in display time,
'          not in set mode).
'          Ex. of viewing on 2x16 LCD characters:
'          Display time, mode:             Set time, mode (cursor on):
'          ------------------              ------------------
'          |Sat, 03 Dec 2011|              |Sat, 03 12  2011|
'          |21:32:03 +26,1*C|              |21:32:03        |
'          ------------------              ------------------
'
'          Hardware configuration is:
'             IC ds1307 is connected with our microcontroller trough RC3=SCL,
'             RC4=SDA (I2C Connections), RB0,RB1,RB4-RB7 are assigned to LCD (2x16)
'             DS18B20 is assigned to RC7,
'             Buttons Menu: RC0= Increment value,
'                           RC1= Decrement value,
'                           RC2= Change cursor position,
'                           RC5= Enter.(It goes to set functions or exit from set
'                                functions)
'  Written by:
'          Aureliu Raducu Macovei, 2014.
'  Test configuration:
'    MCU:                        PIC16F876A;
'    Test.Board:                 WB-106 Breadboard 2420 dots;
'    SW:                         MikroC PRO for PIC 2013 (version v6.0.0);
'  Configuration Word:
'    Oscillator:                 HS (8Mhz)on pins 9 and 10;
'    Watchdog Timer:             OFF;
'    Power up Timer:             OFF;
'    Browun Out Detect:          ON;
'    Low Voltage Program:        Disabled;
'    Data EE Read Protect:       OFF;
'    Flash Program Write:        Write Protection OFF;
'    Background Debug:           Disabled;
'    Code Protect:               OFF
'*******************************************************************************
*/

// LCD module connections
sbit LCD_RS at RB0_bit;                 // LCD_RS assigned to PORT RB0;
sbit LCD_EN at RB1_bit;                 // LCD_EN assigned to PORT RB1;
sbit LCD_D4 at RB4_bit;                 // LCD_D4 assigned to PORT RB4;
sbit LCD_D5 at RB5_bit;                 // LCD_D5 assigned to PORT RB5;
sbit LCD_D6 at RB6_bit;                 // LCD_D6 assigned to PORT RB6;
sbit LCD_D7 at RB7_bit;                 // LCD_D7 assigned to PORT RB7;

sbit LCD_RS_Direction at TRISB0_bit;    // LCD_RS assigned to TRIS B0;
sbit LCD_EN_Direction at TRISB1_bit;    // LCD_EN assigned to TRIS B1;
sbit LCD_D4_Direction at TRISB4_bit;    // LCD_D4 assigned to TRIS B4;
sbit LCD_D5_Direction at TRISB5_bit;    // LCD_D5 assigned to TRIS B5;
sbit LCD_D6_Direction at TRISB6_bit;    // LCD_D6 assigned to TRIS B6;
sbit LCD_D7_Direction at TRISB7_bit;    // LCD_D7 assigned to TRIS B7;
// End LCD module connections

unsigned char sec,min1,hr,week_day,day,mn,year;
//--------------------- Reads time and date information from RTC (DS1307)
void Read_Time(char *sec, char *min, char *hr, char *week_day, char *day, char *mn, char *year)
{
 I2C1_Start();                    // Issue start signal
 I2C1_Wr(0xD0);                   // Address DS1307, see DS1307 datasheet
 I2C1_Wr(0);                      // Start from address 0
 I2C1_Repeated_Start();           // Issue repeated start signal
 I2C1_Wr(0xD1);                   // Address DS1307 for reading R/W=1
 *sec =I2C1_Rd(1);                // Read seconds byte
 *min =I2C1_Rd(1);                // Read minutes byte
 *hr =I2C1_Rd(1);                 // Read hours byte
 *week_day =I2C1_Rd(1);           // Read week day byte
 *day =I2C1_Rd(1);                // Read day byte
 *mn =I2C1_Rd(1);                 // Read mn byte
 *year =I2C1_Rd(0);               // Read Year byte
 I2C1_Stop();                     // Issue stop signal
}
//-----------------write time routine------------------
void Write_Time(char minute, char hour ,char weekday,char day,char month,char year)
{
 char tmp1, tmp2;

 tmp1 = minute / 10;               //Write tens of minute
 tmp2 = minute % 10;               //Write unit of minute
 minute = tmp1 * 16 + tmp2;        //Includes all value

 tmp1 = hour / 10;                 //Write tens of hour
 tmp2 = hour % 10;                 //Write unit of hour
 hour = tmp1 * 16 + tmp2;          //Includes all value

 tmp1 = weekday / 10;              //Write tens of weekday
 tmp2 =  weekday % 10;             //Write unit of weekday
 weekday = tmp1 *16 +tmp2;         //Includes all value

 tmp1 = day / 10;                  //Write tens of day
 tmp2 =  day % 10;                 //Write unit of day
 day = tmp1 *16 +tmp2;             //Includes all value

 tmp1 = month / 10;                //Write tens of month
 tmp2 =  month % 10;               //Write unit of month
 month = tmp1 *16 +tmp2;           //Includes all value

 tmp1 = year / 10;                 //Write tens of year
 tmp2 =  year % 10;                //Write unit of year
 year = tmp1 *16 +tmp2;            //Includes all value

 I2C1_Start();          // issue start signal
 I2C1_Wr(0xD0);         // address DS1307
 I2C1_Wr(0);            // start from word at address (REG0)
 I2C1_Wr(0x80);         // write $80 to REG0. (pause counter + 0 sec)
 I2C1_Wr(minute);       // write minutes word to (REG1)
 I2C1_Wr(hour);         // write hours word (24-hours mode)(REG2)
 I2C1_Wr(weekday);      // write 6 - Saturday (REG3)
 I2C1_Wr(day);          // write 14 to date word (REG4)
 I2C1_Wr(month);        // write 5 (May) to month word (REG5)
 I2C1_Wr(year);         // write 01 to year word (REG6)
 I2C1_Wr(0x80);         // write SQW/Out value (REG7)
 I2C1_Stop();           // issue stop signal

 I2C1_Start();          // issue start signal
 I2C1_Wr(0xD0);         // address DS1307
 I2C1_Wr(0);            // start from word at address 0
 I2C1_Wr(0);            // write 0 to REG0 (enable counting + 0 sec)
 I2C1_Stop();           // issue stop signal
}
//-------------------- Formats date and time---------------------
void Transform_Time(char  *sec, char *min, char *hr, char *week_day, char *day, char *mn, char *year)
{
  *sec  =  ((*sec & 0x70) >> 4)*10 + (*sec & 0x0F);
  *min  =  ((*min & 0xF0) >> 4)*10 + (*min & 0x0F);
  *hr   =  ((*hr & 0x30) >> 4)*10 + (*hr & 0x0F);
  *week_day =(*week_day & 0x07);
  *day  =  ((*day & 0xF0) >> 4)*10 + (*day & 0x0F);
  *mn   =  ((*mn & 0x10) >> 4)*10 + (*mn & 0x0F);
  *year =  ((*year & 0xF0)>>4)*10+(*year & 0x0F);
 }
//------------------------Display time---------------------------
char *txt,*mny;
void Display_Time(char sec, char min, char hr, char week_day, char day, char mn, char year)
{
 switch(week_day)
 {
  case 1: txt="Mon"; break;       // Monday;
  case 2: txt="Tue"; break;       // Tuesday;
  case 3: txt="Wed"; break;       // Wednesday;
  case 4: txt="Thu"; break;       // Thursday;
  case 5: txt="Fri"; break;       // Friday;
  case 6: txt="Sat"; break;       // Saturday;
  case 7: txt="Sun"; break;       // Sunday;
  }

 LCD_Out(1, 1,txt);
 LCD_chr(1, 4,',');

 switch(mn)
 {
  case  1: mny="Jan"; break;
  case  2: mny="Feb"; break;
  case  3: mny="Mar"; break;
  case  4: mny="Apr"; break;
  case  5: mny="May"; break;
  case  6: mny="Jun"; break;
  case  7: mny="Jul"; break;
  case  8: mny="Aug"; break;
  case  9: mny="Sep"; break;
  case 10: mny="Oct"; break;
  case 11: mny="Nov"; break;
  case 12: mny="Dec"; break;
  }

 Lcd_Chr(1, 6, (day / 10) + 48);    // Print tens digit of day variable
 Lcd_Chr(1, 7, (day % 10) + 48);    // Print oness digit of day variable
 Lcd_Out(1, 9,mny);
 Lcd_out(1,13,"20");
 Lcd_Chr(1,15, (year / 10)  + 48);  // we can set year 00-99 [tens]
 Lcd_Chr(1,16, (year % 10)  + 48);  // we can set year 00-99 [ones]
 Lcd_Chr(2, 1, (hr / 10)  + 48);
 Lcd_Chr(2, 2, (hr % 10)  + 48);
 Lcd_Chr(2, 3,':');
 Lcd_Chr(2, 4, (min / 10) + 48);
 Lcd_Chr(2, 5, (min % 10) + 48);
 Lcd_Chr(2, 6,':');
 Lcd_Chr(2, 7, (sec / 10) + 48);
 Lcd_Chr(2, 8, (sec % 10) + 48);
 }
//-------------------Display Time in Set mode--------------------
char minute1,hour1,weekday1,month1;
char minute,hour,weekday,day1,month,year1;
void Display_Time_SetMode()
{
 switch(weekday1)
 {
  case 1: txt="Mon"; break;       // Monday;
  case 2: txt="Tue"; break;       // Tuesday;
  case 3: txt="Wed"; break;       // Wednesday;
  case 4: txt="Thu"; break;       // Thursday;
  case 5: txt="Fri"; break;       // Friday;
  case 6: txt="Sat"; break;       // Saturday;
  case 7: txt="Sun"; break;       // Sunday;
  }

 LCD_Out(1, 1,txt);
 LCD_chr(1, 4,',');

 Lcd_Chr(1, 6, (day1 / 10)   + 48);    // Print tens digit of day variable
 Lcd_Chr(1, 7, (day1 % 10)   + 48);    // Print oness digit of day variable
 Lcd_chr(1,10, (month1 / 10) + 48);    // Print tens digit of month variable
 Lcd_chr(1,11, (month1 % 10) + 48);    // Print oness digit of month variable
 Lcd_out(1,13,"20");
 Lcd_Chr(1,15, (year1 / 10)  + 48);    // Print tens digit of year variable
 Lcd_Chr(1,16, (year1 % 10)  + 48);    // Print oness digit of year variable
 Lcd_Chr(2, 1, (hour1 / 10)  + 48);    // Print tens digit of hour variable
 Lcd_Chr(2, 2, (hour1 % 10)  + 48);    // Print oness digit of hour variable
 Lcd_Chr(2, 3,':');
 Lcd_Chr(2, 4, (minute1 / 10) + 48);   // Print tens digit of minute variable
 Lcd_Chr(2, 5, (minute1 % 10) + 48);   // Print oness digit of minute variable
 Lcd_Chr(2, 6,':');
 Lcd_Chr(2, 7, (0 / 10) + 48);
 Lcd_Chr(2, 8, (0 % 10) + 48);
}

char SPos;
//----------------------Move cursor routine----------------------
char index;
void movecursor()
{
 char i,moveto;
 if(SPos==0)
 lcd_cmd(_lcd_first_row);  // set weekday;
 if(SPos==1)
 lcd_cmd(_lcd_first_row);  // set day;
 if(SPos==2)
 lcd_cmd(_lcd_first_row);  // set month;
 if(SPos==3)
 lcd_cmd(_lcd_first_row);  // set year;
 if(SPos==4)
 lcd_cmd(_lcd_second_row); // set hours;
 if(SPos==5)
 lcd_cmd(_lcd_second_row); // set minutes;

 moveto = 2;
 switch(index)
 {
  case 0: moveto = 2;break;
  case 1: moveto = 6;break;
  case 2: moveto =10;break;
  case 3: moveto =15;break;
  case 4: moveto = 1;break;
  case 5: moveto = 4;break;
  }
  for(i=1; i<= moveto; i++)
  lcd_cmd(_lcd_move_cursor_right);
}
//------------Start Buttons routine--------------;
char setuptime=0;
void Press_Switch()
{
 if(setuptime)
 {
  if(Button(&portc,2,1,0))         // If buttons at port c2 is pressed
  {
   delay_ms(200);
   SPos++;
   if(SPos>5)
   SPos=0;
   index++;
   if(index > 5)
   index=0;
   movecursor();
   }
  //-----------------------------case mode to set all values---------------------
  switch(SPos)
  {
   case 0: if(button(&portc,0,1,0))       // If buttons at port c0 is pressed
           {
            Delay_ms(200);
            weekday1++;
            if(weekday1 > 7)
            weekday1=1;
            Display_Time_SetMode();
            index=0;
            movecursor();
            }

           if(button(&portc,1,1,0))       // If buttons at port c1 is pressed
           {
            Delay_ms(200);
            weekday1--;
            if(weekday1 < 1)
            weekday1=7;
            Display_Time_SetMode();
            index=0;
            movecursor();
            }
           break;
   case 1: if(button(&portc,0,1,0))       // If buttons at port c0 is pressed
           {
            Delay_ms(200);
            day1++;
            if(day1 > 31)
            day1 = 1;
            Display_Time_SetMode();
            index=1;
            movecursor();
            }

           if(button(&portc,1,1,0))        // If buttons at port c1 is pressed
           {
            Delay_ms(200);
            day1--;
            if(day1 < 1)
            day1 = 31;
            Display_Time_SetMode();
            index=1;
            movecursor();
            }
           break;
   case 2: if(button(&portc,0,1,0))         // If buttons at port c0 is pressed
           {
            Delay_ms(200);
            month1++;
            if(month1 > 12)
            month1 = 1;
            Display_Time_SetMode();
            index=2;
            movecursor();
            }

           if(button(&portc,1,1,0))          // If buttons at port c1 is pressed
           {
            Delay_ms(200);
            month1--;
            if(month1 < 1)
            month1 = 12;
            Display_Time_SetMode();
            index=2;
            movecursor();
            }
           break;
   case 3: if(button(&portc,0,1,0))           // If buttons at port c0 is pressed
           {
            Delay_ms(200);
            year1++;
            if(year1 > 99)
            year1 = 1;
            Display_Time_SetMode();
            index=3;
            movecursor();
            }

           if(button(&portc,1,1,0))           // If buttons at port c1 is pressed
           {
            Delay_ms(200);
            year1--;
            if(year1 < 1)
            year1 = 99;
            Display_Time_SetMode();
            index=3;
            movecursor();
            }
           break;
   case 4: if(button(&portc,0,1,0))            // If buttons at port c0 is pressed
           {
            Delay_ms(200);
            hour1++;
            if(hour1 > 23)
            hour1 = 0;
            Display_Time_SetMode();
            index=4;
            movecursor();
            }

           if(button(&portc,1,1,0))            // If buttons at port c1 is pressed
           {
            Delay_ms(200);
            hour1--;
            if(hour1 > 23)
            hour1 = 0;
            Display_Time_SetMode();
            index=4;
            movecursor();
            }
           break;
   case 5: if(button(&portc,0,1,0))            // If buttons at port c0 is pressed
           {
            Delay_ms(200);
            minute1++;
            if(minute1 > 59)
            minute1 = 0;
            Display_Time_SetMode();
            index=5;
            movecursor();
            }

           if(button(&portc,1,1,0))             // If buttons at port c1 is pressed
           {
            Delay_ms(200);
            minute1--;
            if(minute1 > 59)
            minute1 = 0;
            Display_Time_SetMode();
            index=5;
            movecursor();
            }
           break;
   }                            // end "if is in switch mode"
  }                             // end "if is in setup"

 if(button(&portc,5,1,0))                      // If buttons at port c5 is pressed
 {
  Delay_ms(200);
  setuptime = !setuptime;
  if(SetupTime)
  {
   lcd_cmd(_lcd_clear);
   lcd_cmd(_lcd_blink_cursor_on);
   weekday1=week_day;
   hour1=hr;
   minute1=min1;
   day1=day;
   month1=mn;
   year1=year;
   Display_Time_SetMode();
   SPos=0;
   index=0;
   movecursor();
   }
   else
   {
   Lcd_Cmd(_Lcd_clear);
   lcd_cmd(_lcd_cursor_off);
   weekday=weekday1;
   hour=hour1;
   minute=minute1;
   day=day1;
   month=month1;
   year=year1;
   Write_time(minute,hour,weekday,day,month,year);
   }
  }
 }
//----------------------End Buttons Routine-------------------
//------------------Temperature sensor routines---------------
const unsigned short TEMP_RESOLUTION = 12;     // 9 for DS1820 and 12 for DS18B20
char *text = "000,0";
unsigned temp;
void Display_Temperature(unsigned int temp2write)
{
 const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8;
 char temp_whole;
 unsigned int temp_fraction;
 unsigned short isNegative = 0x00;

 // Check if temperature is negative
 if (temp2write & 0x8000)
 {
  text[0] = '-';
  temp2write = ~temp2write + 1;
  isNegative = 1;
  }
  // Extract temp_whole
  temp_whole = temp2write >> RES_SHIFT ;
  // Convert temp_whole to characters
  if (!isNegative){
  if (temp_whole/100)
  text[0] = temp_whole/100  + 48;                // Extract hundreds digit
  else
  text[0] = '+';
  }
  text[1] = (temp_whole/10)%10 + 48;             // Extract tens digit
  text[2] =  temp_whole%10     + 48;             // Extract ones digit
  // Extract temp_fraction and convert it to unsigned int
  temp_fraction  = temp2write << (4-RES_SHIFT);
  temp_fraction &= 0x000F;
  temp_fraction *= 625;
  // Convert temp_fraction to characters
  text[4] =  temp_fraction/1000 + 48;         // Extract thousands digit
  // Print temperature on LCD
  Lcd_Out(2, 10,text);
  lcd_chr(2, 15,0xB2);                        // Ascii code for degrees symbol;
  Lcd_chr(2, 16,'C');                         // Show symbol "C" from Celsius
}
//----------------Read and display Temperature from DS18B20--------------
void Read18b20()
{
 //--- Perform temperature reading
 Ow_Reset(&PORTC, 7);                           // Onewire reset signal;
 Ow_Write(&PORTC, 7, 0xCC);                     // 0xCC Issue command SKIP_ROM;
 Ow_Write(&PORTC, 7, 0x44);                     // Issue command CONVERT_T;
 Delay_us(700);                                 // delay 0,7s (required for signal
                                                // processing);
 Ow_Reset(&PORTC, 7);                           // Onewire reset signal;
 Ow_Write(&PORTC, 7, 0xCC);                     // Issue command SKIP_ROM;
 Ow_Write(&PORTC, 7, 0xBE);                     // Issue command READ_SCRATCHPAD;

 temp = Ow_Read(&PORTC, 7);                     // Next Read Temperature, read Byte
                                                // 0 from Scratchpad;
 temp = (Ow_Read(&PORTC, 7) << 8) + temp;       // Then read Byte 1 from Scratchpad
                                                // and shift 8 bit left and add the Byte 0;
 //--- Format and display result on Lcd
 Display_Temperature(temp);                     // Call Display_Temperature;
 }
//------------------Temperature sensor routines---------------
void Init_Main()
{
 CMCON |=7;             //TURN OFF ANALOGUE COMPARATOR AND MAKE PORTA TO DIGITAL I/O;

 I2C1_Init(100000);         // initialize I2C
 Lcd_Init();                // Initialize LCD
 Lcd_Cmd(_LCD_CLEAR);       // Clear LCD display
 Lcd_Cmd(_LCD_CURSOR_OFF);  // Turn cursor off

 Display_Time(sec, min1, hr, week_day, day, mn, year);

 !setuptime=1;
 index=0;
 SPos=0;
 }
//-----------------Here we have the Main Routine----------------
void main()
{
 Init_Main();
 while (1)                                                 // While loop
 {
  Read_Time(&sec,&min1,&hr,&week_day,&day,&mn,&year);      // read time from RTC(DS1307)
  Transform_Time(&sec,&min1,&hr,&week_day,&day,&mn,&year); // Transform time
  Press_Switch();                                          // Check buttons;
  if(!setuptime)
  {
   Display_Time(sec, min1, hr, week_day, day, mn, year);
   Read18b20();
   }
  }
}

12 comments:

  1. please the mesure for the variable resistor !

    ReplyDelete
    Replies
    1. Hy Mr. Babi, the value for that variable resistor is 10k Ohm.

      Delete
  2. Hello Ducu. This project can be implemented on a PIC16F877A?

    ReplyDelete
    Replies
    1. Yes, It can be implemented with that microcontroller model.

      Delete
  3. Hello,

    I would like to make this clock, but I can not be programmed pic. My programmer knows only the hex. I do not know about programming, maybe you can put a hex file?
    I am very grateful, Thanks

    ReplyDelete
  4. can you please send me the hex code

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Thanks for Sharing!!

    Illini Foundry is a reputable brass foundry located in Peoria, Illinois. The company specializes in producing high-quality brass castings for a variety of industries, including agriculture, construction, mining, and transportation. If you are looking for a reliable brass foundry in Peoria, IL, that can deliver high-quality castings with exceptional service, look no further than Illini Foundry. Contact them today to learn more about their capabilities and how they can help you achieve your goals.

    ReplyDelete
  7. Thanks for Sharing!!

    George Stevens Manufacturing offers high-quality transformer coil winding machines that are revolutionizing the industry. Our machines are designed with precision engineering and the latest technology to ensure the highest levels of performance and reliability. It is responsible for winding the copper wire around the transformer core to create the necessary electrical conductance. Without a reliable coil winding machine, the manufacturing process would be slow, inefficient, and prone to errors. Visit the website.

    ReplyDelete

If you do not understand something, or if you make some aplication helped by this blog, let me know.