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

Timer0 loosing time

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
pat



Joined: 07 Sep 2003
Posts: 40
Location: Adelaide, Australia

View user's profile Send private message

Timer0 loosing time
PostPosted: Thu Jan 29, 2004 6:44 am     Reply with quote

I am trying to count seconds using timer0, but my counter seems to be loosing time! This is on a 16F876 running a 4MHz crystal. CCS V3.168.

I have borrowed Jon Fick's timer0 interrupt service routine as follows

Code:
#int_rtcc
void timer_0_isr (void) {

    /*
    ** Gets here every 15.872ms = (256 - 8) * 64us
    ** "timer_0_count" is incremented every time.
    ** When it gets to 63 it has been 0.999936 seconds
    */
    if (timer_0_count < 63) {
        timer_0_count++;
    }
    else {

        /* Count seconds */
        tenth_sec_a++;

        /* start count over again */
        timer_0_count = 0;

    }

    /* reset timer for 248 counts, rather wrap at 255 */
   set_timer0 (8);

}


The prescaler is set to 64 and
Code:
tenth_sec_a
is an int32 that I use in the main loop to print to an LCD once every 300ms or so.

After 4 minutes (240 seconds), the LCD shows a count of 236 seconds.

My crystal is OK becuase delays and RS232 are working fine.

Could my
Code:
tenth_sec_a
counter be corrupted as I'm printf'ing it? I actually print it using a function call, so a local copy is being made of the var, hence it should not be corrputed/overwritten if another interrupt occurs while I'm printing it to the LCD.

Jon's spreadsheet says that 0.23 seconds will be gained per hour due to the fractional error accumulation.

Any suggestions would be appreciated.

Patrick

PS thanks Jon, also borrowed your key debounce routine, works very well)
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

Int32?
PostPosted: Thu Jan 29, 2004 7:28 am     Reply with quote

What are you using an int32 for? If it is tenths of a second, it only counts from 0-9. An 8-bit int is more than enough. I doubt this could cause the problem you are seeing, but working with multibyte words greatly increases the processor overhead. If you don't need your 'tenth_sec_a' variable to count up to 4,294,967,296 then change to an int.

Printing to an LCD every 300ms is a sensible time interval to use, but if you are printing 10ths of a second you will only capture the counts which coincide with a display refresh. Is this 10ths variable something like a stopwatch, where it is displayed after the timer is stopped?

Do you have an oscilloscope or even better a frequency counter to check the xtal frequency? If your RS232 is slow (<9600 or so) it will not be particularly sensitive to frequency variation.

Just some ideas,
Neil.
Ttelmah
Guest







Re: Timer0 loosing time
PostPosted: Thu Jan 29, 2004 7:46 am     Reply with quote

pat wrote:
I am trying to count seconds using timer0, but my counter seems to be loosing time! This is on a 16F876 running a 4MHz crystal. CCS V3.168.

I have borrowed Jon Fick's timer0 interrupt service routine as follows

Code:
#int_rtcc
void timer_0_isr (void) {

    /*
    ** Gets here every 15.872ms = (256 - 8) * 64us
    ** "timer_0_count" is incremented every time.
    ** When it gets to 63 it has been 0.999936 seconds
    */
    if (timer_0_count < 63) {
        timer_0_count++;
    }
    else {

        /* Count seconds */
        tenth_sec_a++;

        /* start count over again */
        timer_0_count = 0;

    }

    /* reset timer for 248 counts, rather wrap at 255 */
   set_timer0 (8);

}


The prescaler is set to 64 and
Code:
tenth_sec_a
is an int32 that I use in the main loop to print to an LCD once every 300ms or so.

After 4 minutes (240 seconds), the LCD shows a count of 236 seconds.

My crystal is OK becuase delays and RS232 are working fine.

Could my
Code:
tenth_sec_a
counter be corrupted as I'm printf'ing it? I actually print it using a function call, so a local copy is being made of the var, hence it should not be corrputed/overwritten if another interrupt occurs while I'm printing it to the LCD.

