CCS does not monitor this forum on a regular basis. Please do not post bug Reports on this forum. Send them to support@ccsinfo.com
 Timer based Real Time Clock (RTC) Goto page 1, 2, 3, 4  Next
Author Message
ckielstra

Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

Timer based Real Time Clock (RTC)
Posted: Tue Feb 28, 2006 6:18 pm

Many times in the forums I have seen people asking for a real time clock based on the internal timers of the PIC. Several implementations of the version supplied by Neutone can be found in the discussion forum but not here in the code forum so I decided to add a new topic.

I like this algorithm because it is very accurate (zero drift) even when your clock frequency is not a multiple of 256Hz. Most algorithms configure the timer to generate an interrupt every 100ms, and then count the number of times the interrupt occurs. The problem in that approach is that with nice round clock frequencies like 16MHz you can't get an exact 100ms and the small error will count up to an error of several seconds a day. The algorithm presented here doesn't count the number of interrupts but counts the number of clock cycles which makes it very accurate in the long run.

The current implementation uses Timer1 but this can easily be changed to another internal timer.

 Code: /////////////////////////////////////////////////////////// // Zero Drift Real Time Clock // Original code supplied by Neutone. // Some small optimizations by C.Kielstra. /////////////////////////////////////////////////////////// #include <18F458.h> #use delay(clock=20000000) #fuses HS,NOWDT,NOLVP //RTC variables #define XTAL_FREQUENCY  20000000 #define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4)      // 1 clock tick = 1 instr. cycle = crystal frequency / 4 int32 Ticker; int8 Seconds=0; //optional: // int8 Year=11, Month=1, Days=1, Hours=0, Minutes=0;  // 2011-01-01 00:00 //////////////////////////////////////////////////////////////////////////////// // Test whether a given year is a leap year. // This optimized version only works for the period 1901 - 2099 //////////////////////////////////////////////////////////////////////////////// #define IS_LEAP(year) (year%4 == 0) //////////////////////////////////////////////////////////////////////////////// //    Initialize RTC //////////////////////////////////////////////////////////////////////////////// void Initialize_RTC(void) {   Ticker = TIMER1_FREQUENCY;                  // initialize clock counter to number of clocks per second   setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initialize 16-bit Timer1 to interrupt                                               // exactly every 65536 clock cycles                                               // (about 76 times per second)   enable_interrupts( INT_TIMER1 );            // Start RTC } //////////////////////////////////////////////////////////////////////////////// //  -=Process Zero Drift Real Time Clock Information=- // // Most algorithms configure the timer to generate an interrupt every 100ms, and // then count the number of interrupts. The problem in that approach is that most // clock frequencies can't be divided by 256 and you don't get an exact 100ms. // The small errors will add up to an error of several seconds a day. // // The algorithm presented here is exact in the long run because it doesn't // count the number of interrupts but counts the number of clock cycles. //////////////////////////////////////////////////////////////////////////////// #int_TIMER1                                void TIMER1_isr()                          {   Ticker -= 65536;                        // Decrement ticker by clocks per interrupt   if ( Ticker < 65536 )                   // If second has expired   {  Ticker += TIMER1_FREQUENCY;          //   Increment ticker by clocks per second      seconds++;                           //   Increment number of seconds   }   /* --- Optional part start ---   if (Seconds == 60) {Minutes++; Seconds=0;     if (Minutes == 60) {Hours++; Minutes=0;       if (Hours == 24) {Days++; Hours=0;         if (  (Days == 29 && Month==2 && !IS_LEAP(Year))                        // February in leap year            || (Days == 30 && Month==2)                                          // February in normal years            || (Days == 31 && (Month==4 || Month==6 || Month==9 || Month==11 ))  // All months with 30 days            || (Days == 32)                                                      // All months with 31 days            ) {Month++; Days=1;}         if (Month == 13) {Year++; Month=1;}   }}}    --- Optional part end ---  */ } //////////////////////////////////////////////////////////////////////////////// // Example program for using the RTC //////////////////////////////////////////////////////////////////////////////// void main() {   int8 prev_second;     Initialize_RTC();   enable_interrupts( GLOBAL );   // loop forever   while(1)   {     // Toggle output every second      if (seconds != prev_second)     {       prev_second = seconds;       output_toggle(PIN_A1);     }    } }

