![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
fvnktion
Joined: 27 Jun 2006 Posts: 39
|
Why is my CCP timing so far off?? |
Posted: Mon Feb 25, 2008 3:56 pm |
|
|
Hope someone can help me with my timing here. I am measuring the period of a waveform using ccp1 capture rising egde of a 10 khz signal that is generated on ccp2 using pwm.
The timer 1 values that i am getting are not as expected. The values are listed below. I am using an external HS can oscillator. I have measured the pwm signal and am getting a 10 Khz signal.
t1: 488 (timer1 value) = 20e6/(4*(488-45)) = 11.2867 Khz
t2: 451 (ccp1 value) = 20e6/(4*(451)) = 11.08 Khz
Is my understanding that by taking the ccp_1 value i do not need to take into account the ISR overhead?
Thanks for the input.
compiler: 4.057
Code: |
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#include "lcd.c"
//global vars________________________________________________
long rise1,rise2,fall,pulse_width;
//isr________________________________________________________
// CCP_1 is the time the pulse went high
// CCP_2 is the time the pulse went low
// pulse_width/(clock/4) is the time
// In order for this to work the ISR
// overhead must be less than the
// low time. For this program the
// overhead is 45 instructions. The
// low time must then be at least
// 9 us.
#int_ccp1
void isr(){
rise1 = get_timer1();
rise2 = ccp_1;
set_timer1(0);//reset timer early here
}//isr
//main_________________________________________________________
void main(){
lcd_init();
//setup ccp1 to capure on rising edge
setup_ccp1(CCP_CAPTURE_RE); // Configure CCP1 to capture rise
enable_interrupts(int_ccp1);
enable_interrupts(global);
setup_timer_1(T1_INTERNAL); // Start timer 1
//10 Khz:
//setup pwm on ccp2 @ 10 Khz
//clk_spd/(4*divider*period*postscale)
//20Mhz/(4*4*125*1) = note period = (period-1) zero based
//104.1 khz
setup_ccp2(CCP_PWM);
setup_timer_2(T2_DIV_BY_4, 125, 1);
set_pwm2_duty(62);
while(TRUE) {
delay_ms(500);
printf(lcd_putc,"\ft1: %lu\nt2: %lu",rise1,rise2);
}//while
}//main
|
|
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Mon Feb 25, 2008 4:07 pm |
|
|
You are adding back the ISR calling overhead, by resetting the timer.
The hardware CCP, records the timer count, at the moment the edge went high. It is doing this, _from_ the point in the ISR, where you reset the timer. You need to leave the timer free running, and hold the last recorded value, together with the new reading. Then the difference between these, is the time between the rising edges. By resetting the timer, you are removing the advantage of the CCP...
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
Guest
|
|
Posted: Wed Feb 27, 2008 12:24 pm |
|
|
Thanks so much for the great input Ttelmah,
I have done as you suggested and the get the correct timing, but i am having a little bit of trouble understanding the underlying functionality of what is happening. I don't know if you might be able to shed some more light on the topic? I have searched on the forum and see there are regularly questions similar to this topic of timing and ISR's, but not found much for concrete explanation as to the way that it works.
Some of my main questions are:
1. It has been stated that when using the ccs compiler that there are approx. 45 intructions overhead on PIC16xxxx and ~ 80 instructions on PIC18xxxx. I can take this for fact, but i do not understand why in the PIC datasheet it states that there are only 3-3.75 instructions of overhead necessary at the assembly level (section 8.3 of mid-range family reference manual). Maybe my it is my interpretation of overhead that is the problem? I think of overhead as the amount of time used by the code that causes a latent effect in processing.
The updated code i have written measures the difference in timing between using the ccp_1 value (define measure_2) and using the timer value inside of the ISR(define measure_1). The difference of 4 shows up between the measured values. This seems to be consistent with what is stated in the user guide of a 3.75 Tcy latency of entering the ISR.
It would be great if we could a sticky somewhere of a comprehensive use of the ISR and errors that can be caused by using the set_timer() inside of the ISR, I also read about the using of simply forwarding the timer rather than reseting the timer. I would be great to have a clear picture with regard to these topics.
Thanks so much for helping lowly in understanding
Here is the updated testing code i used:
Code: |
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#include "lcd.c"
//global vars___________________________________
int16 t1,t2,t3,t4;
int16 fall,pulse_width;
int16 complete = 0;
int1 start=0;
#define measure_1
//#define measure_2
//ccp1 isr___________________________________________________
//found the difference of ccp1 capture
//and when the timer1 value of entry to
//be 38 Tcy when set_timer1 is not used
#int_ccp1
void isr(){
#ifdef measure_2
//measuring the pulse width without ccp_1
//=504 without reseting the timer1
if(start==0){
t3 = ccp_1;
start = 1;
}
else if(start==1){
t4 = ccp_1;
pulse_width = t4-t3;
start = 0;
}
#endif
#ifdef measure_1
//measuring the pulse width without ccp_1
//=507 without reseting the timer1
if(start==0){
t1 = get_timer1();
start = 1;
}
else if(start==1){
t2 = get_timer1();
pulse_width = t2-t1;
start = 0;
}
#endif
//set_timer1(0);
}//isr
//main_______________________________________________________
void main(){
//ccp1 timer1 setup___________________________
setup_ccp1(CCP_CAPTURE_RE); // Configure CCP1 to capture rise
enable_interrupts(int_ccp1);
enable_interrupts(global);
setup_timer_1(T1_INTERNAL); // Start timer 1
//ccp2 timer2 setup___________________________
//10 Khz:
//setup pwm on ccp2 @ 10 Khz
//clk_spd/(4*divider*period*postscale)
//20Mhz/(4*4*125*1) = note period = (period-1) zero based
//104.1 khz
setup_ccp2(CCP_PWM);
setup_timer_2(T2_DIV_BY_4, 125, 1);
set_pwm2_duty(62);//~50% duty cycle
//start lcd
lcd_init();
printf(lcd_putc,"started....");
while(1){
#ifdef measure_1
//print value incrementally
printf(lcd_putc,"\ft1:%lu t2:%lu\ndiff:%lu",t1,t2,pulse_width);
delay_ms(500);
#endif
#ifdef measure_2
//print value incrementally
printf(lcd_putc,"\ft3:%lu t4:%lu\ndiff:%lu",t3,t4,pulse_width);
delay_ms(500);
#endif
}//while
}//main |
|
|
![](templates/subSilver/images/spacer.gif) |
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Wed Feb 27, 2008 12:48 pm |
|
|
Anonymous wrote: |
1. It has been stated that when using the ccs compiler that there are approx. 45 intructions overhead on PIC16xxxx and ~ 80 instructions on PIC18xxxx. I can take this for fact, but i do not understand why in the PIC datasheet it states that there are only 3-3.75 instructions of overhead necessary...
|
That is because the CCS overhead includes saving the W register, the Status register, certain temporaries, and then figuring out which interrupt it was that caused this interrupt. Now you can define a #GLOBAL INTERRUPT routine in your code and then the overhead would be the 3-3.75 instructions, but then you would be responsible for knowing what you needed to save and how you needed to determine which interrupt it was. This can be great if you know ahead of time that there is only one interrupt enabled and you know that you won't be using any CCS library functions in your ISR. Then you would be skipping the saving of the work temporaries that the CCS library functions use, but if you are not sure you can get away with this shortcut, it is best not to use it. In any case, what you are trying to do should not depend on the exact overhead involved in getting into the ISR if you do it right.
Quote: |
The updated code i have written measures the difference in timing between using the ccp_1 value (define measure_2) and using the timer value inside of the ISR(define measure_1)...
|
This might be interesting for curiosity sake, but if you want to measure the period of a waveform, you don't need to be concerned about this, as long as the overhead is much less than 100 usec. (the expected period you are trying to measure). You get a CCP interrupt on the rising edge. Save the CCP_1 value somewhere in RAM. This does not depend on latency. Then you get another CCP interrupt on the next rising edge. Again you read the CCP_1 value and subtract off the previously saved CCP_1 value. This also does not depend on latency. The difference is the period, and you don't need to take latency into account. You don't need to read the timer in your ISR. You don't need to reset the timer. You don't need to adjust the timer in any way. Simple.
Robert Scott
Real-Time Specialties |
|
![](templates/subSilver/images/spacer.gif) |
fvnktion
Joined: 27 Jun 2006 Posts: 39
|
|
Posted: Wed Feb 27, 2008 2:33 pm |
|
|
Thanks Robert,
I believe you are right in you last comment. I like using the timer ISR just for practice, as it seems to be used for a little bit of everything, which is the reason i really want to know how the innards are working.
Is there a listing of overhead per function call from ccs in any documentation? I don't seem to be able to find anything myself. I know i can view the listing file and do some good old fashion counting, but this can be error prone and time consuming. A listing of overhead would be nice. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Wed Feb 27, 2008 4:13 pm |
|
|
Unfortunately, there is no simple answer to most overheads. It'll change massively with circumstances. For example, a function that is called once, will generally be generated 'inline', removing the overhead to store temporary values. The same function called with a constant, will be just handed the values, while calling with a variable will involve fetching the values concerned. The time for this will change with where the values are stored in memory (this is why if you have variables that are used a lot, and are important for timings, it is worth declaring them 'early' in the code), and also with the nature of the variables, and conversions involved (so if you use a float variable with a function expecting an int, you will have the overhead of reading the float, extracting the integer part, and then using this).
In some cases, the time will also depend on the values concerned (some of the maths operations in particular).
So there are no simple 'this will take xxx uSec' answers universally applicable for any given function.
The best way of evaluating timings, is to run the applicable part of the program, either in a hardware ICE, which supports outputting trigger pulses at particular points (easiest), or in something like the MPLAB simulator and using it's 'stopwatch' function (free, but requires quite a lot of setting up if dealing with complex code), or just reading the assembler, and counting the instructions (hardest).
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|