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

Problem with float math - period to freq
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

Problem with float math - period to freq
PostPosted: Fri Jul 29, 2016 7:58 am     Reply with quote

I know that this is probably one of those little over looked pieces that we all encounter. I just can't seem to find it.

I am trying to convert a period to frequency.

My period is freq5 (0.02913820).

From taking my CCP value on Timer3 multiplying by any overflow on timer 3 and the instruction cycle.

On a calculator, 1/0.02913820 = 34.3192
and then, 34.3192 * 16 = 549.1 which is what it should be.

But as you can see from my printf, I get a negative, not sure why. But it may be related to my next concern, which is why I have to multiply by 16 when my CCP_CAPTURE_DIV4, I would expect to multiply by 4.

Processor: Pic18F27J53
CCS Ver: 4.125
Code:

int32 current_ccp5;
float freq5, freq5a;

Code:

#int_ccp5
void ccp5()
{
current_ccp5 = (int32)GET_TIMER3(); // Read the current CCP
set_timer3(0);
output_toggle(cap5);   
t3_ov = t3_overflow;
t3_overflow =0;
}

Code:

#int_timer3
void timer_three()
{
set_timer3(0);
t3_overflow++;
output_toggle(portc_2);
//set_timer3(0x8000);
}

Code:

void main (void)
{
enable_interrupts(GLOBAL);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
 
// Enable the timer interrupts
enable_interrupts(INT_TIMER3);
enable_interrupts(INT_ccp5);

setup_ccp5(CCP_CAPTURE_RE|CCP_CAPTURE_DIV_4);

while(1)
   {
   delay_ms(100);

   if (t3_ov) current_ccp5 = current_ccp5 + (t3_ov*65536);
   freq5 =(float)current_ccp5 * .0000002; ///0.0000002;
   
   freq5a = ((float)1/freq5);

   printf("CPP5:%8.8f |%8.8f | %lu | %lu    \r", freq5, freq5a, current_ccp5, t3_ov);
   }
}


Code:

My output:
CPP5:0.02913820 |-8.63045888 | 14627 | 2
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Fri Jul 29, 2016 8:34 am     Reply with quote

you are talking about an MPLAB result right ?
your output format does not look like CCS FMT %8.8f to me

Not actual hardware you built, by all appearances,

BTW: are you certain you want to zero timer 3 in the CCP interrupt ??
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Fri Jul 29, 2016 8:38 am     Reply with quote

this is actual hardware output.

I am working in MPLAB, but using the CCS compiler plugin. yes, I want to zero the timer, this way on the next interrupt, I will get the correct number of counts between interrupts.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Jul 29, 2016 8:41 am     Reply with quote

