View previous topic :: View next topic |
Author |
Message |
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
Capture doesn't work |
Posted: Sun Nov 11, 2012 8:37 am |
|
|
Hello!
I'm trying to write a code for 18f2520 which should measure period. But somehow it doesn't work. I put the signal with frequency from 10Hz to 200Hz on pin_b3 (CCP2). Here is the code.
Code: |
#include <18F2520.h>
#fuses NOWDT,NOMCLR,intrc
#use delay(internal=8M)
#define LCD_DATA_PORT getenv("SFR:PORTc")
#define LCD_RS_PIN PIN_c0
#define LCD_RW_PIN PIN_c1
#define LCD_ENABLE_PIN PIN_c2
#define DATA0 PIN_c4
#define DATA1 PIN_c5
#define DATA2 PIN_c6
#define DATA3 PIN_c7
#include "lcd.c"
int16 per;
#int_ccp2
void isr(){
per = ccp_2;
}
void main()
{
lcd_init();
SETUP_CCP2(CCP_CAPTURE_RE);
SETUP_TIMER_1(T1_DIV_BY_8);
enable_interrupts(INT_CCP2); // Setup interrupt on falling edge
enable_interrupts(GLOBAL);
while (true)
{
delay_ms(500);
printf(lcd_putc,"\fPER = %ld",per);
}
} |
What I get as a value is : PER = 53472 and doesn't change when I'm changing the frequency. What is wrong? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19549
|
|
Posted: Sun Nov 11, 2012 9:05 am |
|
|
So many things:
First, pin B3, is _not_ the default pin for CCP2. It is the _alternate_ pin for this function. You need to add the fuse CCP2B3. Currently CCP2 is on pin C1....
Second a comment about your remarks. You have:
"// Setup interrupt on falling edge",
but two lines before (the line that sets up the edge being used by CCP2), you have:
SETUP_CCP2(CCP_CAPTURE_RE);
'RE' stands for _rising edge_.
Then you say your code 'should measure period'. No, it doesn't. It measures _when_ the rising edges occur. To measure period, you need to take the difference _between_ a reading, and the last reading. Currently your code has nothing to do with 'period'.....
Then a general comment (that will come to catch you in the future). Printing takes a lot of time. Is 'per' likely to remain unchanged for all this time?. No, it _will_ change. This means that once it is being updated by the interrupt, you _must_ disable interrupts for a moment, take a copy of it, re-enable interrupts, and print the _copy_ otherwise you will basically get garbage....
Best Wishes |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Sun Nov 11, 2012 9:53 am |
|
|
I remade my code but without siccess.
Code: |
#include <18F2520.h>
#fuses NOWDT,NOMCLR,intrc,CCP2B3 //fuse for pin_b3
#use delay(internal=8M)
#define LCD_DATA_PORT getenv("SFR:PORTc")
#define LCD_RS_PIN PIN_c0
#define LCD_RW_PIN PIN_c1
#define LCD_ENABLE_PIN PIN_c2
#define DATA0 PIN_c4
#define DATA1 PIN_c5
#define DATA2 PIN_c6
#define DATA3 PIN_c7
#include "lcd.c"
int16 per;
#int_ccp2
void isr(){
per = ccp_2-per; //updated value-last value
disable_interrupts(int_ccp2); //disable interrupt
}
void main()
{
lcd_init();
SETUP_CCP2(CCP_CAPTURE_RE); //every rising edge
SETUP_TIMER_1(T1_DIV_BY_8);
enable_interrupts(INT_CCP2);
enable_interrupts(GLOBAL);
while (true)
{
delay_ms(500);
printf(lcd_putc,"\fPER = %lu",per); //make a copy
enable_interrupts(INT_CCP2); //and enable again
}
} |
I get garbage again |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Sun Nov 11, 2012 10:32 am |
|
|
You're not thinking logically about what's happening.
What happens to the value of CCP_2 when timer1 overflows?
What's happening to timer1 whilst you do your delay_ms(xx) then the printing?
How do you know that you haven't missed a capture event?
Mike
Try saving several CCP_2 values to an array, then examine the array to see what you get. |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Sun Nov 11, 2012 11:23 am |
|
|
Does the timer1 resets when capture occurs? |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Sun Nov 11, 2012 12:58 pm |
|
|
Quote: | Does the timer1 resets when capture occurs? | Simple answer. No.
It's all in the Microchip manual.
When the CCP2 trigger event occurs, the timer1 value is copied to CCPR2L and CCPR2H.
CCS translates CCPR2L and CCPR2H to a single 16bit ccp_2 value.
Timer1 is not reset, it continues running.
You have to deal with that.
I don't think you've understood what Ttelmah is telling you.
Mike
EDIT If you're trying to measure up to 200Hz, your 500ms delay is going to screw things up.
Please tell me that all this is NOT a Proteus simulation. |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Mon Nov 12, 2012 10:08 am |
|
|
Thank you for replies!
No it isn't Proteus simulation. I made some changes in my code and I had an success partly. I already have changing value by changing the frequency with correct proportions. But I still don't understand what is the problem about my delay_ms(xx). I reviewed the example code EX_CCPMP.C at the compiler folder and there is also delay into the while cycle.
Code: |
while(TRUE) {
delay_ms(1000);
printf("\r%lu us ", pulse_width/5 );
} |
Why is this wrong in case I use interrupt?
Here is my new code:
Code: |
unsigned int16 x;
#int_ccp2
void isr(){
x=ccp_2; //make a copy of timer1
set_timer1(0); //reset timer every interrupt
}
void main()
{
set_tris_b(0b00010000); //set pin_b3 as an input
lcd_init();
SETUP_CCP2(CCP_CAPTURE_RE); //every rising edge
SETUP_TIMER_1(T1_INTERNAL|T1_DIV_BY_8); //8 times longer period
enable_interrupts(INT_CCP2);
enable_interrupts(GLOBAL);
while (true)
{
delay_ms(500);
printf(lcd_putc,"\fTMR1_val = %lu",x); //print the copy
}
} |
|
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Mon Nov 12, 2012 11:13 am |
|
|
This is the relevant comment from Ttelmah Quote: | To measure period, you need to take the difference _between_ a reading, and the last reading. Currently your code has nothing to do with 'period'..... |
What you are doing is now nearer.
Reseting the timer only partially helps but won't ultimately solve your problem.
You've dramatically changed the way delay_ms(xx) affects your results.
Tell us what kind of results you're getting with the current code version.
In other words tell us what readings you expect, and what you actually get, for several different frequencies within your design range.
Mike |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Mon Nov 12, 2012 1:39 pm |
|
|
My end point is to measure frequency. Before that I need to measure the period? I use this formula:
Period=(TMR1_DIV*CCP_2)/(osc_freq/4) [uS] |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Mon Nov 12, 2012 2:24 pm |
|
|
Yes. I understand how to convert period to frequency.
What I want to know is:-
How good (or bad) is your current code?
Can you tell me what results you're getting?
(As I outlined in my previous post)
I know how to do the job. If I give you an answer you'll learn nothing.
The idea is for you to work it out for yourself, with some gentle hints.
Mike
EDIT I can probably predict the sort of errors you're experiencing, but I want your confirmation.
At the outset you said you want to measure from 10Hz to 200Hz. So tell us what values you get for the period with inputs at say 5, 10, 20, 50 & 100ms (these values correspond to your design frequency range).
AND You're still not dealing with the possibility (probability) that x gets changed whilst it's being printed. |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Tue Nov 13, 2012 11:35 am |
|
|
I managed to fix my code so I can messure frequency now. I get value with accuracy to 1%. I can messure freq rates from 5Hz to 1KHz. Thanks for helping me! Here is my ultimate code:
Code: |
#int_ccp2
void isr(){
x=ccp_2; //make a copy of timer1
set_timer1(0); //reset timer every interrupt
x=1/(0.000004*x);
}
void main()
{
set_tris_b(0b00010000); //set pin_b3 as an input
lcd_init();
SETUP_CCP2(CCP_CAPTURE_RE); //every rising edge
SETUP_TIMER_1(T1_INTERNAL|T1_DIV_BY_8); //8 times longer period
enable_interrupts(INT_CCP2);
enable_interrupts(GLOBAL);
while (true)
{
delay_ms(500);
printf(lcd_putc,"\fFrequency = %luHz",x); //print the copy
}
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19549
|
|
Posted: Tue Nov 13, 2012 11:58 am |
|
|
Do this instead:
Code: |
#int_ccp2
void isr(){
static int16 old;
int16 temp;
temp=ccp_2; //make a copy of timer1
x=temp-old;
old=temp;
}
//seriously, arithmetic in the IRQ, should be avoided like the plague
//This probably sets your upper frequency limit. Division takes an age...
void main(){
int16 working;
set_tris_b(0b00010000); //set pin_b3 as an input
lcd_init();
SETUP_CCP2(CCP_CAPTURE_RE); //every rising edge
SETUP_TIMER_1(T1_INTERNAL|T1_DIV_BY_8); //8 times longer period
enable_interrupts(INT_CCP2);
enable_interrupts(GLOBAL);
while (true) {
delay_ms(500);
disable_interrupts(GLOBAL);
working=x;
enable_interrupts(GLOBAL);
//momentarily disable interrupts and take a _copy_ of the value
//from the IRQ. This avoids momentary glitches that otherwise
//_will_ happen when the value updates while you are printing
working=250000/working; //do the maths out here int32 maths
printf(lcd_putc,"\fFrequency = %luHz",working); //print the copy
}
}
|
See how different it is:
1) In the ISR, only a subtraction, not a division. Hundreds of times faster.
2) Printing, and arithmetic is only done on a copy that cannot be changed by the ISR.
3) Problem with setting the timer to 0, is that it takes _time_ to get to the ISR. As a result, a small error....
You should find accuracy is better, and you don't get occasional spurious values.
Best Wishes |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Tue Nov 13, 2012 1:43 pm |
|
|
Thank you for the valuable advices guys! |
|
|
|