Jon's spreadsheet says that 0.23 seconds will be gained per hour due to the fractional error accumulation.

Any suggestions would be appreciated.

Patrick

PS thanks Jon, also borrowed your key debounce routine, works very well)


Your problem is probably with setting timer_0 to a value.
Remember that it takes _time_ to get to this point in the code. The time taken, will also vary if there has been a delay in handling the interrupt (because of things like serial interrupts). This is why routines that use this type of behaviour, will nearly allways give timing errors. You will see in the archives, that I have allways advocated 'advancing' the timer, rather than using this type of behaviour, so you add a value to the timer, which reduces the problem (there is still a slight problem because of the asynchronous times when the timer is read, and written).
So in order of accuracy, you have:
1) Don't fiddle with the timer, but instead use a counter that can handle the odd times itself. There have been some nice code examples published in the past doing this.
2) Add an amount to the timer, rather than setting it to a value. Again examples have been given in the past.
3) Setting to a value.

Your approach is the least accurate route...
There have been a couple of 'RTC' code examples published here, that give accurate timing. The typical method is to add a value to the counter, and then check against a larger 'limit', to give a counter that corrects for the timer interrupt being at an odd interval. This works much better. :-)

Best Wishes
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

That got me thinking
PostPosted: Thu Jan 29, 2004 8:16 am     Reply with quote

Hi Ttelmah,
do you suggest adding a value to the rtcc because the interrupt overhead can have a variable (latency, is that the word?) time to get to the rtcc handler, depending on what other events occurred?
I use the presetting approach, but don't do any if() testing on the timer count. I haven't done anything time critical yet, but as you know, I have had lots of 'timing issues' in the past!

If you add a constant to timer0, is there still not the danger that it can increment during the add. Is the add operation 2 cycles long? eg.
Code:
movlw CONST;
addwf tmr0;
Can you explain more why presetting is not the best thing to do?

Regards,
Neil.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Thu Jan 29, 2004 9:35 am     Reply with quote

When the interupt flag is set sometimes the code that is being run is in a spot where global interupts are briefly disabled. That will introduce jitter in the amount of time it takes to jump to the interupt function. Setting the timer to a fixed value will insure that every jitter adds up to become long term error. Adding a fixed value to the timer might remove this jitter error if the process of adding a fixed value to the timer has a fixed execution time. This sort of thing is very difficult to calculate. You have to solve for the value to add experimentaly and accuracy is limited by the resolution of your measurments. The drawback of using the RTC code posted in this forum in the past is the jitter in individual readings. On the up side there is none of the long term drift that you have observed when adding a fixed value and it is all based on easily calculated values.
Ttelmah
Guest







Re: That got me thinking
PostPosted: Thu Jan 29, 2004 9:53 am     Reply with quote

neil wrote:
Hi Ttelmah,
do you suggest adding a value to the rtcc because the interrupt overhead can have a variable (latency, is that the word?) time to get to the rtcc handler, depending on what other events occurred?
I use the presetting approach, but don't do any if() testing on the timer count. I haven't done anything time critical yet, but as you know, I have had lots of 'timing issues' in the past!

If you add a constant to timer0, is there still not the danger that it can increment during the add. Is the add operation 2 cycles long? eg.
Code:
movlw CONST;
addwf tmr0;
Can you explain more why presetting is not the best thing to do?

Regards,
Neil.

Because as you say of latency. Remember if you have (say) a serial ISR, and the timer interrupt occurs a moment after the serial one, the global interrupt handler will take typically perhaps 30 instruction times to send the code into the ISR, and then recover the registers. If the ISR itself takes another forty instructions, you have the processor effectively with it's interrupts disabled for seventy instruction times. Inside the timer ISR, there is then another perhaps 18 instructions before you even reach the ISR, and then the duration of the ISR itself, before you reach your 'set' code. Even if the ISR is called immediately, the overhead of saving the registers, and running through the ISR code, is many uSec. So at the point where you increment, the counter may (or may not) have allready incremented. If you then set it to a value, assuming it has not incremented, your clock will now be slow by the amount that the counter had advanced.
Unless you can _guarantee_ that the clock 'tick' won't advance in the time taken to reach the part of the handler where the clock is set, changing the number to a fixed value, will result in errors.
You can just increment the counter by a fixed number (since this is an 8bit counter), but even here, if the 'increment' event, occurs on the instruction edged between reading the register, and writing it back, the event will be lost....
Hence the much better approach, is to make the 'second' counter, use a count value that adjusts for the 'odd' timer interval. So in your case, if you let the timer 'free run' (don't advance it to 8), the timer interrupt will trigger every 64*256uSec. This then gives 61.03515625 interrupts/second. The 'key' is that if you increment a 32bit counter, by the number of uSec/interrupt, and then check for this reaching one million, and when it does, subtract the million from it, the clock will give the correct division ratio. So:
Code:

#int_rtcc
void timer_0_isr (void) {

    /*
    ** Gets here every 16.384mSec = 256 * 64us */
    /* Now increment the count by the number of uSec/interrupt */
    if ((timer_0_count+=16384l)>1000000l)
        timer_0_count-=1000000l;
        tenth_sec_a++;
    }
}


Since the average result of 1000000/16384, is 61.03515625, the 'tenth_sec_a' counter will increment by one every second, and the clock should keep the correct time. The counter will be one 61.035 'late' on some seconds, and slightly early on others, but the overall result, is the required ratio.

Best Wishes
neil



Joined: 08 Sep 2003
Posts: 128

View user's profile Send private message

Sorted
PostPosted: Thu Jan 29, 2004 10:02 am     Reply with quote

Thanks Ttelmah & Neutone. I've got it now.
pat



Joined: 07 Sep 2003
Posts: 40
Location: Adelaide, Australia

View user's profile Send private message

PostPosted: Thu Jan 29, 2004 8:44 pm     Reply with quote

Thanks all for your help. The first order problem that I discovered since my original post is in this code here

Code:
if (timer_0_count < 63) {
        timer_0_count++;
    }


this is a typical gotcha - becuase I reset timer_0_count to zero and compare it to <63 it is actually counting 64 interrupts, not 63. So I have changed the constant to 62. I originally thought this had solved the problem, and while it is vastly "better", I still have some drift, but only observable over a much longer time period, eg 15 minutes or so.

I can't spot any more coding erros, but I really like the look of Ttelmah's suggestion which I will try over this weekend.

Thanks again
pat



Joined: 07 Sep 2003
Posts: 40
Location: Adelaide, Australia

View user's profile Send private message

It works brilliantly - very cool!
PostPosted: Fri Jan 30, 2004 1:52 am     Reply with quote

Very Happy Ttelmah you're a genius!! I just coded it up, and its been running for 20 minutes with zero drift!! Its so clever and simple too. Very Happy

I've actually dropped off a zero from your one million becuase I wanted to count 10ths of seconds.

Now I just need to display the time in hours mins and sec and I'm done!

Thanks again
Ttelmah
Guest







Re: It works brilliantly - very cool!
PostPosted: Fri Jan 30, 2004 3:03 am     Reply with quote

pat wrote:
Very Happy Ttelmah you're a genius!! I just coded it up, and its been running for 20 minutes with zero drift!! Its so clever and simple too. Very Happy

I've actually dropped off a zero from your one million becuase I wanted to count 10ths of seconds.

Now I just need to display the time in hours mins and sec and I'm done!

Thanks again

Good.
I was slightly puzzled in the original, since the variable name referred to 'tenths of a second', yet you counted to 64. Hence my assumption that you were going to 1 second. The lovely thing is that this process of addition, test and subtraction, allows any factor to be dealt with very easily, without too much work for the processor. :-)

Best Wishes
pat



Joined: 07 Sep 2003
Posts: 40
Location: Adelaide, Australia

View user's profile Send private message

PostPosted: Fri Jan 30, 2004 2:48 pm     Reply with quote

13 hours, 21 minutes and it still hasn't drifted. I won't post again Smile
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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