The Real Time Clock (RTC) chip produced by Maxim is a popular and relatively low cost solution for creating a room clock.
Features:
- 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.
- 56-Byte, Battery-Backed, General-Purpose RAM with Unlimited Writes.
- I2C Serial Interface.
- Programmable Square-Wave Output Signal.
- Automatic Power-Fail Detect and Switch Circuitry.
- Consumes Less than 500nA in Battery-Backup Mode with Oscillator Running.
- Operating temperature range for commercial use only: 0°C to +70°C.
- Optional Industrial Temperature Range: -40°C to +85°C.
- Available in 8-Pin Plastic DIP or SO.
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.
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.
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.
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();
}
}
}