Digital Clock using PIC Microcontroller and the DS1307 Real Time Clock – XC8 Compiler
A Digital Clock can be made easily by using PIC Microcontroller and the DS1307 real time clock with an LCD or seven segment display.
The DS1307 is a low power serial real time clock/calender from Maxim Integrated with full binary coded decimal (BCD) clock/calendar plus 56 bytes of Non Volatile Static Random Access Memory.
Data and Address are transferred serially through a bidirectional I2C bus. The RTC provides year, month, date, hour, minute and second information. The end date of months is automatically adjusted for months fewer than 31 days including leap year compensation up to year 2100. It can operate either in 24-hour format or 12-hour format with AM/PM indicator.
DS1307 comes with built-in power sensing circuit which senses power failures and automatically switches to back up supply. Timekeeping operation continues while the part operates from the backup supply.
The DS1307 RTC uses an external 32.768kHz Crystal Oscillator and it does not requires any external resistors or capacitors to operate.
The PIC 18F2620 has a built-in I2C (Inter-Integrated Circuit) bus, any suitable PIC Microcontroller with I2C bus can be used as well for this project.
Although a digital clock can be built without an external real time clock chip, by using only internal PIC timer, The DS1307 RTC and simillar chips makes the software easier as it takes care of all calendar functions, adjustment of for months with fewer than 31 days, accounting for leap years and other functions which could have make the code more complex. The other advantage of using the DS1307, it comes with built-in power sensing circuit which senses power failures and automatically switches to back up supply so it can keep the real time information when the main circuit loses power.
Before continuing, be sure to read through the interfacing the DS1307 real time clock with PIC microcontroller article.
Circuit diagram
As shown on the circuit diagram above, the DS1307 is connected to PORT C of the PIC which has built in I2C bus (pin 14 and 15 of the 18F2620). Connect two pull up resistors (R1 and R2) for the bus to work.
Connect a 3V backup battery (B1) to pin VBAT (pin 3) of the DS1307 to backup the device data but if this is not required, this pin can be grounded.
On our circuit, we are using internal oscillator of the PIC and the MCLR is disabled. If an external oscillator is needed, it can be connected to pins 9 and 10 and if the MCLR is needed to reset the PIC, it can be connected to positive supply via a 10K resisitor.
Three push buttons connected to PORT C as well are used to set the date and time, once the SET button is pressed, the device enters the setup mode. Pressing the UP button will increment the the Hour and pressing the DOWN button will decrement.
Pressing the SET button again will move the cursor to Minutes, pressing again to seconds than to day and so on.
A 16 x 2 lines LCD display is connected to PORT B. refer to the Interfacing LCD Display with PIC microcontroller article to learn more.
The I2C debugger is shown on the circuit for simulation purpose only.
MPLAB XC8 Code
MPLAB XC8 compiler is used to write the code.
MPLAB XC8 provides built-in libraries for I2C devices which make it easy to read/write date and time information to/from the DS1307 chip.
Registers containing the date and time information can be obtained by implementing a START and followed by device identification address. Then each registers can be accessed sequentially by using its address until a STOP condition is executed.
Device Address of the DS1307 is 0x68 = 1101000 (page 12 of datasheet).
More details can be found from the DS1307 Datasheet.
The PIC18 Peripheral Library help document found inside the installation folder of CX8 compiler contains the full description of the functions and macros to write or read from the I2C bus.
DS1307 RTC is fully Binary Coded Decimal (BCD) clock/calender. So the data read from DS1307 should be converted to required format according to our needs and data to be written to DS1307 should be in BCD format as well.
MPLAB CX8 Library functions for Interfacing LCD With PIC Microcontroller need Character or String Data. So data to be displayed in the LCD Screen should be converted to Character.
Addition and Subtraction cannot be directly applied on BCD when updating the time and date. The functions to update data should convert it first from BCD to Binary and vice versa when needed.
The BCD2Binary() function convert BCD to Binary so we can do some basic calculations and the Binary2BCD() function convert Binary to BCD so we can write to the DS1307 Register in BCD.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
int BCD2Binary(int a) //Convert BCD to Binary so we can do some basic calculations { int r,t; t = a & 0x0F; r = t; a = 0xF0 & a; t = a >> 4; t = 0x0F & t; r = t*10 + r; return r; } int Binary2BCD(int a) //Convert Binary to BCD so we can write to the DS1307 Register in BCD { int t1, t2; t1 = a%10; t1 = t1 & 0x0F; a = a/10; t2 = a%10; t2 = 0x0F & t2; t2 = t2 << 4; t2 = 0xF0 & t2; t1 = t1 | t2; return t1; } |
Functions that are used for reading (read_ds1307()) and writing (write_ds1307()) data from DS1307 are explained in great detail in the article Interfacing the DS1307 with PIC Microcontroller please refer it.
The define MSB() and the define LSB() are used to convert BCD to Character.
1 2 |
#define MSB(x) ((x>>4)+ '0') //Display Most Significant Bit of BCD number #define LSB(x) ((x & 0x0F) + '0') //Display Least Significant Bit of BCD number |
Hour, Minute, Second, Date, Month and Year data are stored in DS1307 in separate 8-bit registers in BCD format as shown in the DS1307 Datasheet on page 8. We read the data of these registers to access time/date. The define MSB() converts most significant 4 bits to corresponding character and the define LSB() converts least significant 4 bits to corresponding character. When writing to the hour register, Bit 6 is used to select the 24-hour or 12-hour mode selection bit. When this bit is made high (1), 12-hour mode is selected and Bit 5 will represent AM/PM (Logic High represents PM).
Full Code:
Important:
If you are using XC8 v1.35, the Peripheral Libraries which include the LCD and other peripherals like ADC, SPI, I2C libraries are no longer included in the installation file as with previous versions. You can either write your own LCD library and use MPLAB code configurator to configure the other peripherals or the easiest is to download the Peripheral Libraries as a separate download and install them,. You can download them at microchip website under the MPLAB XC Compilers downloads tab. They are now called PIC18 Legacy Peripheral Libraries.
Download: PIC18 Legacy Peripheral Libraries
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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
// /* * File: RTC.c * Author: Student Companion SA: www.studentcompanion.co.za * Real Time Clock Using DS1307 on I2C Bus * To simulate in Proteus, The I2C debugger must be connected on the I2C Bus. */ #include "RTC.h" #include <stdio.h> #include <stdlib.h> #include <delays.h> #define MSB(x) ((x>>4)+ '0') //Display Most Significant Bit of BCD number #define LSB(x) ((x & 0x0F) + '0') //Display Least Significant Bit of BCD number void DelayFor18TCY(void); //Delay for 18 cycles. void DelayPORXLCD(void); //Delay for 15 ms. void DelayXLCD(void); //Delay for 5 ms. void XLCD_init(void); //Initialize the LCD display unsigned short read_ds1307(unsigned short addressReg); //Function to read from the DS1307 void write_ds1307(unsigned short addressReg,unsigned short w_dataReg); //Function to write to the DS1307 int Binary2BCD(int aa); //Convert Binary to BCD int BCD2Binary(int aa); //Convert BCD to Binary //*** Global variables **** char time[] = "00:00:00 PM"; char date[] = "00-00-00"; int second; int minute; int hour; int hr; int day; int dday; int month; int year; int ap; unsigned short set_count = 0; short set; void main(void) { OSCCON=0x76; //Configure internal oscillator XLCD_init(); // Initialize LCD OpenI2C(MASTER, SLEW_OFF);// Initialize I2C module SSPADD = 45; //100kHz Baud clock @8MHz = 19 //SSPADD = (FOSC/ Bit Rate) /4 - 1 //check for bus idle condition in multi master communication IdleI2C(); StartI2C(); while ( SSPCON2bits.SEN ); putrsXLCD("Time:"); SetDDRamAddr(0x40); putrsXLCD("Date:"); do{ //Read Time and Date, when simulating with proteus, it will pick up the system Clock //from your PC and might display the AM if your PC time is in 24Hrs mode hour= read_ds1307(0x02); //Read Hour hr = hour & 0b00011111; ap = hour & 0b00100000; minute= read_ds1307(0x01); //Read minutes second= read_ds1307(0x00); //Read seconds dday= read_ds1307(0x03); //Read The day of the week (sunday to saturday) day= read_ds1307(0x04); //Read The day of the week (1st to 31st) month= read_ds1307(0x05); //Read month year= read_ds1307(0x06); //Read year time[0]=MSB(hr); time[1]=LSB(hr); time[3]=MSB(minute); time[4]=LSB(minute); time[6]=MSB(second); time[7]=LSB(second); date[0]=MSB(day); date[1]=LSB(day); date[3]=MSB(month); date[4]=LSB(month); date[6]=MSB(year); date[7]=LSB(year); if(ap) { time[9] = 'P'; time[10] = 'M'; } else { time[9] = 'A'; time[10] = 'M'; } //Display Time and Date in 12Hrs Mode (with PM or AM) SetDDRamAddr(0x05); putrsXLCD(time); SetDDRamAddr(0x45); putrsXLCD(date); //Write Time and Date: //1. To set the time, press the SET pushbutton once to select the Hour. //2. Press the UP button to increament the Hour value or DOWN Button to decreament. //3. Press the SET Button again to move to the right to modify the minute. //4. Note when SET is in second position, pressing UP or Down will only reset the second value to 00 //5. Continue doing the same to set the date as well. //6. Pressing the SET Button for the 7th time will exit the SET up mode set = 0; if(PORTCbits.RC0 == 0) { for(int c=0;c<=20;c++)__delay_ms(5); //wait for 100ms if(PORTCbits.RC0 == 0) { set_count++; if(set_count >= 7) { set_count = 0; } } } if(set_count) { if(PORTCbits.RC1 == 0) { for(int c=0;c<=20;c++)__delay_ms(5); //wait for 100ms if(PORTCbits.RC1 == 0) set = 1; } if(PORTCbits.RC2 == 0) { for(int c=0;c<=20;c++)__delay_ms(5); //wait for 100ms if(PORTCbits.RC2 == 0) set = -1; } if(set_count && set) { switch(set_count) { case 1: hour = BCD2Binary(hour); hour = hour + set; hour = Binary2BCD(hour); if((hour & 0x1F) >= 0x13) { hour = hour & 0b11100001; hour = hour ^ 0x20; } else if((hour & 0x1F) <= 0x00) { hour = hour | 0b00010010; hour = hour ^ 0x20; } write_ds1307(2, hour); //write hour break; case 2: minute = BCD2Binary(minute); minute = minute + set; if(minute >= 60) minute = 0; if(minute < 0) minute = 59; minute = Binary2BCD(minute); write_ds1307(1, minute); //write min break; case 3: if(abs(set)) write_ds1307(0,0x00); //Reset second to 0 sec. and start Oscillator break; case 4: day = BCD2Binary(day); day = day + set; day = Binary2BCD(day); if(day >= 0x32) day = 1; if(day <= 0) day = 0x31; write_ds1307(4, day); // write date 17 break; case 5: month = BCD2Binary(month); month = month + set; month = Binary2BCD(month); if(month > 0x12) month = 1; if(month <= 0) month = 0x12; write_ds1307(5,month); // write month 6 June break; case 6: year = BCD2Binary(year); year = year + set; year = Binary2BCD(year); if(year <= -1) year = 0x99; if(year >= 0x50) year = 0; write_ds1307(6, year); // write year break; } } } } while(1); } unsigned short read_ds1307(unsigned short address) //call this function to read date and time //from the date and time registers. { char r_data; StartI2C(); // Start condition I2C on bus IdleI2C(); WriteI2C( 0xD0 ); // addresses the chip IdleI2C(); WriteI2C( address ); // write register address IdleI2C(); StopI2C(); // Stop condition I2C on bus RestartI2C(); // Start condition I2C on bus IdleI2C(); WriteI2C( 0xD1 ); // addresses the chip with a read bit IdleI2C(); r_data = ReadI2C(); // read the value from the RTC and store in result IdleI2C(); NotAckI2C(); // Not Acknowledge condition. IdleI2C(); StopI2C(); // Stop condition I2C on bus return (r_data); } void write_ds1307(unsigned short address,unsigned short w_data) //call this function to write date and time //to the date and time registers. { StartI2C(); // Start condition I2C on bus IdleI2C(); WriteI2C( 0xD0 ); // addresses the chip IdleI2C(); WriteI2C( address ); // write register address IdleI2C(); WriteI2C( w_data ); // write register address IdleI2C(); StopI2C(); // Stop condition I2C on bus } int Binary2BCD(int a) //Convert Binary to BCD so we can write to the DS1307 Register in BCD { int t1, t2; t1 = a%10; t1 = t1 & 0x0F; a = a/10; t2 = a%10; t2 = 0x0F & t2; t2 = t2 << 4; t2 = 0xF0 & t2; t1 = t1 | t2; return t1; } int BCD2Binary(int a) //Convert BCD to Binary so we can do some basic calculations { int r,t; t = a & 0x0F; r = t; a = 0xF0 & a; t = a >> 4; t = 0x0F & t; r = t*10 + r; return r; } void XLCD_init(void){ //Initialize LCD display OpenXLCD(FOUR_BIT&LINES_5X7); while(BusyXLCD()); WriteCmdXLCD(0x06); // move cursor right, don?t shift display WriteCmdXLCD(0x0C); //turn display on without cursor putrsXLCD("Real Time Clock"); SetDDRamAddr(0x44); putrsXLCD("Starting"); for(int c=0;c<=20;c++)__delay_ms(60); SetDDRamAddr(0x44); putrsXLCD("Starting."); for(int c=0;c<=20;c++)__delay_ms(50); SetDDRamAddr(0x44); putrsXLCD("Starting.."); for(int c=0;c<=20;c++)__delay_ms(50); SetDDRamAddr(0x44); putrsXLCD("Starting..."); for(int c=0;c<=20;c++)__delay_ms(50); WriteCmdXLCD(0x01); //Clear Screen WriteCmdXLCD(0x02); //Go Home } void DelayFor18TCY( void ){ Delay10TCYx(20); //18 cycles delay } //***************************************** void DelayPORXLCD (void){ Delay1KTCYx(15); // Delay of 15ms return; }//End DelayPORXLCD //***************************************** void DelayXLCD (void){ Delay1KTCYx(5); // Delay of 5ms return; } |
You can download the full project files (MPLAB XC8 source code and Proteus Schematic design) below here. All the files are zipped, you will need to unzip them (Download a free version of the Winzip utility to unzip files).
Download Digital Clock Header
Download Digital Clock HEX
Download Digital Clock Main C
Download Digital Clock Proteus