CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Capture doesn't work

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
rikotech8



Joined: 10 Dec 2011
Posts: 376
Location: Sofiq,Bulgariq

View user's profile Send private message

Capture doesn't work
PostPosted: Sun Nov 11, 2012 8:37 am     Reply with quote

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: 19480

View user's profile Send private message

PostPosted: Sun Nov 11, 2012 9:05 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Nov 11, 2012 9:53 am     Reply with quote

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 Sad
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Sun Nov 11, 2012 10:32 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Nov 11, 2012 11:23 am     Reply with quote

Does the timer1 resets when capture occurs?
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PostPosted: Sun Nov 11, 2012 12:58 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Nov 12, 2012 10:08 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Nov 12, 2012 11:13 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Nov 12, 2012 1:39 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Nov 12, 2012 2:24 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Nov 13, 2012 11:35 am     Reply with quote

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: 19480

View user's profile Send private message

PostPosted: Tue Nov 13, 2012 11:58 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Nov 13, 2012 1:43 pm     Reply with quote

Thank you for the valuable advices guys!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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