Edit 23 Mar 2006: Fixed the factor 4 error as indicated by Dorinm. Thanks.
Edit 13 Sep 2006: Fixed the leap year calculation (original version of IS_LEAP macro wasn't working at all).
Edit 18 Nov 2011: Fixed bug with day and month starting at zero instead of 1. Thanks ünloco.

Last edited by ckielstra on Thu Nov 17, 2011 6:58 pm; edited 7 times in total
dorinm

Joined: 07 Jan 2006
Posts: 38

 Posted: Sun Mar 05, 2006 10:41 am just tried your code on a 20mhz 458... a second seems to take ~4 seconds ...am I doing something wrong or is there a bug in your code!? edit: just found out what's wrong; according to microchip documentation (18f458), "When TMR1CS is clear, Timer1 increments every instruction cycle." , so that in your code timer1 actually counts the XTALFREQ/4 (a instruction cycle) and not crystal cycles .... ...for personal use i set XTAL_FREQUENCY @ realfreq/4 (5.000.000) and the code work well.
Christophe

Joined: 10 May 2005
Posts: 323
Location: Belgium

 Posted: Tue Jun 20, 2006 7:32 am Thanks for the implementation, works perfectly on my PIC16.
jmann

Joined: 27 Dec 2004
Posts: 21

 Posted: Sat Jun 24, 2006 10:01 pm That's cool! The first assignment in my embedded system design class was just like this.
akokyaw

Joined: 11 Feb 2005
Posts: 24

 Posted: Mon Aug 06, 2007 7:54 am smart calculation that can be compensate decimal part too. Thank for your code, pak
cybergm

Joined: 08 Aug 2007
Posts: 1

 internal timer using WDT Posted: Wed Aug 08, 2007 11:42 am hi, this timer works great, but can anyone tell me if I can use a WDT to do the same operation cos I want my PIC to be in sleep mode
Neutone

Joined: 08 Sep 2003
Posts: 839
Location: Houston

Re: internal timer using WDT
Posted: Wed Aug 08, 2007 3:15 pm

 cybergm wrote: hi, this timer works great, but can anyone tell me if I can use a WDT to do the same operation cos I want my PIC to be in sleep mode

No because the processors must be running in order to keep time.

I have been thinking about using a watch crystal on timer 1 to keep time and instead of going into sleep mode simply switching oscilators. This will drop power by about 2 orders of magnitude if your using a fast crystal. This would get power down to the levels that a coin cell can provide while allowing for higher proformance when more power is available.
jov_damo86

Joined: 29 Nov 2007
Posts: 9
Location: Sarrat, Ilocos Norte,Philippines

 re:Timer based Real Time Clock (RTC) Posted: Fri Dec 14, 2007 11:29 pm can i used that code in PIC16f877A with an external crystal of 20Mhz? what is that 65536 in the code? void TIMER1_isr() { Ticker -= 65536; // Decrement ticker by clocks per interrupt if ( Ticker < 65536 ) // If second has expired { Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second seconds++; // Increment number of seconds } please explain further.. thank you!
Neutone

Joined: 08 Sep 2003
Posts: 839
Location: Houston

Re: re:Timer based Real Time Clock (RTC)
Posted: Sun Dec 16, 2007 5:43 pm

 jov_damo86 wrote: can i used that code in PIC16f877A with an external crystal of 20Mhz? what is that 65536 in the code? void TIMER1_isr() { Ticker -= 65536; // Decrement ticker by clocks per interrupt if ( Ticker < 65536 ) // If second has expired { Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second seconds++; // Increment number of seconds } please explain further.. thank you!

In the first spot it is the number of times the timer will increment per interrupt. In the second spot is checks to see if "ticker" is greater than the number of timer increments per interrupt.

These values assume that the timer increments per instruction cycles is 1:1. Actual ratio could be set to other values and the number used (65536) would have to be changed to the appropriate value.

Timer is 16 bit
2^16=65536
ThomasC

Joined: 09 Oct 2007
Posts: 62

 Posted: Tue Dec 18, 2007 11:17 am Can this function run in the background while another function is being run? Thanks! The program runs well, but it seemed to be blinking 4 times a second so I used: #define XTAL_FREQUENCY (2000000) and now it seems to blink once every second. How can I be sure that it is blinking exactly once a second? I'm comparing it to the windows clock second hand, and it's probably me but it seems off.
Neutone

Joined: 08 Sep 2003
Posts: 839
Location: Houston

Posted: Tue Dec 18, 2007 12:34 pm

 ThomasC wrote: Can this function run in the background while another function is being run? Thanks! The program runs well, but it seemed to be blinking 4 times a second so I used: #define XTAL_FREQUENCY (2000000) and now it seems to blink once every second. How can I be sure that it is blinking exactly once a second? I'm comparing it to the windows clock second hand, and it's probably me but it seems off.

If you can't do the math just let it run for 24 hours and see how much error you have. You would have to add a printF statement to show time.
ThomasC

Joined: 09 Oct 2007
Posts: 62

 Posted: Tue Dec 18, 2007 1:48 pm I was wrong. Its rights on the money.
ThomasC

Joined: 09 Oct 2007
Posts: 62

 Posted: Fri Dec 21, 2007 3:54 pm Is there a way to simultaneously run this clock program so it records the ON time of the pic while you are doing other tasks? Or does it only run in succession? Thanks.
cnsnt

Joined: 29 Nov 2007
Posts: 6

 Posted: Sun Dec 23, 2007 2:33 pm hi masters.. can I use these codes for pic16f628 with internal rc osc(4Mhz).I have to do it because I have a very small area.I can not use rtc hardware(ds1307 etc.)because of small area. thanks
ThomasC

Joined: 09 Oct 2007
Posts: 62

 Posted: Wed Dec 26, 2007 8:12 am It should work. I answered my own question, I figured out that it does run in the background. Sorry for the redundant question.
 Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First
 All times are GMT - 6 HoursGoto page 1, 2, 3, 4  Next Page 1 of 4

 Jump to: Select a forum Software----------------General CCS C DiscussionCode LibraryEZ App LynxBest OfKnown Issues Hardware----------------CCS ICD / Mach X / Load-n-Go
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum