View previous topic :: View next topic |
Author |
Message |
darrepac
Joined: 19 Jan 2005 Posts: 22
|
Workaround for timer latency |
Posted: Wed Jan 19, 2005 4:09 pm |
|
|
Hello,
As many of you (as far I seen in the forum), I have discovered that timer as a blind latency due to context saving... Ok great it answers the question I had from long time : why my 69.5us timer is doing a 79us time.
I have seen a lot of answers from Ttelmah and also one from Mark explaining this problem and possible workaround : adding value to the timer, ccp usage, soft counter usage.
Ok but I have dificulties to understand all of them :
adding value to the timer
Ok here is the solution I thought I have understood...but I have done the following trials (quick and dirty code, teh final goal is not to generate output pulse but internal timing)
Code: | #use delay(clock=16000000) // 16 MHz OSC
int1 debug_pin1;
#INT_TIMER0
void timer_interrupt(void) //4800*3 /sec , 1/(4800*3) = 69.444us => 69.5us by dividing by 4*2 + overflow at 139
{
//set_timer0(256-139); //cause around 10us time more
set_timer0 (get_timer0()+256-139);
if (debug_pin1 == 0) debug_pin1 = 1;
else debug_pin1 = 0;
output_bit (LED, debug_pin1);
return;
}
void main (void)
{
int1 delire;
setup_timer_0 (RTCC_INTERNAL|RTCC_DIV_2);
//setup_counters (RTCC_INTERNAL, RTCC_DIV_2);
enable_interrupts (INT_TIMER0);
enable_interrupts (GLOBAL);
set_timer0(256-139);
set_tris_b (LED_WRITE);
debug_pin1 = 0;
while (1)
{
if (delire == 0) delire = 1;
else delire = 0;
}
} |
and in the fact, at the output, the level 1 (debug_pin1 = 1) has the right time : around 69us. But the level 0 has still the 79us time (?!)... So still open question here.
ccp usage
Really do not precisely understand the idea behind..may-be it is really not accurate for my purpose....check http://www.ccsinfo.com/forum/viewtopic.php?t=21479&highlight=timing+interrupt
Counter usage
http://www.ccsinfo.com/forum/viewtopic.php?t=18175&highlight=latency
here also, I am not so sure it is accurate for my purpose...
Thanks
Pac |
|
|
Haplo
Joined: 06 Sep 2003 Posts: 659 Location: Sydney, Australia
|
|
Posted: Wed Jan 19, 2005 6:26 pm |
|
|
Not an answer to your question, but a suggestion. If you need a very precise timer interrupt, you can use #INT_GLOBAL. This stops the Compiler from generating any context saving/dispatching code:
Quote: | #INT_GLOBAL
Syntax: #int_global
Elements: None
Purpose: This directive causes the following function to replace
the compiler interrupt dispatcher. The function is
normally not required and should be used with great
caution. When used, the compiler does not generate
start-up code or clean-up code, and does not save the
registers.
Examples: #int_global
isr() { // Will be located at location 4
#asm
bsf isr_flag
retfie
#endasm
}
Example Files: ex_glint.c |
|
|
|
darrepac
Joined: 19 Jan 2005 Posts: 22
|
|
Posted: Thu Jan 20, 2005 1:39 am |
|
|
Interesting information...even if for me I prefer to have the classical context savings....
In fact I do not understand why my code with get_timer0() doesn't work well (in fact it is working half time)... |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jan 20, 2005 2:45 am |
|
|
Are you using a processor from the PIC16 or PIC18 series? I'm asking this because of the different overheads in interrupt processing for both processor lines.
Code: | setup_timer_0 (RTCC_INTERNAL|RTCC_DIV_2); | Please add RTCC_8_BIT. For the PIC18 series this is compulsory or you'll get a 16-bit timer, for the PIC16 series it's just a nice add-on.
You are not showing your whole program. Are you sure there is no other interrupt interfering? |
|
|
darrepac
Joined: 19 Jan 2005 Posts: 22
|
|
Posted: Thu Jan 20, 2005 4:00 am |
|
|
Sorry I miss to include the "#include" line, but except this mistake this is the whole code...
I am using a PIC16F876A so no choice : timer 0 is a 8Bit counter
no other interrupt |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jan 20, 2005 4:25 am |
|
|
Quote: | Sorry I miss to include the "#include" line, but except this mistake this is the whole code... | I still can't compile; LED and LED_WRITE are missing too. Please post these as well.
Looking at your code, I think the timing error is in the way you meassure the data. Am I right when I assume you are only meassuring the first pulse? The first pulse has the interrupt overhead included but following pulses would be correct. It is quiet difficult in this setup to get the first pulse correct, the only way I can think of is by hard coding a correction factor, but newer compiler releases might affect this.
Is it important to you to have the very first pulse exact?
Last edited by ckielstra on Thu Jan 20, 2005 4:36 am; edited 1 time in total |
|
|
darrepac
Joined: 19 Jan 2005 Posts: 22
|
|
Posted: Thu Jan 20, 2005 4:32 am |
|
|
Really strange... my goal wa to do a copy paste of my whole code but clearly lines are missing...
I am at work and to not have access to my code here...hummmm let me try...yes I have the info needed...here is the part missing (hope it will be ok this time)
Code: |
#include <16F876A.H>
#use fast_io(B)
#device *=16
#fuses HS,PUT,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOBROWNOUT
#define RS232_XMIT PIN_C6
#define RS232_RCV PIN_C7 // PIC line which receives PC transmission
#define LED PIN_B1
#define LED_WRITE 0x0
#use delay(clock=16000000) // 16 MHz OSC
|
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jan 20, 2005 6:44 am |
|
|
Now your code is complete and compiles.
You didn't answer my two questions.
Code: | set_timer0 (get_timer0()+256-139); |
Note that 256 is a 16-bit constant. The CCS preprocessor is terrible and does a bad job of resolving constants, resulting in 16-bit runtime calculations. It takes an incredible 15 instructions to read and update the timer. At 16MHz that's an error of 3,75us.
Change to Code: | unsigned int8 TMR0;
#locate TMR0=0x0001
TMR0 += (int8)(256-139); | Which is just 2 instructions.... And, taking in account your RTCC_DIV_2, you can even correct for that by changing to: Code: | TMR0 += (int8)(256-139+1); |
|
|
|
darrepac
Joined: 19 Jan 2005 Posts: 22
|
|
Posted: Thu Jan 20, 2005 6:48 am |
|
|
Quote: |
Looking at your code, I think the timing error is in the way you meassure the data. Am I right when I assume you are only meassuring the first pulse?
|
No I am measuring pulse some time after and so I am sure I do not see the first one...second when I trace with my scope, I see several pulse, so clearly the error is not comnig from there....But it was better to clarify
Quote: |
Is it important to you to have the very first pulse exact? |
Not at all... |
|
|
darrepac
Joined: 19 Jan 2005 Posts: 22
|
|
Posted: Thu Jan 20, 2005 6:50 am |
|
|
ckielstra wrote: | Now your code is complete and compiles.
You didn't answer my two questions.
Code: | set_timer0 (get_timer0()+256-139); |
Note that 256 is a 16-bit constant. The CCS preprocessor is terrible and does a bad job of resolving constants, resulting in 16-bit runtime calculations. It takes an incredible 15 instructions to read and update the timer. At 16MHz that's an error of 3,75us.
Change to Code: | unsigned int8 TMR0;
#locate TMR0=0x0001
TMR0 += (int8)(256-139); | Which is just 2 instructions.... And, taking in account your RTCC_DIV_2, you can even correct for that by changing to: Code: | TMR0 += (int8)(256-139+1); |
|
Ok interesting to know.... but it should not cause the error I have, no? |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
Re: Workaround for timer latency |
Posted: Thu Jan 20, 2005 7:16 am |
|
|
darrepac wrote: | Hello,
As many of you (as far I seen in the forum), I have discovered that timer as a blind latency due to context saving... Ok great it answers the question I had from long time : why my 69.5us timer is doing a 79us time.
I have seen a lot of answers from Ttelmah and also one from Mark explaining this problem and possible workaround : adding value to the timer, ccp usage, soft counter usage.
Ok but I have dificulties to understand all of them :
adding value to the timer
Ok here is the solution I thought I have understood...but I have done the following trials (quick and dirty code, teh final goal is not to generate output pulse but internal timing)
Code: | #use delay(clock=16000000) // 16 MHz OSC
int1 debug_pin1;
#INT_TIMER0
void timer_interrupt(void) //4800*3 /sec , 1/(4800*3) = 69.444us => 69.5us by dividing by 4*2 + overflow at 139
{
//set_timer0(256-139); //cause around 10us time more
set_timer0 (get_timer0()+256-139);
if (debug_pin1 == 0) debug_pin1 = 1;
else debug_pin1 = 0;
output_bit (LED, debug_pin1);
return;
}
void main (void)
{
int1 delire;
setup_timer_0 (RTCC_INTERNAL|RTCC_DIV_2);
//setup_counters (RTCC_INTERNAL, RTCC_DIV_2);
enable_interrupts (INT_TIMER0);
enable_interrupts (GLOBAL);
set_timer0(256-139);
set_tris_b (LED_WRITE);
debug_pin1 = 0;
while (1)
{
if (delire == 0) delire = 1;
else delire = 0;
}
} |
and in the fact, at the output, the level 1 (debug_pin1 = 1) has the right time : around 69us. But the level 0 has still the 79us time (?!)... So still open question here.
ccp usage
Really do not precisely understand the idea behind..may-be it is really not accurate for my purpose....check http://www.ccsinfo.com/forum/viewtopic.php?t=21479&highlight=timing+interrupt
Counter usage
http://www.ccsinfo.com/forum/viewtopic.php?t=18175&highlight=latency
here also, I am not so sure it is accurate for my purpose...
Thanks
Pac |
Should be exactly the same. Maybe you counting rise and fall times in the 0 state? Try this, toggle the output pin every other cycle. If what you say is true, then I would expect the level 1 time to be 69+79 = 148us.
[/code] |
|
|
darrepac
Joined: 19 Jan 2005 Posts: 22
|
|
Posted: Thu Jan 20, 2005 7:21 am |
|
|
Excuse-me Mark, but I have difficulty to understand your idea...Could you elaborate more? |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Thu Jan 20, 2005 7:38 am |
|
|
darrepac wrote: | Excuse-me Mark, but I have difficulty to understand your idea...Could you elaborate more? |
Code: |
void timer_interrupt(void) //4800*3 /sec , 1/(4800*3) = 69.444us => 69.5us by dividing by 4*2 + overflow at 139
{
static int8 count=0;
set_timer0 (get_timer0()+256-139);
count++;
if (count == 2)
{
output_high(LED);
}
else if (count == 4)
{
output_low(LED);
count = 0;
}
return;
}
|
But this is how I would do it
Code: |
unsigned int8 TMR0;
#locate TMR0=0x0001
#INT_TIMER0
void timer_interrupt(void)
{
static int1 toggle=0;
TMR0 += 117; //(256-139);
if (toggle)
{
output_high(LED);
toggle = 1;
}
else
{
output_low(LED);
toggle = 0;
}
return;
}
|
|
|
|
darrepac
Joined: 19 Jan 2005 Posts: 22
|
|
Posted: Thu Jan 20, 2005 8:02 am |
|
|
Ok I understand that you want me to do somehow a frequency divider by 2...ok I can try and see the level 0 and 1 time in that case...
But for Quote: | Maybe you counting rise and fall times in the 0 state? | what do you mean exactly? |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Thu Jan 20, 2005 8:12 am |
|
|
darrepac wrote: | Ok I understand that you want me to do somehow a frequency divider by 2...ok I can try and see the level 0 and 1 time in that case...
But for Quote: | Maybe you counting rise and fall times in the 0 state? | what do you mean exactly? |
Signals don't change instantaneous. It takes a certain amount of time for this to occur. Inductance and Capacitance play a big role in this. The rise/fall time can also be called the slope of the line. Rise time is the amount of time it takes for the signal to go from the low level to the high level. The fall time is the time it take to go from the high level to the low level.
For your original code, try measuring the time between the rising edges. See if you get 138us or 148us. |
|
|
|