|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 12, 2007 11:46 am |
|
|
The problem with using CCP1 is that with an input pulse of 500ms
you are near the maximum pulse width that the hardware can
capture (with a 4 MHz oscillator). If you have an input pulse of say,
550 ms, or 600 ms, it won't work.
Suppose you use the maximum Timer1 prescaler value of 8.
Timer1 can count up to 65535.
This is the equation to calculate the pulse width in ms.
Code: |
CCP delta
pulse width in ms = -----------------------------------------
Osc Freq / ( 4 * Timer1 prescaler * 1000)
|
Plug the maximum numbers into it:
Code: | 65535
max pulse width = ----------------------- = 524 ms
4 MHz / (4 * 8 * 1000)
|
You could capture a longer pulse, but you would have to extend Timer1
to 24 or 32 bits. I don't want to do that right now. I don't have the
time.
This code will capture a positive pulse of 1 to 524 ms.
Code: |
#include <16F877.h>
#fuses XT, NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
int8 capture_rising_edge;
int8 got_pulse_width;
int16 ccp_delta;
#int_ccp1
void ccp1_isr(void)
{
static int16 t1_rising_edge;
// If current interrupt is for rising edge.
if(capture_rising_edge)
{
setup_ccp1(CCP_CAPTURE_FE);
capture_rising_edge = FALSE;
t1_rising_edge = CCP_1;
}
else
{
setup_ccp1(CCP_CAPTURE_RE);
capture_rising_edge = TRUE;
ccp_delta = CCP_1 - t1_rising_edge;
got_pulse_width = TRUE;
}
}
//====================================
main()
{
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_rising_edge = TRUE;
setup_ccp1(CCP_CAPTURE_RE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = local_ccp_delta / 125;
printf("%lu ms \n\r", pulse_width_ms);
got_pulse_width = FALSE;
}
}
} |
-----
Edited to disable interrupts while loading the ccp_delta into a local variable.
Last edited by PCM programmer on Fri Oct 12, 2007 1:51 pm; edited 1 time in total |
|
|
Kova
Joined: 06 May 2007 Posts: 35
|
|
Posted: Fri Oct 12, 2007 12:15 pm |
|
|
PCM programmer wrote: |
CUT
This code will capture a positive pulse of 1 to 524 ms.
CUT
|
Ohhh thanks a lot PCM programmer for the help
You are so kind to everybody
An ultimate thing: If I want to to capture a negative pulse I must invert all the setup_ccp1 ?
At limit I can use a bjt to invert the pulse in positive ;)
Thanks another time for your time and for the code.
Bye ;) |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 12, 2007 12:39 pm |
|
|
To capture a negative pulse width, you just flip everything.
Also, I've improved the loop code, so that it loads the ccp delta
value into a local variable before using it.
Here is the negative pulse program:
Code: | #include <16F877.h>
#fuses XT, NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
int8 capture_falling_edge;
int8 got_pulse_width;
int16 ccp_delta;
#int_ccp1
void ccp1_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp1(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_1;
}
else
{
setup_ccp1(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_1 - t1_falling_edge;
got_pulse_width = TRUE;
}
}
//====================================
main()
{
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_falling_edge = TRUE;
setup_ccp1(CCP_CAPTURE_FE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = local_ccp_delta / 125;
printf("%lu ms \n\r", pulse_width_ms);
}
delay_ms(500);
}
} |
|
|
|
ghadabeydoun
Joined: 09 Nov 2010 Posts: 1
|
reception and reconstruction of a signal |
Posted: Tue Nov 09, 2010 3:39 am |
|
|
hello everybody,
I am trying to write a program using CCP that receives a signal and measures the pulse width (negative and positive) and then using this information I will reconstruct the signal at the output of the program. Any help!! : |
|
|
lasfclestat
Joined: 20 Apr 2011 Posts: 2 Location: Campinas, SP - Brazil
|
|
Posted: Wed Apr 20, 2011 7:56 am |
|
|
hello ...
Using the example posted earlier I tried to follow the example below, I can generate a square wave with period of 26ms, but I am not able to generate the CCP1 interrupt, you are missing something in code?
Thanks
Code: |
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=20000000,RESTART_WDT)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
int8 capture_rising_edge;
int8 got_pulse_width;
int16 ccp_delta;
int flag = 0;
#INT_TIMER2
void TIMER2_isr(void) //timer2 interrupt
{
if (flag)
{
output_high(pin_c0);
flag = 0;
} else
{
flag = 1;
output_low(pin_c0);
}
clear_interrupt(int_timer2);// clear timer2 interrupt's flag
}
#int_ccp1
void ccp1_isr(void)
{
static int16 t1_rising_edge;
// If current interrupt is for rising edge.
if(capture_rising_edge)
{
setup_ccp1(CCP_CAPTURE_FE);
capture_rising_edge = FALSE;
t1_rising_edge = CCP_1;
}
else
{
setup_ccp1(CCP_CAPTURE_RE);
capture_rising_edge = TRUE;
ccp_delta = CCP_1 - t1_rising_edge;
got_pulse_width = TRUE;
}
}
//====================================
main()
{
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_rising_edge = TRUE;
setup_ccp1(CCP_CAPTURE_RE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
setup_timer_2(T2_DIV_BY_16,255,16);
enable_interrupts(INT_TIMER2);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = local_ccp_delta / 125;
printf("%lu ms \n\r", pulse_width_ms);
got_pulse_width = FALSE;
}
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Apr 20, 2011 1:25 pm |
|
|
I made it work. Here's what I did:
1. Your generated signal on pin C0 is actually running at approx. 38 Hz.
The Timer2 interrupts are occurring at a 76 Hz rate. A half-cycle of the
waveform on pin C0 takes about 13 ms. This is the pulse width that you
are trying to measure.
2. You must have a wire connecting pin C0 to pin C2 on your PIC.
This connects the 38 Hz squarewave to the CCP1 input. Do you have it ?
3. You get a compiler warning about main() not returning a value.
Fix that by declaring main() as void. Example:
4. You're using a 20 MHz crystal. The example uses a 4 MHz crystal.
The pulse width equation must be adjusted for this. Timer1 will count
up 5x faster with a 20 MHz crystal, compared to a 4 MHz crystal.
Therefore, you need to divide the CCP value by 5, to compensate for
the faster crystal, as shown below in bold:
Quote: |
pulse_width_ms = local_ccp_delta / (125 * 5); |
5. Your update speed on the terminal window is too fast. It scrolls the
terminal too fast. Add a delay to the main loop to slow it down, as shown
in bold below:
Quote: |
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = local_ccp_delta / (125 * 5);
printf("%lu ms \n\r", pulse_width_ms);
got_pulse_width = FALSE;
delay_ms(500);
}
|
I did all that and it works. I get this output in the TeraTerm window:
Quote: |
13 ms
13 ms
13 ms
13 ms
13 ms
13 ms
13 ms
13 ms |
This was tested with compiler version 4.119 on a PicDem2-Plus board. |
|
|
lasfclestat
Joined: 20 Apr 2011 Posts: 2 Location: Campinas, SP - Brazil
|
|
Posted: Wed Apr 20, 2011 2:03 pm |
|
|
Okay, I understand ...
Yes I'm connected the Pin C0 to C2
I'll make the changes, and test again, then I submit the result.
Thanks |
|
|
tienchuan
Joined: 25 Aug 2009 Posts: 175
|
|
Posted: Wed Nov 13, 2013 1:34 am |
|
|
PCM programmer wrote: | To capture a negative pulse width, you just flip everything.
Also, I've improved the loop code, so that it loads the ccp delta
value into a local variable before using it.
Here is the negative pulse program:
Code: | #include <16F877.h>
#fuses XT, NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
int8 capture_falling_edge;
int8 got_pulse_width;
int16 ccp_delta;
#int_ccp1
void ccp1_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp1(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_1;
}
else
{
setup_ccp1(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_1 - t1_falling_edge;
got_pulse_width = TRUE;
}
}
//====================================
main()
{
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_falling_edge = TRUE;
setup_ccp1(CCP_CAPTURE_FE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = local_ccp_delta / 125;
printf("%lu ms \n\r", pulse_width_ms);
}
delay_ms(500);
}
} |
|
Hi all
I tried this code to test capture pulse by used IC555 pulse generator(40ms) .
In my project, I used crystal 10MHz, so that I edit:
In fuse config
Code: |
#fuses HS, NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(clock=10000000)
|
In main code:
Code: |
pulse_width_ms = local_ccp_delta * 0.0032; (ccp_delta *( 1/(10MHz)*(4*8*1000) ) )
|
But the result always change and failure.
Code: |
12 ms
13 ms
14 ms
12 ms
14 ms
9 ms
12 ms
12 ms
32 ms
34 ms
50 ms
11 ms
10 ms
33 ms
33 ms
50 ms
33 ms
50 ms
10 ms
31 ms
33 ms
50 ms
10 ms
30 ms
10 ms
10 ms
33 ms
33 ms
10 ms
10 ms
33 ms
33 ms
33 ms
50 ms
10 ms
10 ms
33 ms
50 ms
10 ms
30 ms
10 ms
32 ms
33 ms
50 ms
10 ms
32 ms
33 ms
50 ms
33 ms
33 ms
50 ms
32 ms
32 ms
10 ms
32 ms
33 ms
33 ms
50 ms
33 ms
30 ms
10 ms
10 ms
31 ms
33 ms
33 ms
36 ms
31 ms
10 ms
32 ms
33 ms
10 ms
10 ms
33 ms
50 ms
34 ms
50 ms
10 ms
33 ms
33 ms
10 ms
30 ms
33 ms
10 ms
33 ms
33 ms
33 ms
50 ms
33 ms
33 ms
33 ms
50 ms
10 ms
33 ms
30 ms
10 ms
10 ms
33 ms
33 ms
33 ms
32 ms
33 ms
33 ms
10 ms
10 ms
33 ms
33 ms
35 ms
50 ms
10 ms
...
10 ms
|
Pls show me way to fix it.
Thanks all _________________ Begin Begin Begin !!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Wed Nov 13, 2013 3:26 am |
|
|
First, you are copying ccp_delta, to local_ccp_delta, with the interrupts disabled, as shown in the code?.
Second, what is this?:
(ccp_delta *( 1/(10MHz)*(4*8*1000) ) )
If it's a remark, it needs // in front
Then you are making a fundamental change to how the code works. /125, forces an integer division, and uses integer maths, to give an integer result. Total time about 140uSec. Multiplying by 0.0032, forces the integer to be converted to float, and then fp maths, and then the result to be converted back to integer. Total time about 500uSec...
Use /500. Though division is slower for a given maths type than multiplication, an int16 division is a lot faster than floating point multiplication, and two lots of conversion.
Best Wishes |
|
|
tienchuan
Joined: 25 Aug 2009 Posts: 175
|
|
Posted: Wed Nov 13, 2013 8:42 am |
|
|
Thanks Ttelmah.
Code: | (ccp_delta *( 1/(10MHz)*(4*8*1000) ) ) |
Yes, It it's a remark.
In my project change use crystal 10MHZ so that I must calcualtor this constant.
Oh,I thought failure,, I'm careless when using division to change the code, because I always think division math is slower than multiplication. You pls show me way to calculate time for math operation.
Added, when i test with this code, it has return value when i disconnect pulse clock in CCP1 pin, If true, it must no return value,so that I think need to clear int_ccp1 flag?
here is my full code I changed flow your idea:
Code: |
#include <16F877A.h>
#fuses HS, NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
int8 capture_falling_edge;
int8 got_pulse_width;
int16 ccp_delta;
#int_ccp1
void ccp1_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp1(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_1;
}
else
{
setup_ccp1(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_1 - t1_falling_edge;
got_pulse_width = TRUE;
}
}
//====================================
main()
{
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_falling_edge = TRUE;
setup_ccp1(CCP_CAPTURE_FE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = (local_ccp_delta * 32) /10000 ;
printf("%lu ms \n\r", pulse_width_ms);
}
delay_ms(500);
}
}
|
_________________ Begin Begin Begin !!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Wed Nov 13, 2013 9:22 am |
|
|
It's not INT_CCP1 that is causing it to display with no input. You are not clearing the flag 'got_pulse_width' when you print.
Code: |
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
got_pulse_width=FALSE; //need this or it'll keep printing....
pulse_width_ms = (local_ccp_delta * 32) /10000 ;
printf("%lu ms \n\r", pulse_width_ms);
}
delay_ms(500);
}
|
On times, CCS has a table in the manual 'how much time do maths operations take', which can be scaled for different clock rates, but the simplest solution is to just use a pulse output and time the actual duration, or use the stopwatch feature in MPLAB.
There is one problem with *32/10000. The result of *32, won't fit in an int16, so for longer times, the values out the top will be lost (65mSec max). If this matters you'll need to cast to an int32.
Best Wishes |
|
|
tienchuan
Joined: 25 Aug 2009 Posts: 175
|
|
Posted: Wed Nov 13, 2013 8:34 pm |
|
|
Thanks u, Ttelmah.
Well, It worked
But it seem has error when i disconnect pulse in CCP1, It has return value???
And is here my code edited follow your comment:
Code: |
#include <16F877A.h>
#fuses HS, NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
int8 capture_falling_edge;
int8 got_pulse_width;
int32 ccp_delta;
#int_ccp1
void ccp1_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp1(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_1;
}
else
{
setup_ccp1(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_1 - t1_falling_edge;
got_pulse_width = TRUE;
}
}
//====================================
main()
{
int32 pulse_width_ms;
int32 local_ccp_delta;
got_pulse_width = FALSE;
capture_falling_edge = TRUE;
setup_ccp1(CCP_CAPTURE_FE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
got_pulse_width= FALSE;
pulse_width_ms = (local_ccp_delta * 32) /10000;
printf("%lu ms \n\r", pulse_width_ms);
}
delay_ms(500);
}
}
|
Thanks u so much. _________________ Begin Begin Begin !!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Thu Nov 14, 2013 1:28 am |
|
|
No pulse = line floating (unless you have arranged a pull-up resistor). May start picking up RF/mains interference....
Best Wishes |
|
|
tienchuan
Joined: 25 Aug 2009 Posts: 175
|
|
Posted: Sat Nov 16, 2013 8:05 pm |
|
|
Well, I worked exactly:)
I added config in main code:
Follow instructions in datasheet:
Quote: | If the RC2/CCP1 pin is configured as an output, a write to the port can cause a Capture condition |
Thanks u very much _________________ Begin Begin Begin !!! |
|
|
|
|
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
|