CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Timer based Real Time Clock (RTC)
Goto page Previous  1, 2, 3, 4  Next
 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
jov_damo86



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

View user's profile Send private message

PostPosted: Thu Jan 10, 2008 5:21 am     Reply with quote

int32 Ticker;
int8 Seconds=0;

//optional:
// int8 Year=0,Month=0,Days=0,Hours=0,Minutes=0;

////////////////////////////////////////////////////////////////////////////////
// Test whether a given year is a leap year.
// This optimized version only works for the period 2001 - 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))
|| (Days == 30 && Month==2)
|| (Days == 31 && (Month==4 || Month==6 || Month==9 || Month==11 ))
|| (Days == 32)
) {Month++;Days=0;}
if(Month == 13) {Year++; Month=0;}
}}}
--- Optional part end --- */
}



Wow great! it was very accurate...i tried it! thanks for the code...

But,any code to modified:
int8 Year=0,Month=0,Days=0,Hours=0,Minutes=0;
or seconds using a push button
Thanks in advance! Wink
nachomanz



Joined: 17 Jan 2008
Posts: 1

View user's profile Send private message

PostPosted: Thu Jan 17, 2008 7:30 am     Reply with quote

Hi,

Can I use this code to measure milliseconds or microseconds? How can I do it?

Thanks!!
jov_damo86



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

View user's profile Send private message

PostPosted: Sun Feb 10, 2008 1:47 am     Reply with quote

I guess you can modify the program by multiplying the seconds to make into smaller units:

int8 millisecond, microsecond
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
millisecond=seconds*1000; //in millisecond
microsecond=seconds*1000000 //in microsecond
}



for the conversion:
1000 millisec=1second
1^6 microsec = 1 second
LourenceGuo



Joined: 14 Feb 2008
Posts: 7
Location: China, Shenzhen

View user's profile Send private message

PostPosted: Thu Feb 14, 2008 6:58 pm     Reply with quote

I tried this code with 8MHz crystal with PIC16LF873A, it works.
But the when I let the clock work over 12 hours, it is 6seconds faster than my computer, I am not sure if my computer clock is so precise, but is always work well.
I add some function to test the time: a 2*16 LCD display, and a temperature sensor to measure the temperature, three keys to set the time.

And I modified a little in the:
--- Optional part start ---
The begining value for Days and Month can not be 0, so I changed it to 1.

Code:
///////////////////////////////////////////////////////////
// Zero Drift Real Time Clock
// Original code supplied by Neutone.
// Some small optimizations by C.Kielstra.
///////////////////////////////////////////////////////////
#include <16F873A.h>
#device ADC=10
#use delay(clock=8000000)
#fuses HS,NOWDT,NOLVP,NOBROWNOUT
#include "lcd.c"

//RTC variables
#define XTAL_FREQUENCY  8000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4)      // 1 clock tick = 1 instr. cycle = crystal frequency / 4
int32 Ticker;
int8 Seconds=0;
//int8 Seconds_H=0;
//int8 Seconds_L=0;

int8 Minutes=0;
int8 Minutes_H=0;
int8 Minutes_L=0;

int8 Hours=0;
int8 Hours_H=0;
int8 Hours_L=0;

int8 Days=0;
int8 Days_H=0;
int8 Days_L=0;

int8 Month=0;
int8 Month_H=0;
int8 Month_L=0;

int8 Year=0;
int8 Year_H=0;
int8 Year_L=0;

float ad_value;
signed int ad_result;

int8 bk_counter;
int8 cursor_position=1;
int8 pre_cursor_position;
int8 number_count=0;
int8 my_pre_second;
int1 key_flag=0;
int1 set_key_flag=0;
int1 up_key_flag;
int1 down_key_flag;

char disp_char[]={'0','1','2','3','4','5','6','7','8','9'};

void time_display();
void Initialize_RTC(void);
void TIMER1_isr();
void key_detection();
void temp_ad();
void temp_display();

#define SET_OK_KEY  input(PIN_C0)
#define UP_KEY      input(PIN_C1)
#define DOWN_KEY    input(PIN_C2)

#define BK          PIN_B0

#priority  TIMER1, TIMER0//set interrupt priority - timer1, timer0

//optional:
// int8 Year=0,Month=0,Days=0,Hours=0,Minutes=0;

////////////////////////////////////////////////////////////////////////////////
// Test whether a given year is a leap year.
// This optimized version only works for the period 2001 - 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 30.5 times per second)
  setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2);    // setup interrupts                                           
  enable_interrupts( INT_TIMER1 );            // Start RTC
  enable_interrupts(int_timer0);              // Enables the interrupt Timer0
 
  setup_adc_ports(AN0_AN1_VSS_VREF);   //setup adc channel: AN0, AN1; VREF_VSS as refference voltage
  setup_adc(ADC_CLOCK_INTERNAL);       //use internal clock source
 
  setup_timer_2(T2_DISABLED,0,1);
  SETUP_CCP1(CCP_OFF);
  SETUP_CCP2(CCP_OFF);
  SETUP_SPI(SPI_SS_DISABLED);
  setup_comparator(NC_NC_NC_NC);
 
  //output_float(PIN_A0);
  //output_float(PIN_A1);
  output_float(PIN_A2);
  //output_float(PIN_A3);
  output_float(PIN_A4);
  output_float(PIN_A5);
  output_float(PIN_C3);
  output_float(PIN_C4);
  output_float(PIN_C5);
  output_float(PIN_C6);
  output_float(PIN_C7);
}

////////////////////////////////////////////////////////////////////////////////
//  -=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, Ticker=Ticker-65535
  if ( Ticker < 65536 )                   // If second has expired
  {
     Ticker += TIMER1_FREQUENCY;          //   Increment ticker by clocks per second,Ticker=Ticker+65536
     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))
           || (Days == 30 && Month==2)
           || (Days == 31 && (Month==4 || Month==6 || Month==9 || Month==11 ))
           || (Days == 32)
           ) {Month++;Days=1;}
        if(Month == 13) {Year++; Month=1;}
      }
    }
  }
  // --- Optional part end ---
}


#int_rtcc             
void clock_isr()
{           
   if(key_flag==1)
   {
     output_high(BK);
     
     if (seconds != my_pre_second)
     {
       my_pre_second = seconds;
       bk_counter++;
     }
     else if(bk_counter==5)
     {
       output_low(BK);
       key_flag=0;
       bk_counter=0;
     }
  }                                 
   
}
////////////////////////////////////////////////////////////////////////////////
// Example program for using the RTC
////////////////////////////////////////////////////////////////////////////////
void main()
{
  //int8 prev_second;
 
  Initialize_RTC();
  enable_interrupts( GLOBAL );
  lcd_init();
  output_low(BK);
  delay_ms(10);
  // loop forever
  while(TRUE)
  {
    time_display();
    key_detection();
    temp_ad();
    temp_display();
    //sleep();
    //delay_ms(10);
  }
}

void key_detection()
{}

void time_display()
{}
    //*****************************

void temp_display()
{}

void temp_ad()
{ }

_________________
All things are difficult before they are easy.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Fri Feb 15, 2008 12:49 pm     Reply with quote

LourenceGuo wrote:
I tried this code with 8MHz crystal with PIC16LF873A, it works.
But the when I let the clock work over 12 hours, it is 6seconds faster than my computer, I am not sure if my computer clock is so precise, but is always work well.

I would guess that your crystal oscillator circuit has a problem. Make sure you have the right capacitor values. You may need a resistor in the circuit for improved stability. I would be surprised if your PC is off by 6 seconds in 12 hours.
LourenceGuo



Joined: 14 Feb 2008
Posts: 7
Location: China, Shenzhen

View user's profile Send private message

PostPosted: Fri Feb 22, 2008 9:00 pm     Reply with quote

Thank you for the reply!

The crystal I used is 8MHz, HC-49S, but the load capacitor and ppm is
not sure. The capacitor I used is pf, there is no resistor in parallel or serial connection with the crystal.
Please kindly help to figure out how to connect the resistor to stablize the crystal, and what is the value ? Because the design we made before is not so critical for the crystal circuit.
And I also will try to use a clear load capacitor and ppm crystal to try the time precision.

The computer I use, I think have battery to hold the time on the mainboard, so the time for computer will never stopped.
Thank you!
_________________
All things are difficult before they are easy.
hayee



Joined: 05 Sep 2007
Posts: 252

View user's profile Send private message

PostPosted: Tue Jun 24, 2008 1:11 am     Reply with quote

Hi Everyone,
U guys r really doing well. I have seen ur RTC code and tried it But i have a problem in this. I have add lcd also for display but it is not working properly. When i add this line to the code
Code:

printXY_LCD(prev_second,1,1);

the clock is not working and when i remove this line and show results on pc through serial port this works fine. What is wrong in my code ? Am i initializing lcd wrong or is there any other problem ?

Code:

#include "RTC Timer1.h"
#include "string.h"
char str[];
#include "lcd.c"
#define XTAL_FREQUENCY 20000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4)

int32 Ticker;
int8 Seconds=0;
int8 Minutes=0,Hours=0;

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
}

#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
  }
   
  if(Seconds == 60)
    {
     Minutes++;
     Seconds=0;
    }
    if(Minutes == 60)
      {
       Hours++;
       Minutes=0;
      }
     if(Hours == 24)
      {
       Hours=0;
      }
   
}

void main()
{
   int8 prev_second;
   
   Initialize_RTC();
   enable_interrupts( GLOBAL );
   
   {
   initLCD();
   delay_ms(dly);
 
  // loop forever
  while(1)
  {
     if (seconds != prev_second)
     {
      prev_second = seconds;
      printf(" %d:%d:%d \r\n",Hours,Minutes,prev_second);
      printXY_LCD(prev_second,1,1);
      delay_ms(dly);
     } 
  }
 }
}
hayee



Joined: 05 Sep 2007
Posts: 252

View user's profile Send private message

PostPosted: Fri Jun 27, 2008 10:55 pm     Reply with quote

Hi Everyone,
I have solved the problem.It was an initializing problem.
Thanx
sjharris



Joined: 11 May 2006
Posts: 78

View user's profile Send private message

PostPosted: Mon Jul 07, 2008 8:30 am     Reply with quote

I would like to use this code with a 16F877, have made the code and simulated it in Labcenter electronics and it seems to drift -3 minutes in 15 minutes of operation.
So I programmed into a PIC and tried it in circuit but I can't get it to work. Does the 16f877 need an external oscillator on it to make the internal clock work??

TIA
SJHarris
ckielstra



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

View user's profile Send private message

PostPosted: Tue Oct 21, 2008 3:45 pm     Reply with quote

A late response but I don't like open questions.
sjharris wrote:
Does the 16f877 need an external oscillator on it to make the internal clock work??
The PIC16F877 has no internal clock, and even if it did it wouldn't be accurate enough for a clock application. Use an external crystal.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

hardware can do a nice job on this and FAR more ROBUST
PostPosted: Sat Nov 22, 2008 2:03 pm     Reply with quote

I did a project over a year ago that required fail safe time keeping no matter what happened to the pic ( watchdog , batt change, powerdown etc) and am much more in favor of using parts like the ST micro M41T94MQ6E, which is a fine 2nd generation+ , free standing RTC clock, with its own 32khz crystal.

It includes extra non volatile ram - so can be used with a family parts to add NV storage and is communicated with using SPI .

my project had to remember the time no matter what happened in restart etc land and also had to be a deterministic design, so no interrupts were used.

I see a lot of crazy bending AND FOLDING of pic code in this post , when a bit of extra HARDWARE - controlled by the PIC can do a MUCH better and more robust job of it . and with a small diode coupled ultracap, can KEEP telling accurate RealTime for many MINUTES - while batteries are changed

if you want REAL time keeping use a REAL time keeping chip ;-))
just my 2 cents
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Sat Nov 22, 2008 4:16 pm     Reply with quote

Yes, a hardware RTC is a better solution for a lot of applications. I guess, the other forum members participating in this thread did already know about.
volcane



Joined: 23 Feb 2008
Posts: 29

View user's profile Send private message

Re: Timer based Real Time Clock (RTC)
PostPosted: Sun Mar 15, 2009 5:50 pm     Reply with quote

Quote:
#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
}

it is better change this part of code:

Code:
#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
  }   

In this way the increase of the seconds occurs every 76 cycles equal to 0996s , first cycles ranged from 76 to 77
mutthunaveen



Joined: 08 Apr 2009
Posts: 100
Location: Chennai, India

View user's profile Send private message

it works perfectly on my 16F877
PostPosted: Sat Dec 12, 2009 12:53 am     Reply with quote

Thanks for your code..... thank you very much.

Now I am calculating for the drift.
nukeman



Joined: 04 Jan 2010
Posts: 3

View user's profile Send private message

PostPosted: Mon Jan 18, 2010 11:43 am     Reply with quote

I'll admit I'm new to PIC programming, but I'm pretty experienced with math in general and I've figured out a way to save a little memory when using this algorithm. I'm sure this isn't generally as much of a concern when using a PIC that has 8k words or more memory, but if you're trying to do a lot of stuff at once with one of the smaller chips, saving every byte you can is very handy.

The basic principle involves first noting that the TIMER1_FREQUENCY value and 65536 can generally be divided by some meaningfully large power of 2. In my particular implementation, TIMER1_FREQUENCY is 1000000, so both values can be divided evenly by 64. This means we're now decrementing Ticker by 1024 each time instead of 65536 and TIMER1_FREQUENCY becomes TIMER1_FREQUENCY_BY_64, or 15625. If we do this, it means we can now declare Ticker as an int16 instead of an int32.

I believe in total this saves at least 2 bytes of RAM and 6 to 8 words of ROM. Of course, it isn't quite as pretty to look at. For anyone who chooses to make this change, I recommend commenting it well enough so you can figure out later what you did. At the very least, comment out anything you change so if you're looking at it later you can figure out what it used to look like.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Goto page Previous  1, 2, 3, 4  Next
Page 2 of 4

 
Jump to:  
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


Powered by phpBB © 2001, 2005 phpBB Group