Lots of things seem to be wrong. You have a count of 145691. Now if you are seeing this, from an input frequency of 549Hz (which seems to be what you are giving as your 'expected' result, then you would be clocking the timer at 80MHz, which is beyond it's clock rate.... Either your frequency is not what you expect or your figures are wrong somewhere....

The whole thing is several orders of magnitude too complex. If your oscillator feeding the CCP is (say) 20MHz, then all you need to do, is divide 20000000 by the counter you return. No need for floating arithmetic at all....
Obviously the figures used need to be right, which I don't think they currently are.

Remember the Timer prescaler is not fed off the master oscillator (Fosc), but Fosc/4. So with /4 prescaler off this, this gives Fosc/16. You almost certainly actually want an /1 prescaler so you are using Fosc/4.

Then when CCP5 interrupts, the timer value at the instant of the edge, has been recorded into CCP5. You don't want to read the timer, read CCP5. Similarly when Timer3 interrupts, the interrupt signals that it _has_ wrapped to zero. You don't want to set it to zero again.

You can do the whole generate the 32bit count, by combining the two int16 counts in a structure.
Code:

//header for your processor
#define TIMER_FREQ 5000000

struct words
{
   int16 LSW;
   int16 MSW;
};

union
{
   int32 whole;
   struct words part;
} builder32;

int32 result;
int1 update=FALSE;

#int_timer3
void timer_three()
{
   //set_timer3(0); //The interrupt says the timer _has_ wrapped to zero
   builder32.part.MSW++; //increment upper counter word
   //output_toggle(portc_2);
}

#int_ccp5
void ccp5()
{
   builder32.part.LSW = CCP5; // Read the current CCP (not the timer.....)
   set_timer3(0);
   //output_toggle(cap5);   
   result=builder32.whole;
   builder32.whole=0;
   update=TRUE;
}

void main()
{
   builder32.whole=0; //clear 32bit counter
   setup_ccp5(CCP_CAPTURE_RE|CCP_CAPTURE_DIV_4);
   //setup before you enable the interrupts
   enable_interrupts(INT_CCP5);
   enable_interrupts(INT_TIMER3);
   enable_interrupts(GLOBAL);
   while(TRUE)
   {
      delay_ms(100);
      update = FALSE;
      while (update==FALSE)
         ; //wait for update
      //This ensures we can do the maths before it updates again.
      update=FALSE;
      //Now do the maths in integer
      printf("Freq = %lw\n", TIMER_FREQ/result);
   }
}


There is a slight error introduced by zeroing timer3 in the interrupt. Remember the actual edge was some time ago. Better to instead use a continuously running timer, and subtract the 'old' from 'new' to give the recorded time.
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Fri Jul 29, 2016 9:05 am     Reply with quote

Yes, 20MHz crystal. which I figured was:

input freq is apporx 549Hz

Fosc = 20Mhz
Fosc/4 = 5MHs (0.2uS)

my counts was 14627 add 2 timer 3 overflows (65536*2= 131.072)
131072+14627 = 145699

145699 total clocks * Fosc/4 = .0291398 period

1/0.0291398 = 34.317325

34.317325 * 16 = 549.07 Freq


so I as taking the count from Timer 3 * 0.0000002 to get my period
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Fri Jul 29, 2016 9:07 am     Reply with quote

I copied and pasted Ttelmah's code and the result was:

Code:

Freq = 0.023364
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Fri Jul 29, 2016 9:43 am     Reply with quote

Quote:

when CCP5 interrupts, the timer value at the instant of the edge, has been recorded into CCP5.


According to the manual, Timer1 is recorded in to CPPx during interrupt. my ultimate goal is to read 4 frequency simultaneously, each with their own timer.

CPP4 = timer1
CPP5 = timer3
CPP6 = timer0
CPP7 = timer2

I don't think it is possible to dedicate a specific timer to a CPP, which is why I was reading the timer and not CPPx
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Jul 29, 2016 10:40 am     Reply with quote

My code won't work without the tweaks to match your timings.

However there is something wrong with the values (as I felt). If your input is 549Hz, then the count should be 2276.

Your timer is feeding off Fosc/16 (Fosc/4/4), so is counting 1250000 times per second. This over 549, gives 2276 counts per interrupt. The timer should not have wrapped at all.

You are missing the point about using the CCP. It actually latches the timer value at the instant the edge occurs. You can use several channels all off the one time, _provided you do not reset the timer_.

Then you also need to use timer3. This is probably where your problem is at present. Currently the CCP is running off timer1....

You need to record the first time, then the second, and subtract one from the other.
Code:

//header for your processor
#define TIMER_FREQ 5000000

struct words
{
   int16 LSW;
   int16 MSW;
};

union
{
   int32 whole;
   struct words part;
} builder32;

int32 result=0;

#int_timer3
void timer_three()
{
   builder32.part.MSW++; //increment upper counter word
}

#int_ccp5
void ccp5()
{
   static int32 old;
   static int1 first=TRUE;

   builder32.part.LSW = CCP5; // Read the current CCP (not the timer.....)
   if (first) 
   {
      first=FALSE;
   }
   else
   {
      result=builder32.whole-old; //generate the difference   
   }
   old=builder32.whole; 
}

void main()
{
   int32 local;
   builder32.whole=0; //clear 32bit counter
   setup_ccp5(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_1 | CCP_USE_TIMER3_AND_TIMER4);
   //setup before you enable the interrupts
   //changing this to give 5Mhz, and to use timer3 for the capture
   enable_interrupts(INT_CCP5);
   enable_interrupts(INT_TIMER3);
   enable_interrupts(GLOBAL);
   while(TRUE)
   {
      delay_ms(100);
      do
      {
          local=builder32.whole;
      } while (local != builder32.whole);
      //This copies the 32bit value into a local variable, even if a timer
      //update occurs.

      //Now do the maths in integer
      printf("Freq = %lw\n", TIMER_FREQ/result);
   }
}


You should find this will now work and give the right frequency. Also you can just add further CCP's using the same timer, and they will also work.
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Sat Jul 30, 2016 8:49 am     Reply with quote

Ttelmah, Thanks for your help.. I have taken your idea (not your code) and applied it to mine and it works well!!
Code:

int32 cpp5a, cpp5_ov, cpp5_old, cpp5_org, cpp5_out, a, b, c;
float d,e,f;
//----------------------------------------------------------------------------------
 #int_timer1
 void timer_one()
 {
    output_toggle(PIN_C2);
   cpp5_ov++;
 }
//----------------------------------------------------------------------------------
 #int_ccp5
 void ccp5()
 {
    cpp5_org = CCP_5; // Read the current CCP (not the timer.....)
   cpp5a = cpp5_org;
   output_toggle(PIN_C1);
   
   if (cpp5_ov) cpp5a += (65536*cpp5_ov);   //if timer 1 interrupted we need to add number of int's * 65536
                                 // and add that to our total count
   cpp5_out = cpp5a - cpp5_old   ;         //  now subtract the new count from the old

   cpp5_old = cpp5_org;               // save the new count, so it can be the old next time.
   cpp5_ov =0;                        // clear out t1 overflows.
 }
//----------------------------------------------------------------------------------
 void main()
 {

  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  setup_ccp5(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_4);

    //setup before you enable the interrupts
 
    enable_interrupts(INT_CCP5);
    enable_interrupts(INT_TIMER1);
    enable_interrupts(GLOBAL);
    while(TRUE)
    {
       delay_ms(100);

   d= (float)cpp5_out* 0.0000002; //  cpp5_out * Fosc/4  [20mhz xtal]
   e= 1/d;                     // get the inverse
   f= e*16;                  //  multiply by 16  (think it should be 4)
       printf("Freq = %8.8f ", f );
    }
 }



still not understanding why I have to multiply by 16 when I am using CCP_CAPTURE_DIV_4.

interestingly, I tried both:
setup_ccp5(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_4);

and

setup_ccp5(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_16);

and there was no difference in the output.. therefore it must not be reading it..

I do have
Code:

// DEFINE WHICH DEVICE TO COMPILE, CCS DEVICE FILE
#include "18F27J53.h"      
#include "main.h"
//header for your processor
 


so I checker the header file:
Code:

#define CCP_CAPTURE_DIV_4               6
#define CCP_CAPTURE_DIV_16              7
 


I am not one to automatically suspect a bug, just not sure whats going on...

so I recompiled both ways and checked the lst file:

Code:

....................   setup_ccp5(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_4); 
0075E:  BSF    F93.5
00760:  CLRF   x0F
00762:  MOVWF  x0F
00764:  BCF    x51.2


Code:

....................   setup_ccp5(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_16); 
0075E:  BSF    F93.5
00760:  CLRF   x0F
00762:  MOVWF  x0F
00764:  BCF    x51.2


and they are identical... WIERD!!!
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Sat Jul 30, 2016 9:50 am     Reply with quote

i think the #define for capture_ccp could be faulty in your old 4.125 -
header for the 18F27J5x.h file you have

check the defines

you may be able to just edit the header file and fix it
in 5.042 it is ok

#define CCP_CAPTURE_DIV_4 0x06
#define CCP_CAPTURE_DIV_16 0x07

what is in YOUR define file ?
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Sat Jul 30, 2016 9:53 am     Reply with quote

Code:

#define CCP_CAPTURE_DIV_4 6
#define CCP_CAPTURE_DIV_16 7
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sat Jul 30, 2016 2:11 pm     Reply with quote

You are fundamentally misunderstanding.
You don't need separate timers.

The point about 'capture', is it records counts.
You can use the same timer for one input frequency or a hundred.

Look at what I'm doing. I'm recording the difference between two edges. Because I don't stop, reset or change the timer in any way, the same timer can be used for all CCP's.

Think about a stopwatch with the ability to record 'times'. So long as the _recorded values_ are separate, you can happily use the same stopwatch to record lap times from loads of separate runners. Runner 1 records times of 200 & 4000. So a 'lap' of 3800. Runner 2 records times of 400, and 4200, so a lap time of 3800 as well. The times are different, but the interval is the same.

This is the whole reason to use 'delta'. By not changing the timer, just letting the CCP 'record' it. you have time to measure the delta.

You don't need to multiply. Look at what I do:

Think about an int32. It is four bytes, or two 16bit 'words'. The union, allows you to increment the upper 'word', separately. So a word of 00 00 00 00 becomes 00 01 00 00, without having to waste time multiplying etc...

The key to it working though is that the code in the interrupt _must_ be done in less than the interval. Simple subtraction is fast. Multiplication (even by rotation), is dozens of times slower.
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Sat Jul 30, 2016 5:43 pm     Reply with quote

Yes, i understand about not resetting the timers, and in my last code i am not resetting but using the delta, and realized that i CAN in fact use timer1 for all my frequency inputs.

You are correct in that my code could be greatly optimized by incrementing the upper word instead of multiplying, i will work on that tomorrow.

But i am still concerned about the DIV_4 and DIV_16 not working as expected, and compiled produce that same asm in the lst file.

Although i am using ver 4.125, i do plan on updating next week and will try again.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sun Jul 31, 2016 5:08 am     Reply with quote

I just tried pasting the setup line into an empty project, using your compiler version:
Code:

....................    setup_ccp5(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_16);   
00048:  BSF    F93.5
0004A:  CLRF   x0F
0004C:  MOVLW  07
0004E:  MOVWF  x0F
00050:  BCF    x51.2


Now, the only reason it'd clear the register as yours shows, is it thinks the result of the or is zero. Try these lines:
Code:

int16 temp;
    temp=CCP_CAPTURE_RE;
    //then print this
    temp=CCP_CAPTURE_DIV_16;
    //and again print this


I suspect you are going to find that these values are not what you think.
mtsoule



Joined: 19 Oct 2011
Posts: 31

View user's profile Send private message

PostPosted: Sun Jul 31, 2016 5:48 am     Reply with quote

you are correct, they are not what I would expect..

here is what I did:
Code:

    temp=CCP_CAPTURE_RE;
     printf("RE temp: %lu \r\n", temp); //then print this
     temp=CCP_CAPTURE_DIV_4;
     printf("4 temp: %lu \r\n", temp); //then print this
     temp=CCP_CAPTURE_DIV_16;
     printf("16 temp: %lu \r\n", temp); //then print this
     temp=(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_4); ;
     printf("or4 temp: %lu \r\n", temp); //then print this
     temp=(CCP_CAPTURE_RE | CCP_CAPTURE_DIV_16); ;
     printf("or16 temp: %lu \r\n", temp); //then print this
     //and again print this


and here is what I got:
Code:

RE temp: 5
4 temp: 6
16 temp: 7
or4 temp: 7
or16 temp: 7


so I go to my header file:


Code:

#define CCP_OFF                         0
#define CCP_CAPTURE_FE                  4
#define CCP_CAPTURE_RE                  5
#define CCP_CAPTURE_DIV_4               6
#define CCP_CAPTURE_DIV_16              7
#define CCP_COMPARE_SET_ON_MATCH        8
#define CCP_COMPARE_CLR_ON_MATCH        9


obviously, my header file is wrong.. because 5|6=7 and 5|7=7 so, I checked the datasheet...

Code:

bit 3-0 CCPxM<3:0>: CCPx Module Mode Select bits
0000 = Capture/Compare/PWM disabled (resets CCPx module)
0001 = Reserved
0010 = Compare mode, toggle output on match (CCPxIF bit is set)
0011 = Reserved
0100 = Capture mode: every falling edge
0101 = Capture mode: every rising edge
0110 = Capture mode: every 4th rising edge
0111 = Capture mode: every 16th rising edge
1000 = Compare mode: initialize CCPx pin low; on compare match, force CCPx pin high (CCPxIF bit is set)
1001 = Compare mode: initialize CCPx pin high; on compare match, force CCPx pin low (CCPxIF bit is set)
1010 = Compare mode: generate software interrupt on compare match (CCPxIF bit is set, CCPx pin
reflects I/O state)
1011 = Compare mode: Special Event Trigger; reset timer on CCPx match (CCPxIF bit is set)
11xx = PWM mode


and it appears, that if I select 4th or 16 edge, it is automatically risining edge, the fact that I am also including CAPTURE_RE is where my or statement goes wrong..
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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