View previous topic :: View next topic |
Author |
Message |
irmanao
Joined: 08 Apr 2015 Posts: 77
|
Problem measuring pulse width with one CCP |
Posted: Sun Sep 10, 2017 7:29 am |
|
|
I'm having a problem measuring a negative pulse width coming from an IR receiver (tsop31230) in order to distinguish two different negative pulses sent from the transmitter. One with a negative pulse width of 1.3ms and the other of 2.3ms. The delay (dms) is increased or decreased (for the purpose of a dimmer) according to the different pulses. However the lamp starts to behave erratically when there are pulses being received... ccs v.5.008
Code: |
#include <16F1827.h>
#fuses NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(internal=4000000)
int8 capture_falling_edge;
int8 got_pulse_width;
int16 ccp_delta;
int8 dms=6;
#int_ccp4
void ccp4_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp4(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_4;
}
else
{
setup_ccp4(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_4 - t1_falling_edge;
got_pulse_width = TRUE;
}
}
//====================================
main()
{
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_falling_edge = TRUE;
clear_interrupt(INT_EXT);
ext_int_edge(1, H_TO_L);
enable_interrupts(INT_EXT);
setup_ccp4(CCP_CAPTURE_FE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
enable_interrupts(INT_CCP4);
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;
}
if (pulse_width_ms>2){
dms--;
if (dms<2){dms=2;}
}
if (pulse_width_ms<2){
dms++;
if (dms>7) {dms=7;}
}
}
}
#INT_EXT
void ext_int (void){
delay_ms(dms);
output_high(PIN_A0);
delay_us(10);
output_low(PIN_A0);
}
|
The code is taken from a PCM Programmer's post. Any ideas?
thanks |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Sep 10, 2017 9:38 am |
|
|
You have a really early vs. 5 compiler. It may not even support CCP4.
I didn't check.
Is this a Proteus project or is it in real hardware ? If Proteus, it may
not support CCP4. Or it may not work anyway.
Assuming it's real hardware,
1. What is the Vdd voltage of your PIC ?
2. Are the negative pulses full scale ?
i.e., are the high and low signal levels going from 0v to Vdd ?
3. Are the negative pulses clean, square, digital waveforms ?
Or do they have a large RC time constant curve on their edges ?
4. What triggers the External interrupt ? Is it a push-button switch ? |
|
|
irmanao
Joined: 08 Apr 2015 Posts: 77
|
|
Posted: Sun Sep 10, 2017 10:06 am |
|
|
This is in real hardware. The compiler does support CCP4 as i used it in a different project. Vdd is 5 Volts. The pulses are full scale and very clean. The external interrupt is being triggered by the zero crossing pulses from a 4n38.
When i plug in my board the lamp does stay on dimmed with the pre-selected delay (dms=6). However when ir signal arrives at the tsop and the PIC, i am not getting the correct results.
thanks |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Sep 10, 2017 2:41 pm |
|
|
First get rid of your dms++, #int_ext, etc., code.
Use serial output from the PIC with a printf statement to display the
pulse width output on a terminal window on the PC. This will prove if
the CCP4 capture is working correctly in this program. Maybe it worked
correctly in some previous project, but first prove that it works OK in
the current program.
If that works, add the dms++, dms-- code. Use a printf statement to
display the value of dms. See if that works correctly with the CCP code.
And also, put a delay_ms(500) at the end of your while(1) loop. This will
slow it down enough to see the results of the above tests. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Sep 12, 2017 12:58 am |
|
|
I did this test.
I used two of the older style PicDem2-Plus boards. One board has an
18F46K22 and it generates the 1.3ms and 2.3ms pulses. I have jumper
bringing the pulses over to a 2nd PicDem2-Plus board that has a 16F1827
on it. I also have a ground jumper between the two boards. Both boards
run at +5v. I first tested that it works with vs. 5.074. Then I installed
vs. 5.008 (your version) and it works with that one too. Vs. 5.008
was only used to compile the 16F1827 program.
It displays the following output on the TeraTerm window. Based on the
pulses sent by the other PicDem2-Plus board, this output is correct:
Code: |
1
2
1
2
1
2
1
2
1
2
1
2
1
2
|
This proves that the CCP4 pulse width capture is working OK, and
whatever problem exists, it's in your dms code or your #int_ext code.
Also, you didn't tell us what the repetition rate of your pulses are.
I chose a pulse spacing that convenient for me. You can see that
in the 18F46K22 pulse generation code further below.
Here is the pulse width capture program. It's your program with the
dms code and the #int_ext code commented out. All I wanted to do was
verify that the pulse width capture works. The output is sent to a PC
through a DB-9 serial cable and is displayed on a TeraTerm window.
Code: | #include <16F1827.h>
#fuses NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(internal=4000000)
#use rs232(baud=9600, UART1, ERRORS)
int8 capture_falling_edge;
int8 got_pulse_width;
int16 ccp_delta;
// int8 dms=6;
#int_ccp4
void ccp4_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp4(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_4;
}
else
{
setup_ccp4(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_4 - t1_falling_edge;
got_pulse_width = TRUE;
}
}
//====================================
void main()
{
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_falling_edge = TRUE;
// clear_interrupt(INT_EXT);
// ext_int_edge(1, H_TO_L);
// enable_interrupts(INT_EXT);
setup_ccp4(CCP_CAPTURE_FE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8 );
enable_interrupts(INT_CCP4);
enable_interrupts(GLOBAL);
while(TRUE)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = local_ccp_delta / 125;
got_pulse_width = FALSE; // *** ADDED THIS LINE ***
printf("%lu\n\r", pulse_width_ms);
}
delay_ms(500);
/*
if (pulse_width_ms>2){
dms--;
if (dms<2){dms=2;}
}
if (pulse_width_ms<2){
dms++;
if (dms>7) {dms=7;}
}
*/
}
}
/*
#INT_EXT
void ext_int (void){
delay_ms(dms);
output_high(PIN_A0);
delay_us(10);
output_low(PIN_A0);
}
*/
|
Here is the pulse generation program. It's very simple.
Code: |
#include <18F46K22.h>
#fuses INTRC_IO, NOWDT, BROWNOUT, PUT, NOPBADEN
#use delay(clock=4M)
#define PULSE_OUT_PIN PIN_D7
//======================================
void main()
{
output_high(PULSE_OUT_PIN); // Init pin to inactive level
while(TRUE)
{
output_low(PULSE_OUT_PIN);
delay_us(1300); // 1.3 ms negative pulse
output_high(PULSE_OUT_PIN);
delay_ms(500); // Wait 1/2 second between pulses
output_low(PULSE_OUT_PIN);
delay_us(2300); // 2.3 ms negative pulse
output_high(PULSE_OUT_PIN);
delay_ms(500); // Wait 1/2 second between pulses
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Sep 13, 2017 1:05 am |
|
|
One more thing about your program, which I think comes from here:
http://www.ccsinfo.com/forum/viewtopic.php?t=31470&start=6
The isr in your posted code sets a flag when the capture is complete.
Quote: |
#int_ccp4
void ccp4_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp4(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_4;
}
else
{
setup_ccp4(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_4 - t1_falling_edge;
got_pulse_width = TRUE;
}
} |
But then in your main loop, you don't ever clear the flag. The code in
the link does clear the flag. You left that part out and it's probably
the main cause of your problems:
Code: |
while(1)
{
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(GLOBAL);
pulse_width_ms = local_ccp_delta / 125;
}
.
.
.
}
|
|
|
|
irmanao
Joined: 08 Apr 2015 Posts: 77
|
|
Posted: Wed Sep 13, 2017 9:19 am |
|
|
Thanks a lot for your replies and time PCM.
Sorry for not replying earlier, i am kind of
busy this week but i will try to make the
proper changes that you pointed out and do
the tests also. As far as the repetition rate goes,
i simply receive one negative pulse from the tsop
(whenever two buttons are pressed) with different pulse widths.
Will post soon.
thanks |
|
|
irmanao
Joined: 08 Apr 2015 Posts: 77
|
|
Posted: Fri Sep 15, 2017 10:48 am |
|
|
Ok i cleared the flag but it still doesn't work perfectly. I only get the dms to change when
the pulse width is larger than 3ms and even then it doesn't change
with every push of the button. There must be something wrong with
the pulse widths but i am unable to check them via rs232, so i
opened another topic in an attempt to solve that.
thanks |
|
|
irmanao
Joined: 08 Apr 2015 Posts: 77
|
|
Posted: Sun Sep 17, 2017 9:40 am |
|
|
Ok, i did the rs232 tests and i am indeed getting the correct delays.
Even when i include the part of the code you left out i still get correct results.
So there must be some issue with the timing i'm guessing and that's why i'm having problems with the dimming part?
thanks |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sun Sep 17, 2017 10:55 am |
|
|
One big problem you have (if the remarked sections are put back), is delays both inside and outside an interrupt.
This implies that you will be getting a warning from the compiler 'interrupts disabled to prevent reentrancy', and interrupts will be disabled in the main code for the entire delay here. Also while it is in INT_EXT, and the delays are being generated, you could have a situation where the flag is set, but the first time has been updated, and not the second, which will give wrong values....
You need to re-think how to generate the pulse.
Consider using a timer interrupt to do this. Just set a timer to expire in the time you want for the DMS, and exit the EXT interrupt handler immediately.
Then when this expires set your pin A0 high, and use delay_cycles(10) for the pulse. Delay cycles does not usually use the delay library, so won't generate the reentrancy problem, and the short 10uSec delay won't cause overflow problems. This way the long delay is being done by a hardware timer instead of using software delays. |
|
|
irmanao
Joined: 08 Apr 2015 Posts: 77
|
|
Posted: Thu Sep 21, 2017 8:24 am |
|
|
I got a bit confused about the timer interrupt part of your comment..
This is what i came up with.. Code: | #INT_EXT // zero crossing
void ext_int (void){
flag=1;
}
#INT_TIMER0
void timer0_int(void){
set_timer0(65286);
counter=counter-1;
if (counter==0 && flag==1){
counter=5; // dms=5ms, when the clock is at 8MHz
output_high(PIN_A0);
//delay_us(10);
delay_cycles(10);
output_low(PIN_A0);
flag=0;
}
}
|
..but it didn't work. The lamp wouldn't light up at all.
Could you be a bit more detailed or tell me what i'm missing here?
thanks |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Thu Sep 21, 2017 9:15 am |
|
|
You need a pulse, a time after the trigger edge.
You had this being done by having a programmable time in the interrupt.
This would prevent interrupts being enabled in all the delays outside.
To generate the same effect you need to generate a programmable delay with the timer. But to do this _you_ are going to have to work out how long to program the timer for (_you_ will have to work out what value needs to go into the timer counter to give the required delay). You need to setup the delay in the INT_EXT ISR, clear the interrupt, and enable it. Then when the timer interrupt triggers do the pulse and disable it.
There has been example code for doing a programmable delay like this posted here in the past. |
|
|
irmanao
Joined: 08 Apr 2015 Posts: 77
|
|
Posted: Sun Sep 24, 2017 9:46 am |
|
|
Ok, i changed the code a bit: Code: | #INT_EXT
void ext_int (void){
set_timer1(dms);
clear_interrupt(int_timer1);
enable_interrupts(INT_TIMER1);
}
#INT_TIMER1
void timer1_int(void){
output_high(PIN_A0);
delay_us(10);
output_low(PIN_A0);
disable_interrupts(INT_TIMER1);
} |
Code: | if (pulse_width_ms>11){
delay_ms(50);
dms=dms-500;
if (dms<63786){dms=63786;}
}
if (pulse_width_ms<7){
delay_ms(50);
dms=dms+500;
if (dms>65036) {dms=65036;}
}
} |
The numbers on dms are all correct (checked with oscilloscope), i am also getting the correct incrementation on dms (checked with terminal) but the lamp seems to dim both ways with either of the pulse_width_ms. I'm guessing there is a problem when i disable int_timer1 since it is also used in the CCP?
thanks |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Sun Sep 24, 2017 9:57 am |
|
|
OK, silly question but.... the variable dms, do you declare it as 8 bit or 16 bit ?
You've made a lot of changes, so perhaps post your latest, full test program.
Jay |
|
|
irmanao
Joined: 08 Apr 2015 Posts: 77
|
|
Posted: Sun Sep 24, 2017 10:46 am |
|
|
16bit
Code: |
#include <16F1827.h>
#fuses PLL, NOWDT, NOPROTECT, PUT, BROWNOUT, NOLVP
#use delay(internal=8000000)
#use rs232(baud=9600,xmit=PIN_B5,rcv=PIN_B2,bits=8,ERRORS)
int8 capture_falling_edge;
int8 got_pulse_width;
int16 ccp_delta;
int16 dms=64036;
//int flag=0;
int counter;
#int_ccp4
void ccp4_isr(void)
{
static int16 t1_falling_edge;
// If current interrupt is for falling edge.
if(capture_falling_edge)
{
setup_ccp4(CCP_CAPTURE_RE);
capture_falling_edge = FALSE;
t1_falling_edge = CCP_4;
}
else
{
setup_ccp4(CCP_CAPTURE_FE);
capture_falling_edge = TRUE;
ccp_delta = CCP_4 - t1_falling_edge;
got_pulse_width = TRUE;
}
}
//====================================
void main()
{//#rom 0x8008 = {0x19ff}
int16 pulse_width_ms;
int16 local_ccp_delta;
got_pulse_width = FALSE;
capture_falling_edge = TRUE;
clear_interrupt(INT_EXT);
ext_int_edge(1, H_TO_L);
enable_interrupts(INT_EXT);
setup_ccp4(CCP_CAPTURE_FE);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
set_timer2(dms);
enable_interrupts(INT_CCP4);
enable_interrupts(GLOBAL);
while(TRUE)
{//output_high(PIN_A0);
//delay_ms(dms);
//output_low(PIN_A0);
if(got_pulse_width)
{
disable_interrupts(GLOBAL);
local_ccp_delta = ccp_delta;
enable_interrupts(int_timer1);
pulse_width_ms = local_ccp_delta /250;
got_pulse_width = FALSE; // *** ADDED THIS LINE ***
// printf("%lu\n\r", pulse_width_ms);
printf("%lu\n\r", dms);
if (pulse_width_ms>11){
delay_ms(50);
dms=dms-500;
if (dms<63786){dms=63786;}
}
if (pulse_width_ms<7){
delay_ms(50);
dms=dms+500;
if (dms>65036) {dms=65036;}
}
}
}
}
#INT_EXT
void ext_int (void){
set_timer1(dms);
clear_interrupt(int_timer1);
enable_interrupts(INT_TIMER1);
}
#INT_TIMER1
void timer1_int(void){
output_high(PIN_A0);
delay_us(10);
output_low(PIN_A0);
disable_interrupts(INT_TIMER1);
}
|
|
|
|
|