View previous topic :: View next topic |
Author |
Message |
oxxyfx
Joined: 24 May 2007 Posts: 97
|
CCP1 question |
Posted: Mon Aug 27, 2012 9:05 am |
|
|
Hello,
I am working on a project where I have to accurately measure the width of an incoming pulse. I chose the PIC12F1840 for this project, it seems quite a versatile little PIC.
My compiler version is 4.135.
I have connected the incoming signal to PIN A2 and I enabled the interrupt for the RE.
Code: |
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); //13.1 ms overflow
setup_ccp1(CCP_CAPTURE_RE);
.....
.....
.....
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
|
The interrupt routine looks like this:
Code: |
#int_CCP1
void CCP1_isr(void)
{
rise = CCP_1;
While(Input(Signal)){};
fall = get_timer1();
pulse_width = fall - rise;
iflag = 1;
}
|
Which works well, however in the many posts I have seen it looks like there is a way to do this with interrupts only - but those refer to setting up the CCP2 - which I do not have.
Is there a better way of measuring the width of the pulse perhaps triggering the interrupt on the falling edge as well?
Will the results be the same as what I get right now or more precise?
Thank you. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Mon Aug 27, 2012 9:26 am |
|
|
You can just change the programming of CCP1, to be on the falling edge at the start of the interrupt. So the sequence becomes:
1) Read the CCP, into ccp_val[flag].
2) Test flag - if set, this is the second edge, goto 4
3) Change the interrupt edge, and set the flag - exit
4) Now the time is ccp_val[1]-ccp_val[0]. If this is -ve add 65536.
5) clear flag, and exit.
Big advantage is you won't be stuck waiting in the interrupt. If (for instance), the signal does not drop, you would be stuck forever waiting in
While(Input(Signal)){};
Code for doing this with a single CCP has been posted here in the past. A search should find it.
It'll be slightly more accurate (the latency of the while loop).
Best Wishes |
|
|
oxxyfx
Joined: 24 May 2007 Posts: 97
|
|
Posted: Mon Aug 27, 2012 10:02 am |
|
|
Thank you,
so this is the code I came up with based on your reply:
Code: |
#int_CCP1
void CCP1_isr(void)
{
If (!ccpflag){
rise = CCP_1;
ccpflag = 1;
setup_ccp1(CCP_CAPTURE_FE);
}
Else {
fall = CCP_1;
pulse_width = fall - rise;
iflag = 1;
ccpflag = 0;
setup_ccp1(CCP_CAPTURE_RE);
}
}
|
where the ccpflag is initialized as an int8 and given a zero value when the program starts.
I wasn't sure I can change the setup of the CCP_CAPTURE once the interrupt has been enabled.
So will this work? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Mon Aug 27, 2012 1:13 pm |
|
|
Yes, you can change the capture mode at any time.
Only problem you will have, is if the timer has overflowed between reading the rising edge, and the falling edge. Then fall will be less than rise, and you need to wrap the arithmetic.
Best Wishes |
|
|
oxxyfx
Joined: 24 May 2007 Posts: 97
|
|
Posted: Mon Aug 27, 2012 1:27 pm |
|
|
Thank you.
So my Timer 1 is defined as:
Code: |
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); //13.1 ms overflow
|
and the oscillator is set to internal, 4Mhz:
Code: |
setup_oscillator(OSC_4MHZ|OSC_INTRC|OSC_PLL_OFF,0);
|
Since my pulse is always less than 2ms, theoretically the time should never overflow.
So here is where I am confused gain, usually I do a
to initialize the timer that way it never overflows. How about in the case above? The timer is rolling and the:
just grabs the exact time value - and on the next read the next time value? If that is the case it can overflow indeed. In that case I will need to create an interrupt routine for the Timer 1 overflow as well? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Mon Aug 27, 2012 2:37 pm |
|
|
Not true.
Remember the timer is running all the time, and is independant of your pulse. It could wrap on the very next clock cycle after the rising edge is detected, or at any time between the two edges.
Best Wishes |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Aug 27, 2012 4:16 pm |
|
|
Quote: |
accurately measure
|
as somebody who deals with issues like this all the time -
the natural question is HOW accurate????
you never mentioned a spec for resolution of the reading
Or
acceptable jitter ( error) between adjacent / (or sets of) readings
Or
or indicate how the 4mhz clock was chosen - i suspect because you WONT BE ALL that accurate with the INTRC clock LOL !!!!
accuracy and the INTRC simply dont go together.
there are factors in play here that depend on your Fosc value too.
too a low frequency master clock for the required resolution
can play havoc with the readings you get -
no matter what acquisition coding approach you use.
just my 2 cents |
|
|
oxxyfx
Joined: 24 May 2007 Posts: 97
|
|
Posted: Mon Aug 27, 2012 4:19 pm |
|
|
Thank you,
so what is the value the timer 1 overflows at? The compiler says it will overflow every 13.1ms, which would be 13100 micro s.
S if I out an IF statement there to check the value of the "rise" compared to the value of "fall", and if the "fall" is smaller than it is obvious it was an overflow.
In that case I could compute the exact value of the pulse by knowing where was the overflow value.
So is it overflowing at 13100? |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Aug 27, 2012 4:29 pm |
|
|
Quote: |
what is the value the timer 1 overflows at?
|
It will be 0xFFFF on the clock cycle BEFORE it overflows
and no other.
The PRESET time for the timer, determines the actual period
you get before said overflow.
If you preset the timer to 0xfffe with a DIV_1 prescalar
there is only ONE clock input to overflow.
That auto preset is what the PWM does for you on timer2,
in nice efficient hardware. |
|
|
oxxyfx
Joined: 24 May 2007 Posts: 97
|
|
Posted: Mon Aug 27, 2012 4:54 pm |
|
|
Thank you, no offense but that sounds like chinese to me
So that all said the clock overflows at 65535.
I do not have a preset timer, I am assuming that would mean:
set_timer1(0xfffe)
but I do not have that. Since I am using a 4Mhz internal clock with a div_1 as you previously mentioned and a delay clock of 4Mhz as well, what is the highest decimal value the
fall = CCP1
can have? Is it 65535 or 13100? |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Aug 27, 2012 5:07 pm |
|
|
One clock to overflow will not work in reality
The code overhead of setting 0xfffe will allow an overflow after the NEXT 2 instructions in your program. ie 2 normal instructions later....
You have to get a GRIP on the time domain implications of what you are trying to do and THINK harder.
So what resolution do you need?
What absolute accuracy?
How much delta / jitter can you accept in the data points you get ?
Surely you can see that higher clock rates can provide better resolution,
and as i said the 4 mhz INTRC clock is pretty coarse in the time increment department too-
and
orders of magnitude less absolutely accurate than a crystal source - as well as more jittery over time and temp.
Accurate and INTRC don't go in the same sentence - really....... |
|
|
oxxyfx
Joined: 24 May 2007 Posts: 97
|
|
Posted: Mon Aug 27, 2012 5:11 pm |
|
|
I am happy with the resolution the 4Mhz provides.
So what is the maximum value I am looking for? |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Mon Aug 27, 2012 5:49 pm |
|
|
the BEST THEORETICAL resolution you can hope for - using a crystal oscillator at 4 mhz HAS TO BE much greater than 1 usec ,
tho in practice this may be impossible to achieve -
i would suggest that 1 usec is therefore your probable jitter spec.
with INTRC , absolute jitter is sure to be worse than that
as to the HOW of it:
everything i know about it -
i first learned from section 11.3 of the 16f887 data sheet .
my understanding of it was cemented by building and testing circuits.
"11.3" is an excellent guide to the task you are undertaking .
do check it out.
from a PRACTICAL standpoint
due to interrupt latency , and the likely count of the instrux U will need to handle the capture - and with a div_1 for timer1 ::
i'd say the MIN pulse you can catch is somewhere > 100usec and obviously r <65536 usec, right ? |
|
|
oxxyfx
Joined: 24 May 2007 Posts: 97
|
|
Posted: Mon Aug 27, 2012 8:06 pm |
|
|
My pulses are between 1ms and 2ms. It is a standard RC receiver output.
The minimum pulse is not an issue, the incoming pulse never goes under 7-800us and the maximum never goes above 22-2300us.
However the pulses repeat themselves every 20-22.5ms and I am capturing every single one of them. These go into a rolling buffer and get averaged for the output. I am just trying to minimize the error pulses, so I have two options. When there is a buffer overflow I can throw out that pulse and ignore it - or I can calculate the correct length with the following formula:
Code: |
pulse_width = 65535-rise+fall;
|
and it looks like the value I was looking for is 65535 I found that with trial and error.
Thanks for your help. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Tue Aug 28, 2012 4:01 am |
|
|
Your accuracy will go _down_ if you set the clock in the interrupt.
Problem is that it takes significant _time_ to get into the interrupt handler.
The whole 'point' about the CCP, is that it (without you having to do anything), records the timer reading when an edge occurs. This is then available for you to read at any point till it updates again. When you arrive in the handler, it is probably already about 30 clocks _after_ the recording was made.
The approach you already have, should be fine, and as good as your are going to get from the hardware, with 1000 possible values between 1mSec, and 2mSec.
That you are getting chatter at all in the signal, suggests you are using a fairly 'basic' RC. Most modern digital sets, will hold the output pulse accurate to tiny fractions, if the input source is stable, and will maintain that accuracy, even if the transmission is interrupted (usually switching to an 'emergency' output setting if the break is longer than a few seconds).
However, if you have chatter, something like an Olympic average, may be far better suited to this application, than a simple sum and divide average.
Best Wishes |
|
|
|