Note: If you typed DS1703 Real Time Clock to find this page you probably mis-spelled the chip type.
Anyway you can find a DS1307 (RTC) Real Time Clock IC project and information on this page.
Note: This RTC project has been updated with easier to use software i.e. the software loaded into the PIC (Note the compiler is free for <2k and currently the code uses about 1550 bytes). There are also two additional modes one for information and one for debugging:
The first new mode is found when pressing key 3 which will display an indication of what the current display is showing (sometimes it is hard to figure that out by just looking at numbers!). The second is when pressing key 4 which will cycle through all the RA0-RA7 output pins and show the LEDs lit on the 7-segment display. This means it is easier to figure out if the RA0-RA7 connections are correct when you wire it up (Note RA5 is not used as it can only be an input - see detail below). The project has also been updated to use the latest MikroC compiler 6.0.1
|Accuracy||Watch crystal spec typically 20ppm|
|Compiler||Mikroelectronika MikroC Compiler Free!|
|Target||16F88 (re-targettable to other PICs that have Analogue input AN0).|
|Software notes||Switching between i/p & o/p to read analogue/drive display. Using I2C routines.|
|Hardware notes||Special care must be taken in placing the DS1307 and the crystal.|
your details to get the Download Link
and get the microcontroller newsletter:
(Note: Your email is safe it will never
be sold or rented).
Although the PIC16F88 has a built in oscillator for a 32kHz watch crystal a DS1307 is easier to use on a bread board. This is because you can control the layout of the circuit more easily.
The RTC also makes the software easier as it takes care of all calendar functions; accounting for leap years etc.
The DS1307 (RTC) Real Time Clock IC (an I2C RTC) is an 8 pin device using an I2C interface (although the data sheet does not mention I2C to avoid royalty payments!). It has 8 read/write registers that store the following information:
Note: Addresses 0x08 to 0xf3 are user RAM and if you use a backup battery these are then non volatile ram i.e. they will save their contents after the power is off - so you have an extra 56 bytes of ram to play with! less the one used for storing the year high digits at 0x20- you could change this to not use the RAM but it is also used as an initialisation check - see the code.
Note: Address 3f is used in this project as a check to see if the clock needs initialising and to store the upper year digit (for easier coding).
The last address 0x08 is the CONTROL address and it determines what is generated at the SQW/OUT pin. You can control the level directly via I2C or set it to 1Hz, 4096Hz, 8192Hz, or 32768kHz. In this software it is set to 1Hz and used to drive an LED that can be used as a back light for the 4x7-segment module (if you shine a light through the module you'll see the two central holes (like a colon character) that are between the left and right sets of two 7-segments. This is usually used to flash seconds so placing the LED behind this will achieve that operation.
In the same way as the I2C pins you need to add a pull-up to V+ at the SQW/OUT pin to see any output signal as it is an open drain output! or as in this circuit, an LED and 470R resistor are connected in series and to the +5V power. The other end goes to the SWQ/OUT pin of the DS1307.
There are two specific 'gotcha' type controls embedded in the addresses which make using the chip slightly more complicated.
The most important is the Clock Halt Bit (CH) which is bit 7 of address 0. This is the register that controls 'seconds' and the CH bit has to be preserved otherwise the chip stops the clock. Writing zero to this bit resets the CH bit so that the clock runs.
Note: You have to reset the CH bit to zero to let the chip operate!
Warning: The default state of the
DS1307 is undefined
so you must clear the CH bit to start the oscillator.
In general you should leave this bit at zero and only set it if you have to. This bit is contained within register zero which is also the "minutes" and "seconds" register. In general keep this bit at zero unless you are updating the seconds part of the register (you don't want the seconds changing while you are editing them).
The second is the 24/12 hour control which is bit 6 of address 2. It is set high for 12 hour mode and low for 24 hour mode. In this project it is set low for 24 hour mode.
The problem with these two bits is that you have to preserve them when accessing the registers to write data and ignore them when reading out values for display. Its not a big problem and you can see how it's done when you look at the code (see function edit_DS1307() and the 1st 2 case statements for address 0 (CH) and 2 (12H/24H) ).
Surprisingly making an accurate 32kHz oscillator is a difficult task (much more than a high speed oscillator e.g. a MHz crystal oscillator). This is because low speed oscillator drivers are designed for low power operation. That means high impedance and therefore low current which makes the driver extremely sensitive to noise (or any nearby signals which can capacitively couple to the crystal wire).
Using the DS1307 lets you put the crystal in the least noisy part of the board. In addition it sets the crystal load capacitance which is critical in making the crystal oscillate at exactly 32kHz - controlling its initial error i.e. for the specified ppm error value the load capacitance must be exact.
Note: A common way of calibrating a crystal (not in this project) is crystal pulling or changing the capacitance at one crystal pin relative to the other - so load capacitance is crucial.
The DS1307 loads the crystal with 12.7pF so you need to buy a crystal that is defined to use this load capacitance. Circuit layout also affects the capacitance at the crystal pins so you must keep the crystal as close as possible to the chip and the tracks from crystal to chip must be short.
To ensure the crystal oscillates correctly you must ensure that :
If you are doing a board layout there is good advice in the PIC 16F88 datasheet (Timer 1 section) on crystal pcb guard rings. Dallas recommends Application note 58 which I have not read yet.
The DS1307 detects a power failure if its input voltage (Vcc) falls below (VBat) and automatically switches to the Vbat supply input (you should use a lithium 3V battery here as the backup battery). It also inhibits I2C control signals until Vcc is 1.25 x Vbat so you won't be able to put bad data into the chip as the power is failing!
To save microcontroller pins there are four input keys which are all connected to a single analogue input pin. This pin also drives one of the seven segment display LEDs so it has to be switched between input (to read the analogue voltage) and output (to drive the led).
Note: This works because the analogue input is switched to receive analogue for only (150us approx) there is a 100us delay to let the inputs settle (just a guess and could be made lower - but it is not critical in this application). Then the input signal is read. Because of persistence of vision (on which the entire display methodology depends) your eye can not see this missing light pulse so it looks like the display is completely steady!
Each key pulls the analogue input to a different voltage level which you can easily read using the ADC (RA0).
The display is made up of four 7 segments built into a convenient block where all 8 led drive lines are connected together. You could wire up individual 7-segments to achieve the same result but doing it on a soldered breadboard is a pain. The block just saves you effort (and errors in wiring). So this results in 8 data lines and 4 digit select lines.
Initially I used RA0 to RA7 as the data line drivers and RB2,5,6,7. Then I realised that RA5 is only allowed to be an input because it is also multiplexed with the reset pin MCLR. Since it had been wired up this way the simplest solution was to eliminate RA5 but how do you do that without changing all the drive data definitions?
When eliminating RA5 I moved RA6 and RA7 down to bits b5 and b6 so this leaves the LED drive for the decimal point unconnected but that is OK since we are not using that part of the 7-segment display.
So the solution is to use a helper function that maps the bits 6,7 to bits 5,6 just before outputting the data to the 7-segment LED bus and the function has an obvious name:
The digit select lines are each driven by a transistor switch that allows more current if needed ( note I have not measured the exact current drawn as it depends on the refresh rate of the display).
Yes there are none! i.e. between the RA bus and the LED drives there are no limit resistors.
In all other circuits you can see them. I chose not to use them since the display is only ever driven in refresh mode. If it were driven directly then the LEDs would blow up.
If you are uncomfortable with this then add 8 off of 330R or 470R resistors to limit the maximum current to each LED (e.g. if the are driven permanently during testing) from each drive RA bus to the 4x7-segment display.
The reason that it works is to do with heat generated - as the current flows in the LED the junction heats up and if you drive too much current then it melts the internal wires - which is where the max. current limit comes from.
When driving the display in refresh mode the display is held on for a very short time compared to the time it is held off and this defines an average current that is within the current limit of each LED.
Hey.. don't knock it... it works fine...promise.
Note: The idea comes from chip design where the current drawn by the device depends on the switching speed of the system since for FETs maximum current is drawn when both upper and lower FETs in a switch are on i.e. during the transition from current level to next i.e. when switching.
When the system powers up the ram location 0x3f is checked for value 0x20.
If this exists then it means that the backup battery has saved contents of the
RAM and all the registers have therefore been initialised. So the software
skips the initialisation sequence.
If 0x20 is not found then the Real Time Clock IC is initialised to the following values:
|0 Seconds||0x59 (bit 7 cleared).|
|2 Hours||0x24 (bit 6 cleared).|
|6 Year||0x99 (low digits)|
|3f Year||0x20 ( high digits)|
These are all Binary Coded Decimal numbers which the Real Time Clock IC works with. Note that the initialisation values have been chosen so you can easily understand what the display is showing but from V1.02 this is not as important as you can hit key 3 to get pseudo text on what is displayed.
However when re-visiting the project it turns out that it is a difficult task (figuring out exactly what is on the display especially if it is 8.20pm in 2014 i.e. all the numbers are identical) which is why pressing key 3 (when not editing a value) flashes the display at a flicker (slow) rate and indicates what you are looking at with the following 7-segment outputs.
|MODE_HRS_MINS||Hrnn||Hours and minutes|
|MODE_MINS_SECS||nnSe||Minutes and seconds|
|MODE_DAY_MON||dAnn||Day and Month|
Pressing mode button cycles the display showing different data after each button press. The following display sequence is followed:
To change a value the Edit button must be pressed (Key2) . When it is the left hand two digits are flashed on and off and the Up (key3) and Down (Key4) buttons now let you edit the value. Hitting Edit again flashes the two right hand digits and you can change the value as before. Hitting the Edit button or the Mode button again then exits the edit mode.
The clock is only stopped when editing the Minutes/Seconds display and restarted at the end of the edit sequence or when the Mode button is hit (the routine check_start_ds1307 will not change the state or write to the oscillator register if the clock is already running i.e. the clock oscillator is only started if it was previously stopped). This lets you set the seconds accurately but does not disturb the clock in other modes.
Note I am not sure why but it may be how the DS1307 works you have to set the time 1 second in advance of the desired transition time.
say the clock currently read 1328 13 minutes and 28 seconds to set the time fairly well edit the time to 13 45 then hit the edit key when your clock you are trying to synchronize to is at 1344. This seems to get the seconds changing at the same time as the source clock. This is probably due to the double register set within the DS1307 updating only at a specific time.
"Right Click Here" to Download the schematic of the Real Time Clock. Either right click and choose save as or click to open in browser (if you have enabled a pdf reader plug-in).
You can use any PIC microcontroller that has an ADC and enough memory to hold the program and enough pins for the display multiplexing.
You can program the PIC in circuit through the ICSP connector even while the program is running and since there is no I2C communication the correct time will be maintained as the DS1307 is not updated.
There is nothing really difficult about the project except for placing the DS1307 away from noise sources and decoupling it as mentioned earlier.
Compiler project files
There are a lot more for 6.4.0 (see the download file).
C Source files.
bit.h - bit manipulation macros
I2C.h - definitions for I2C operation.
This contains the main routine and support functions for
controlling the DS1307.
This contains the software implementation of I2C (clock stretching has not been tested). To change the PORTs/pins alter the #define statements at the start of I2C.c
These are all self explanatory.
Converts a number into the required output value for PORTA to drive the
seven segment display. The internal array ret holds the 7-segment values -
these are pure 8 bit values as if all data lines to the 7-segments are
connected. The routine:
... moves bits around in a byte to account for the fact that RA5 is not used.
This routine decodes the analogue levels into a specific key press. it returns a value from 1-4 representing keys 1-4. On the schematic key1 is pb1 and should be located on a board at the top.
This calls decode_ANA_keys() to determine the key pressed.
In this routine the analogue input is turned on, a delay of 100us is used, then the key decoded, then the analogue input returned to a digital output.
Note how this is using the RA0 pin as both an analogue input pin and as a digital output pin.
This works for two reasons:
Note: If this was interrupt driven code the portA (LED drives) writing interrupt would be required to read the analogue port so that the pin direction was not changed during a read i.e. you would use that interrupt routine since it would know when it is safe to read the analogue value.
Because it is not interrupt driven the code is always following a set order of operation therefore it can never turn the port back to an output while the analogue pin is being read.
Your eye can not see the difference in 100us when the port is read - it really just extends the led off period by 100us.
This is a simple range detect that returns the key pressed if the analogue value falls within a specific range.
This is used in the "main" routine to allow up/down edit of the current DS1307 register values. It takes as input the DS1307 address and direction desired and increments or decrements the register value depending on dir and also limits the value that is updated to keep it in a valid range.
Note how it manages the 'problem' addresses that contain CH and 12/24 Hour bits as well as the desired value.
Also note how the 'one' routine uses different upper limit and lower limit controls to control operation for individual registers i.e. depending on register use the values are constrained to specific limits.
This function provides the main display ability of the system and it works slightly differently to traditional code. This code is designed to be re-entrant and to do a different job every time it is called it is effectively a state machine with the state stored by the static variable 'digit'.
Each time it is called digit is incremented and this chooses the next segment driver pin on PortB.
Since the function also takes two binary coded values as arguments (one for the left two 7-segment digits and one for the right two 7-segment digits). It can display a different digit every time the function is called. So by calling the function fast enough the 4x7-segment display appears to be constantly on due to persistence of vision.
The complexity in this routine is for selectively blanking left or right pairs of digits so that they can be flashed on and off. Other complexity is preserving the control bits at addresses 0x00 and 0x02.
The array idx holds two values that are the addresses into the DS1307
clock address space. idx holds an address that allows display on the left
two 7-segments while idx holds an address that is used to display data on
the right two 7-segments.
By changing the idx values, specific register values in the DS1307 can be displayed. For instance the code for selecting hours and minutes to be displayed is :
This lets the display routine use DS1307 address 2 for the left digits and DS1307 address 1 for the right digits. If you look at the address map of the DS1307 address 2 is used to hold the hour information while address 1 is used to hold the minutes information.
...is used to update the display using this information.
The variable 'edit' is used both as a flag to indicate that editing is in progress and as an index into the idx array to specify which or the two array values is being incremented or decremented. If 'edit' has the value FINISHED_EDIT (=-1) then editing is not taking place or it has just finished (by the user pressing key two after the right hand digit set has been edited.
You can see how this works in the two case statements case 3 : and case : 4 for up and down editing of a value respectively. If not editing then the test() function or display_mode() function is called instead.
Flashing a digit set is performed by controlling the blankIdx value at the end of the main() function. If it is -1 then no value is displayed using the function clock_display(). The actual timing is controlled by the variable 'flash' that is incremented every pass round the while loop. 'flash' is kept to simple values of 60 and 30 and half the time (up to 30 the digit is on while it is off for the other half).
In main keys are detected and a
value returned in the range 1-4
|Key||Key function||key action|
|1||Change mode||Selects the displayed data e.g. Hours minutes, year etc.|
|2||Enter edit mode||Enter edit mode for the current data display and edit left.|
|2||If in edit mode||move to next digit or exit mode.|
|3||If in edit mode||Increase the selected digits value.|
|3||Not in edit mode||Display Pseudo-text indicating what is on the display.|
|4||If in edit mode||Decrease the selected digits value.|
|4||Not in edit mode||Enter test mode (each press moves to display diff. led)|
Note "Not in edit mode" are extra features.
You can use this to see if the 4x7-segment connections are correct.
This routine is executed when not in edit mode and key 4 is pressed.
At each press of key4 a different led is lit (the same one in each individual 7-segment).
Note: The display is flickered at a low rate to indicate test mode.
The order is:
This routine is similar to test except that it takes in a mode value that is the one being used in the main routine. It displays pseudo-text on the display to indicate what the current display is showing.
This routine is executed when not in edit mode and key 3 is pressed.
See Pseudo Text
Display above for the pseudo-text displayed on the 4x7-segments.
Apparently it is common practice for compilers to automatically include some
simple functions and I used them a while ago and could not figure how they were
linked into the code since there was no include statement for the following
These are rather irritating since there is inconsistency in their definition 'To' and '2' in the middle. However they are useful and get the job done. You can search online and find how others have defined these if you want to. For the definitions of their operation and prototype definitions search in the MikroC help system.
The update rate is controlled within clock_display() and its
delay function call to delay_ms(1) within that routine and it only turns on the
display for 1ms but this is for one digit. The routine clock_display() is
effectively a re-entrant state machine and on each call services a different
7-segment digit. So effectively the total delay throughout a complete cycle (of
4 calls as there are 4 digits) is 4ms.
At 8MHz the routines are doing a lot and you need 20ms refresh rate to achieve a stable persistence of vision so the rest of the system must be going at about 16ms (not measured) since the output seems stable enough. That means there is not a lot of room to do 'other' stuff.
This is because we are using the internal clock at 8MHz. You can go far faster using an external crystal but you only need to do this if you really have to. If you do need faster operation you may run out of pins, since two would be used to drive the xtal oscillator, which is why on other projects on the site, such as the 7-segment frequency counter, a helper chip is used - in this case it is the 4017 (Johnson counter - walking one). You could also use a serial to parallel chip such as the 595 to save pins.
However for the task it is required to do at the moment, it works just fine.
Note : The interesting way of using the RA0 pin as both a digital output and as an analogue input. See above discussion on this.
And thats it - enjoy.
A simple exercise that you might want to do is to add an alarm function - at the moment 1552 bytes of flash are used up leaving room for quite a lot of additional code 2k -1552 = space left. Adding an alarm function would simply be a matter of adding an additional definition:
... and using some of the DS1307 RAM to store the alarm setting (since it is preserved by the battery backup when the power fails).
You would need to change the code where the mode setting is updated and the pseudo text code as well. In the main loop you would read the current Hour,Minute setting until it matched the stored value and then generate an alarm.
For output there is a spare pin RB3 - there is a 10k pull-down but this is not useful if the part has been taken out of LVP mode by programming it using the ICSP programmer - so it is free for you to attach a piezo disc (remove the resistor - prob. was not really needed anyway!).
Jump from Real Time Clock design to
the Home Page.
The Essential Guide to the 74HC595; What it is and how you can easily use one in any of your projects.
Which pic programmer do you need? This page discusses PIC programmers and gives some essential information on choosing or building your own programmer.
learn how to use Arduino pulseIn and pulseInLong to get the most accurate pulse measurement on an Arduino.
How to use Arduino millis() for deylays but still make the processor do work - Stop using delay()
This MCP23017 Interrupt tutorial shows you how to connect interrupt outputs from several MCP23017s to a single microcontroller interrupt pin.
Arduino Delay() is a useful function but not for large programs - Find Out Why Here...