View previous topic :: View next topic |
Author |
Message |
dacenrie
Joined: 02 Mar 2006 Posts: 16
|
Need Help with Capture Module!!! |
Posted: Mon Apr 23, 2007 5:22 pm |
|
|
Greetings All,
I am having a difficult time getting the capture module to work. All I'm trying to do is measure the frequency/period and duty cycle of a square wave tied into the CCP1 port. Please help! It works for most of the time but when I get past a certain frequency I get invalid values.
Does anyone know the basics of the capture module and anything I need to be aware of? Oscillator speed, max capture frequency, etc.???
Please Help!
Thanks! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Apr 23, 2007 5:39 pm |
|
|
Post the details.
1. Post the input frequency which causes a problem.
2. Post the PIC's oscillator frequency.
3. Post the CCP prescaler that you're using.
4. Post the Timer1 prescaler. |
|
|
dacenrie
Joined: 02 Mar 2006 Posts: 16
|
|
Posted: Tue Apr 24, 2007 9:17 am |
|
|
Thanks for the response PCM programmer, here is the info:
1.) After 22KHz problem starts to arise.
2.) PIC oscillator frequency: 8MHz
3.) CCP Prescaler: 1
4.) TIMER1 prescaler: 1
5.) CCP1 is set to capture first rising edge. |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Tue Apr 24, 2007 1:48 pm |
|
|
You are probably running into interrupt processing time problems.
At 22kHz the period is 45uS, and the interrupt latency and processing time (saving and restoring context as well as the actual code) will be close to that.
Instead of counting over one period, count over 16 cycles.
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_ccp1(CCP_CAPTURE_RE|CCP_CAPTURE_DIV_16);
This will also provide an averaging effect.
Also, a faster method is not to use interrupts at all, just test the CCP1IF flag bit in the hardware (the capture will happen regardless of what the
processor is doing). Leave timer 1 to free run and take two captures - the measurement is the difference between the two captured values.
For a 16F
#byte PIR1 = 0x0C
#bit CCP1IF = PIR1.2
Code: |
int16 new_t1count;
int16 old_t1count;
int16 delta_count;
CCP1IF = 0;
while (!CCP1IF);
old_t1count = CCP_1;
CCP1IF = 0;
while (!CCP1IF);
new_t1count = CCP_1;
delta_count = new_t1count - old_t1count;
|
There will be a lower input frequency limit. The difference between successive captures needs to be less than the 16 bit width of timer 1.
Last edited by Kenny on Tue Apr 24, 2007 11:19 pm; edited 1 time in total |
|
|
dacenrie
Joined: 02 Mar 2006 Posts: 16
|
|
Posted: Tue Apr 24, 2007 2:51 pm |
|
|
OMG.....it works!!! Thank you very much Kenny. I had a feeling my interrupt might have been taking up too much time between captures. I no longer have any ISRs in my code, I just follow the flag bit like you suggested.
Thanks again and your suggestion was very much appreciated. |
|
|
dacenrie
Joined: 02 Mar 2006 Posts: 16
|
|
Posted: Wed Apr 25, 2007 3:55 pm |
|
|
Ok so...upon calculating the difference between the 1st and 2nd capture, I can easily call that the PR2 value. Hence, it can be loaded into the "period" parameter of setup_timer_2(mode, period, postscale) function, for the PWM module. What about the duty cycle? I cannot find a way to retrieve the duty cycle and come up with a value to store onto the set_pwmx_duty(value) function. PLease help!!!
Thanks. |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Thu Apr 26, 2007 3:55 am |
|
|
Your question on an earlier thread suggested that you wanted the pwm out from the third CCP to be the same frequency and duty cycle as the input. That didn't make sense because the input signal is already what you need and explains why there were no replies.
Do you mean "I want a pwm out from the third CCP at a fixed frequency of about ....Hz, and the duty cycle to be the same as that of the input. The input frequency can vary from ....Hz to ....Hz "? |
|
|
dacenrie
Joined: 02 Mar 2006 Posts: 16
|
|
Posted: Thu Apr 26, 2007 8:52 am |
|
|
No...I want to REPLICATE a signal going into the CCP1 pin or CCP2 (both in capture mode) and output that same signal on the CCP3 pin (PWM mode). This means that the output PWM will vay depending on what signal is coming in the CCP1 pin; the same signal coming in is the same signal coming out.
I know the input is all I need. So I capture the period and duty cycle of the input signal (from CCP1 or CCP2). That same period and duty cycle has to be present on the output signal as well (CCP3).
As of right now, the input signal period is successfuly measured and replicated on the output signal. Now, I am having problems successfuly measuring the duty cycle of the input signal and replicating it on the output signal.
Thanks for all your help. |
|
|
dacenrie
Joined: 02 Mar 2006 Posts: 16
|
|
Posted: Thu Apr 26, 2007 11:43 am |
|
|
Here's some code for you to look at and review:
#include <16F737.h>
#fuses NOWDT, NOPROTECT, INTRC_IO, PUT, NOBROWNOUT, MCLR, CCP2C1, DEBUG, NOFCMEN, NOIESO, NOBORSEN
#byte PIR1 = 0x0C
#byte PIR2 = 0x0C
#bit CCP1IF = PIR1.2
#bit CCP2IF = PIR2.2
int16 period_capture, old_capture, duty_capture;
int16 duty_cycle;
int8 PR2_val, change_osc = FALSE;
/***********************************************************************/
/****************************Main***************************************/
/***********************************************************************/
void main()
{
set_tris_C(0xFF); // set C port as input
set_tris_B(0xDF); // RB5 as output
setup_adc(ADC_OFF); // turn off ADC
setup_comparator(NC_NC_NC_NC); // turn off comparator
setup_oscillator(OSC_31KHZ); // change oscillator speed
setup_ccp1(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_16); // Configure CCP1 to capture rising edge
setup_ccp2(CCP_CAPTURE_FE | CCP_CAPTURE_DIV_16); // Configure CCP2 to capture falling edge
setup_ccp3(CCP_PWM); // Configure CCP3 as PWM
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // Set timer1 to run at (system_clk/4)
while(1)
{
CCP1IF = 0;
while (!CCP1IF);
old_capture = CCP_1; //Capture first rising edge
CCP1IF = 0;
while (!CCP1IF);
period_capture = CCP_1; //Capture second rising edge
PR2_val = ((period_capture - old_capture) >> 4); //Calculate PR2 (period) value
CCP1IF = 0;
while (!CCP1IF);
old_capture = CCP_1; //Capture first rising edge
CCP2IF = 0;
while (!CCP1IF);
duty_capture = CCP_2; //Capture next falling edge
duty_cycle = ((duty_capture - old_capture) >> 4); //Calculate duty cycle
if(PR2_val < 5 || PR2_val > 250) //Check if oscillator needs change
change_osc = TRUE; //Speed up oscillator
setup_timer_2(T2_DIV_BY_1, PR2_val, 1); //Load period into TIMER2 for PWM
set_pwm3_duty(duty_cycle); //Set duty cycle for PWM
}
}
Note: I have a signal generator going into the CCP1 & CCP2 pins.
I am reading the CCP3 pin signal through an o-scope. |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Thu Apr 26, 2007 2:30 pm |
|
|
Edited: Cleaned up the post in an attempt to make the meaning clearer.
OK, here is a way of measuring the duty cycle of the input. As a ratio it will be the number representing 16 times the ‘on’ time for one cycle of the input divided by the number representing the time taken for 16 cycles of the input obtained earlier. The duty cycle measurement can be done after the period measurement assuming that the input frequency is not changing during the measurements.
The measurement of 16 times the ‘on’ time of the input cannot be measured over 16 cycles in the same way that the period was measured.
Instead, get the timer 1 count difference for the time between the rising and falling edges of one pulse, do this 16 times, adding the new value to a variable that holds the sum.
For a PIC16
#byte PIR2 = 0x0D
#bit CCP2IF = PIR2.0
setup_ccp2(CCP_CAPTURE_FE); // Note that there is no post scaler possible for falling edge capture
Loop the following 16 times.
1. Clear CCP1IF.
2. Wait for CCP1IF to go high.
3. Clear CCP2IF.
4. Get the captured timer 1 count from CCP_1.
5. Wait for CCP2IF to go high (may have already done so!).
6. Get the new captured timer 1 count from CCP_2.
7. Subtract the old one from it and add the result to sum variable
CCP1IF will occur every 16 cycles of the input, so there should be enough time to do the above in the time in between them for each time around the loop.
Edited: Back from a break. Added results of a test:
With a 16MHz crystal was able to measure down to about 2uS pulse width and get a number for 16 'on' times. The input pulse width needs to be greater than 2uS. |
|
|
dacenrie
Joined: 02 Mar 2006 Posts: 16
|
|
Posted: Mon May 21, 2007 11:28 am |
|
|
The frequency read is working, but I still can't quite get the duty cycle read working. Here is my current code and hopefully you can find something I don't. Thanks.
I'm throwing the same square wave signal into CCP1 and CCP2. I then output PWM to CCP3, after I've established frequency/period and duty cycle measurements.
#include <16F737.h>
#fuses NOWDT, NOPROTECT, INTRC_IO, PUT, NOBROWNOUT, MCLR, CCP2C1, DEBUG, NOFCMEN, NOIESO, NOBORSEN
#byte PIR1 = 0x0C
#byte PIR2 = 0x0D
#bit CCP1IF = PIR1.2
#bit CCP2IF = PIR2.0
int16 period_capture, old_capture, duty_capture;
int16 duty_cycle;
int8 PR2_val, change_osc = FALSE;
/***********************************************************************/
/****************************Main***************************************/
/***********************************************************************/
void main()
{
set_tris_C(0xFF); // set C port as input
set_tris_B(0xDF); // RB5 as output
setup_adc(ADC_OFF); // turn off ADC
setup_comparator(NC_NC_NC_NC); // turn off comparator
setup_oscillator(OSC_500KHZ); // change oscillator speed
setup_ccp1(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_16); // Configure CCP1 to capture rising edge
setup_ccp2(CCP_CAPTURE_FE); // Configure CCP2 to capture falling edge
setup_ccp3(CCP_PWM); // Configure CCP3 as PWM
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1); // Set timer1 to run at (system_clk/4)
while(1)
{
int x;
CCP1IF = 0;
while (!CCP1IF);
old_capture = CCP_1; //Capture first rising edge
CCP1IF = 0;
while (!CCP1IF);
period_capture = CCP_1; //Capture second rising edge
PR2_val = ((period_capture - old_capture) >> 4); //Calculate PR2 value
if(PR2_val < 5 || PR2_val > 250) //Check if oscillator needs change
change_osc = TRUE; //Speed up oscillator
for (x=0; x<16; x++)
{
CCP1IF = 0;
while (!CCP1IF);
CCP2IF = 0;
old_capture = CCP_1;
while (!CCP2IF);
duty_cycle += (CCP_2-old_capture);
}
setup_timer_2(T2_DIV_BY_1, PR2_val, 1); //Load period into TIMER2 for PWM
set_pwm3_duty(duty_cycle); //Set duty cycle for PWM
}
} |
|
|
Kenny
Joined: 07 Sep 2003 Posts: 173 Location: Australia
|
|
Posted: Mon May 21, 2007 7:45 pm |
|
|
Add the following line before the looping, otherwise the variable will keep incrementing each time around the main loop.
duty_cycle = 0;
Ideally for these measurements it is better to use a more stable clock source than the internal one, and at the highest clock speed for the pic, 20MHz. But I understand that you will be adjusting the pwm frequency by altering the internal clock frequency in stages.
I should have posted my test code:
Code: |
#include <16F876.h>
#fuses HS,NOWDT,PUT,NOLVP,NOPROTECT,BROWNOUT
#use delay(clock=16000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7,ERRORS)
#byte PIR1 = 0x0C
#bit CCP1IF = PIR1.2
#byte PIR2 = 0x0D
#bit CCP2IF = PIR2.0
void main()
{
int16 new_t1count;
int16 old_t1count;
int16 delta_count;
int16 period;
int16 duty;
int8 i;
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_ccp1(CCP_CAPTURE_RE|CCP_CAPTURE_DIV_16);
setup_ccp2(CCP_CAPTURE_FE);
while(1)
{
// Period
CCP1IF = 0;
while (!CCP1IF);
old_t1count = CCP_1;
CCP1IF = 0;
while (!CCP1IF);
new_t1count = CCP_1;
period = new_t1count - old_t1count;
// Duty
duty = 0;
for (i=0;i<16;i++)
{
CCP1IF = 0;
while (!CCP1IF);
CCP2IF = 0;
old_t1count = CCP_1;
while (!CCP2IF);
new_t1count = CCP_2;
delta_count = new_t1count - old_t1count;
duty += delta_count;
}
printf("period=%5lu duty=%5lu\n\r",period,duty);
delay_ms(1000);
}
}
|
|
|
|
|