|
|
View previous topic :: View next topic |
Author |
Message |
mtsoule
Joined: 19 Oct 2011 Posts: 31
|
Problem with float math - period to freq |
Posted: Fri Jul 29, 2016 7:58 am |
|
|
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
|
|
Posted: Fri Jul 29, 2016 8:34 am |
|
|
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
|
|
Posted: Fri Jul 29, 2016 8:38 am |
|
|
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
|
|
Posted: Fri Jul 29, 2016 8:41 am |
|
|
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
|
|
Posted: Fri Jul 29, 2016 9:05 am |
|
|
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
|
|
Posted: Fri Jul 29, 2016 9:07 am |
|
|
I copied and pasted Ttelmah's code and the result was:
|
|
|
mtsoule
Joined: 19 Oct 2011 Posts: 31
|
|
Posted: Fri Jul 29, 2016 9:43 am |
|
|
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
|
|
Posted: Fri Jul 29, 2016 10:40 am |
|
|
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
|
|
Posted: Sat Jul 30, 2016 8:49 am |
|
|
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
|
|
Posted: Sat Jul 30, 2016 9:50 am |
|
|
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
|
|
Posted: Sat Jul 30, 2016 9:53 am |
|
|
Code:
#define CCP_CAPTURE_DIV_4 6
#define CCP_CAPTURE_DIV_16 7 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Sat Jul 30, 2016 2:11 pm |
|
|
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
|
|
Posted: Sat Jul 30, 2016 5:43 pm |
|
|
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
|
|
Posted: Sun Jul 31, 2016 5:08 am |
|
|
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
|
|
Posted: Sun Jul 31, 2016 5:48 am |
|
|
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.. |
|
|
|
|
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
|