|
|
View previous topic :: View next topic |
Author |
Message |
PICman
Joined: 02 Nov 2007 Posts: 26
|
...Float multiplication/division not working |
Posted: Thu Dec 01, 2011 12:30 pm |
|
|
Hi....
My code is a controller for a DDS chip (AD9850).
I want to enter the RF frequency directly ("frequence" variable) and then, do a simple calculation (one multiplication and a division) involving the reference frequency, to get the right frequency word to be sent (serially) in the DDS chip.
My problem is that the PIC does not perform the calculations.
If i enter the frequency word directly, everything works perfectly ! The data is sent and the DDS generates the required signal..
I've done a lot of C for computers (PC) without any problem, but for the PIC, i don't know why, it doesn't work !
The variables are "float"..
In the "calcul_bits" subroutine, i do perform a substraction with success, but something prevents the multiplication/division to be performed.
Compiler CCS 4.120, µC: PIC12F1840
Code: |
// DDs controller (AD9850) using 12F1840 in C.
//////////////////////////////////////////////////////////////////////////
// _________ _________ //
// __| \/ |__ //
// | | | | //
// +5V |__|Vdd Vss|__| //
// __| PIC12F1840 |__ //
// | | | | //
// |__|Ra5 Ra0/ICSPDAT|__| Clock //
// __| |__ //
// | | | | //
// |__|Ra4 Ra1/ICSPCLK|__| Data //
// __| |__ //
// | | ____ | | //
// |__|Ra3/MCLR/Vpp Ra2|__| Latch //
// |____________________| //
// //
//////////////////////////////////////////////////////////////////////////
#if defined(__PCM__)
#include <12F1840.h>
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES WDT_SW //No Watch Dog Timer, enabled in Software
#FUSES PUT //Power Up Timer
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(int=8000000,RESTART_WDT)
#use rs232(baud=9600,parity=N,xmit=PIN_A3,rcv=PIN_A2,bits=8,stream=PORT1)
#use delay(clock=4000000)
#endif
#include <stdlib.h>
void sortie_bits(void);
void sortie_zero(void);
void sortie_un(void);
void sortie_latch(void);
void calcul_bits(void);
void ini_pic(void);
int1 signal[36];
float reference,frequence,addit,diviseur;
void main(void)
{
ini_pic();
reference=125000000;
while(1)
{
delay_ms(500);
frequence=1050;
calcul_bits();
sortie_bits();
}
}
void ini_pic(void)
{
setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
setup_oscillator(OSC_8MHZ|OSC_INTRC|OSC_PLL_OFF,0);
}
/////////////////////////////////////
void calcul_bits(void) //
{ //
int8 c2,ex; //
//
addit=1050*4294967296; // \__ This portion doesn't work !
addit/=125000000; // /
//
// addit=34359738; // If i uncomment this line,
// everything works !
//
diviseur=2147403648; //
//
for(c2=0;c2<32;c2++) //
{ //
ex=31-c2; //
//
if(addit>=diviseur) //
{ //
signal[ex]=1; //
addit-=diviseur; //
} //
else //
signal[ex]=0; //
//
diviseur=diviseur/2; //
} //
} //
/////////////////////////////////////
////////////////////////////
void sortie_bits(void) //
{ //
int8 c1; //
//
for(c1=0;c1<32;c1++) //
{ //
if(signal[c1]==1) //
sortie_un(); // Output required bits
else // folowing the 32
sortie_zero(); // "signal" bits.
} //
//
sortie_zero(); //
sortie_zero(); //
sortie_zero(); //
sortie_zero(); //...followed by 8 zeroes
sortie_zero(); // (control/phase bits)
sortie_zero(); //
sortie_zero(); //
sortie_zero(); //
//
sortie_latch(); //
} //
////////////////////////////
//////////////////////////
void sortie_zero(void) //
{ //
delay_ms(1); //
output_low(PIN_A0); //
output_low(PIN_A1); //
output_low(PIN_A2); //
delay_ms(1); // Output a ZERO
output_high(PIN_A0); //
delay_ms(1); //
output_low(PIN_A0); //
delay_ms(1); //
} //
//////////////////////////
//////////////////////////
void sortie_un(void) //
{ //
delay_ms(1); //
output_low(PIN_A0); //
output_high(PIN_A1); //
output_low(PIN_A2); //
delay_ms(1); // Output a ONE
output_high(PIN_A0); //
delay_ms(1); //
output_low(PIN_A0); //
delay_ms(1); //
} //
//////////////////////////
//////////////////////////
void sortie_latch(void) //
{ //
delay_ms(1); //
output_low(PIN_A0); //
output_low(PIN_A1); //
output_low(PIN_A2); // Output a LATCH
delay_ms(1); //
output_high(PIN_A2); //
delay_ms(1); //
output_low(PIN_A2); //
delay_ms(1); //
} //
//////////////////////////
|
Thank for your attention ! _________________ The ideal electronic world: A place where the words VERSION and REVISION do NOT exist ! |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Dec 01, 2011 1:05 pm |
|
|
Code: |
addit=1050*4294967296; // \__ This portion doesn't work !
addit/=125000000;
// addit=34359738;
|
my trusty HP35s says your two lines of
math resolve to
36077. 7253 ??
there is a HUGE difference in the result you show -
- ORDERS of magnitude in fact --
the two lines are NOT resulting in 34359738 as you seem to imply ;
just in case
did you try "casting" the results of your float operations
with the (float) casting call?? |
|
|
PICman
Joined: 02 Nov 2007 Posts: 26
|
|
Posted: Thu Dec 01, 2011 6:14 pm |
|
|
1 - 0 for your HP35 !!!!
In debugging, i tried different values for my DDS.
And in editing / translating (from french to english) my code to send it here, i erased the wrong value. 34359738 is for a 1 MHz output signal...
(1000000*4294967296/125000000)
I never tried casting.... Is casting used to "translate" a variable between float and int ? Because my variables are all float.
Hmmm.. back to the CCS manual !!! _________________ The ideal electronic world: A place where the words VERSION and REVISION do NOT exist ! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Dec 01, 2011 6:39 pm |
|
|
Quote: | addit=1050*4294967296;
addit/=125000000;
|
Just tell the compiler that the numbers (constants) are floats. Add ".0"
on the end of them. Then it will do floating point math instead of integer math. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Fri Dec 02, 2011 2:47 am |
|
|
Take a look at http://www.ccsinfo.com/forum/viewtopic.php?t=24066 which deals with a very similar problem. maybe even the same problem.
For someone who has "done a lot of C for computers (PC) without any problem" you seem unaware of basic C, such as casting and constants which are not specific to CCS.
Float does not have the precision to accurately represent the constants you are using. It may work well enough, however. The poster of the other thread noticed this and was hopign to use doubles instead, but CCS doesn't provide a double length floating point type.
What both you and they have failed to notice is that the constants include powers of two, such as 4294967296 and 2147403648, which are 2^31 and 2^32. This says that fixed point, i.e. scaled integer, arithmetic is most appropriate for this and should, if done carefully, retain all the precision, not loose a lot as floats will.
Also
Code: |
addit=1050*4294967296; // \__ This portion doesn't work !
addit/=125000000; // /
|
would collapse in the compiler if done on one line. In other words the PIC wouldn't do the calculation each and every time your routine runs, instead it would be done just once when the code is compiled, just as Asmboy did with his HP35.
Code: |
// addit=1050*4294967296 / 125000000; // \__ This portion...
addit = 36077.7252864; // ...collapses to this in the compiler.
|
Even then, "*4294967296" doesn't even need to be done by multiplication, it is simply left shift by 32 bits. And then you divide the result down by 2^31, but hang about, you just multiplied by 2^32, so 32 bits of this simply cancel. You could simply do:
Code: |
// addit=1050 / 125000000 * 2; // \__ This portion...
addit = 0.0000168; // ...collapses to this in the compiler.
|
There's other thing which show you're not really thinking about your code. Such as looping up in your for loop, then immediately subtracting the count from 31. Instead why not simply loop down from 31 to zero in your for()? For loops don't just count up....
Is this an SPI device by any chance? Using your own bit-bashing to send data to it eh? With 1ms delays everywhere? You do realise that the PIC12F1840 has SPI hardware that will do all that for you, don't you?
RF Developer |
|
|
PICman
Joined: 02 Nov 2007 Posts: 26
|
|
Posted: Fri Dec 02, 2011 6:05 am |
|
|
Hi RF_developer...
In calculations, i always try to privilege the use of ints and double precision floats. I rarely use single precision floats.
And i wanted to use INTs for this project... but can't !!!! The first multiplication already implies 2^32 (The 32bit DDS freq accumulator), so no multiplication possible, since it would require way more than 32 bits. I cannot invert the operations either (divide freq by reference first) since the result woult be zero. Finally i cannot divide 2^32 by the reference since an INT result would be in the magnitude of 34, thus mediocre precision.
I know that the final solution would be to use a power of 2 crystal (e.g. 134.217718 MHz), but i'd prefer by far to have the code do the work instead of having to order a custom crystal. Also, working with code would give me the option to "fine tune" the DDS by slightly warping the "reference" value instead of mechanically tune the quartz.
I didn't try casting since the variables are ALREADY formatted as float... But i'll try it ! _________________ The ideal electronic world: A place where the words VERSION and REVISION do NOT exist ! |
|
|
PICman
Joined: 02 Nov 2007 Posts: 26
|
|
Posted: Fri Dec 02, 2011 5:48 pm |
|
|
Thanks PCM_programmer !!!!!
I had to add ".0" to all my values to "tell" the compiler that i need a float value.
But what i don't understand, is that i have ALREADY configured the variables as floats !!! It looks like an autocast function. You configure a variable as a float in your code, but if you enter a number that looks like an INT (no DP), the compiler switches (or casts) it to an INT.... Is it possible to disable this function ?
Thanks for the help ! _________________ The ideal electronic world: A place where the words VERSION and REVISION do NOT exist ! |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Fri Dec 02, 2011 7:00 pm |
|
|
The compiler won't think like you..it will try to be a mathematician. The mathematical notation on the left side of an equal sign doesn't take on the notation of the left hand side until the calculation is done and ready to be assigned. Integers are notated without a decimal point and come in 8 bit 16 bit and 32 bit format. The compiler will minimize the calculation and use the smallest integer when possible. This means if you want 3 to be a 32bit integer you need to cast it to be so otherwise it will get 8 bits. The notation for float (since so far for CCS there is only one float size) is 3.0 this is how notation works. 3.0 notation is equivalent to (float)3 notation.
The fact that you ask for the result of the right hand-side to be placed in a float doesn't dictate float will be used during the intermediate calculations required to produce the result. Remember the internal notation of a number in a pic register is binary..the compiler will switch from decimal to binary for you...decimal and binary notations are only equivalent for integers and decimal fractions that are a power series of 2. Conversion to and from decimal notation to binary notation can never be exact except for the condition in the prior sentence. |
|
|
PICman
Joined: 02 Nov 2007 Posts: 26
|
|
Posted: Sat Dec 03, 2011 4:53 am |
|
|
Thanks Douglas Kennedy...
That confirms what i said on my last post... CCS has an "autocast" function that automatically casts the variables to the smallest memory needs possible... Is that makes useless the variable declaration at the code's beginning (simply declare everything as "int" and let the compiler do the rest) ? _________________ The ideal electronic world: A place where the words VERSION and REVISION do NOT exist ! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Sat Dec 03, 2011 5:30 am |
|
|
No.
This is not CCS, but C, and the variable sizes are distinct from the maths sizes.
In C, when you evaluate any mathematical function, the compiler looks at the value(s) used. It takes the _highest_ type of these, and this is the arithmetic type used for the operation. So:
2*3
Both values are integer, and both fit into 8bit (in CCS), so int8 arithmetic.
2L*3
Both values are integer, and the first is a 'long' (int16 in CCS), so int16 maths.
(int32)2*3
Both integer, but first value is int32, so int32 arithmetic used.
2.0*3
First value is now a float, so float arithmetic used.
Now, this applies for each section of an arithmetic formula, so:
3.0*(54*56)
Here the 54*56 is evaluated first, and since both values are integer, and will fit into an int8, int8 arithmetic is used. The result is then converted to a float, and float arithmetic used for the second multiplication by 3. Since the first value has overflowed, the result will be 208*3.0 = 624.0....
The type _is_ derived from the number, if a constant value is used, but is the variable type, if a variable is used. So:
Code: |
int fred=54;
int16 harry=56;
float val;
val=3.0*(fred*harry);
|
Will result in fred*harry being solved in int16 arithmetic, since this is the type used for harry. This is then multiplied by 3, to give 9072.0.
If you force a type in a sum, the result is then converted _back_ after this is done. So:
harrry=2.16*(fred*harry);
Gives harry, as the integer 6531 (the result is 6531.84, and the decimals are simply lost).
However:
fred=2.16*(fred*harry);
Gives fred = 179, with just the low 8 bits being saved of the result.
If you declared 'everything as int', then this type of trimming would take place with every result.
The _only_ difference about this in CCS, is that the default int type is int8, and there is no automatic saving of overflows. On the PC, because the hardware arithmetic unit, uses int16 as it's _lowest_ supported type, sums default to being done in int16, even if int8 is specified. Also the unit automatically saves overflows, so that it is slightly more forgiving. Only _slightly_ though.
Best Wishes |
|
|
|
|
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
|