|
|
View previous topic :: View next topic |
Author |
Message |
ChicoDaRave
Joined: 21 Nov 2014 Posts: 10
|
Math in define (I believe a basic error with float) |
Posted: Thu Mar 05, 2015 4:39 pm |
|
|
Hello.
I'm trying to do some simple math in a define for calculating ADC values in a resistor divisor.
Looks like this:
Code: | #define R1 220
#define R2 47
#define ADCVref 5
#define ADCMaxVal 255
#define VTB(x) (int8)((ADCMaxVal/ADCVref)*((R2/(R1+R2))*x))
|
But the resulting ASM is CLRF 24
I believe it is calculating as integer, so something * zero is zero.
I simplified the math to #define VTB(x) (int8)((255/5)*((47/267)*x))
But same result.
I got a result when added ".0" in all values or to 47 or 267, but 255 and 5 not, which makes sense.
When working with values I can "easily" add ".0" to values, but if I work with defines or variables, how can I do this?
Can I keep R2 define value as just 47 or I really need to put 47.0?
There is another way to force it to calculate as float and later convert to integer?
I have other questions also.
1. There is some way to print something in the output console?
2. There is an way to change C/ASM values from Hex to decimal?
Thank you very much.
Last edited by ChicoDaRave on Thu Mar 05, 2015 5:11 pm; edited 1 time in total |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Mar 05, 2015 4:48 pm |
|
|
what happens if you just
code it as a normal function?
what FLOAT ?
how are you trying to use the define in your program?
what var types are your other items in this thing?
define x !!!
absent casting var types - this is a disastrous bit of mess.
'C is NOT BASIC........
Code: |
int8 VTB( int8 x) {
return(ADCMaxVal/ADCVref)*((R2/(R1+R2))*x);
}
|
what you defined makes no sense to me.
And the compiler is surely just as confused |
|
|
ChicoDaRave
Joined: 21 Nov 2014 Posts: 10
|
|
Posted: Thu Mar 05, 2015 4:58 pm |
|
|
asmboy, this math is just to make easier and tidier to make the code.
Will use like this:
If (ADC <= VTB(21))...
VTB(21) will mean 21v in the resistor divider input.
Got it?
Sorry, I dont know how to explain it better than this :( |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Mar 05, 2015 5:04 pm |
|
|
Quote: | When working with values I can "easily" add ".0" to values, but if I work
with defines or variables, how can I do this? |
An alternative to appending .0 on the end, is to use casting. Example:
or
etc. |
|
|
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Fri Mar 06, 2015 12:05 am |
|
|
The problem are that 47/267 = 0. The way I do that, is to reshuffle the maths so that I first do all the multiplication and then divide as the compiler wants to work in ints if you do not force floats.
Regards |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Fri Mar 06, 2015 2:00 am |
|
|
Alan has said it all.
The key to understand is that float is not being used anywhere. None of the items are float, so float will not be used. The types in a define do not always behave exactly as they do in the language itself, but in general they behave very similarly. So if you start at the innermost section:
R2/(R1+R2)
With all the values coded as integer, this is always going to use integer maths, so is always going to give 0.....
A cast, or adding .0 to R2, forces float to be used throughout.
As a comment, doing your define in the C in Unix also codes as 0. Not a CCS problem, but a failure to understand C. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Fri Mar 06, 2015 2:54 am |
|
|
Ttelmah wrote: | Alan has said it all.
The key to understand is that float is not being used anywhere. None of the items are float, so float will not be used. The types in a define do not always behave exactly as they do in the language itself, but in general they behave very similarly. |
I agree, but I'll add this: the types should behave exactly as in the language, because no maths at all are done in defines, or the preprocessing step. Defines, and C pre-processing in general is just text substitution and string manipulation. Anything that looks like maths in the defines is done, after all the relevant textual substitution, by the compiler itself. It therefore MUST follow the rules of C and be syntactically correct C for it to work. All the normal C typing rules apply.
Note, some, or all of the "maths in defines" can be done at compile time, and just because there is a divide sign, it doesn't mean a divide actually gets done at run time by your PIC. The compiler does what is called "constant collapse" and pre-computes, using the normal rules and syntax of C, the results of expressions formed from constants and replaces them with an equivalent single constant. Its often useful to be aware of this, and to ensure your defines do generate constants, even if the maths in them looks quite complicated.
One word of warning: constant collapse can only happen with simple functions, such as the basic four functions. Maths which are defined as actual C functions, such as sin(), log() etc, in CCS C at least (not necessarily, but most likely in all Cs), even when they have constant inputs, have to be evaluated at run time. The programmer has to pre-compute these and put the resulting number in the define.
I use the same sort of thing for my ADC scaling. I generally accept that the final scaling will require a float operation. I arrange my constants to generate something that I can multiply, as obviously floating division takes much longer. Occasionally I can use a much simpler integer scaling, but for completeness, and to help anyone maintaining my code in the future to understand where the number came from, I tend to still use the same process, but just use the much simpler integer constant.
Personally I don't like to use function macros in situations like this. You can be at the mercy of what the user decides to put in as a parameter, and that can mess with the syntax of the result, giving unexpected results. Also, adding in a (probably) variable breaks the concept of having simple constants, and possibly forces the PIC to do more work at runtime than you are expecting. I prefer for my defined stuff, in this context, to always evaluate to constants that can be predictably collapsed by the complier, and only the operations the PIC does are those that the user uses in their code (as opposed to my defines/macros). So, no hidden, unexpected maths operations.
Here's some of my "maths in defines" from a current (undebugged at the time of posting) project of mine:
Code: |
// Analogue calibration and conversion constants.
#define ADC_VREF 4.096f // Reference voltage
#define ADC_COUNTS 4096 // 12 bits unipolar
#define ADC_VOLTS_PER_BIT (ADC_VREF / ADC_COUNTS)
#define GATE_SENSE_R 220
#define GATE_F_R 47e3 // Required for op-amp circuits. Just use xxxx_SENSE_GAIN for fixed gain amps.
#define GATE_IN_R 47e3 // One or both of these should be float to ensure GATE_SENSE_GAIN is float.
#define GATE_SENSE_GAIN (GATE_F_R / GATE_IN_R) // This is float if the above condition has bees met.
#define GATE_EFF_SENSE_R (GATE_SENSE_R * GATE_SENSE_GAIN) // Actual sense resistance times buffer gain - also float
#define GATE_UA_PER_BIT (1e6 * ADC_VOLTS_PER_BIT/GATE_EFF_SENSE_R) // 1e6 gives uA and forces result to be float.
...
// This is how its used. This requires just one floating multiplication at runtime, despite all the multiplies and divides in the defines.
// This is because GATE_UA_PER_BIT evaluates in the compiler, i.e. "collapses", to just a simple, single float constant.
Gates_Current_uA = (int16)(ADC_Value * GATE_UA_PER_BIT); |
Note how I've used "f" to force a constant to be treated as a float, and used exponent notation on others (resistor values - I prefer to think about 47K than 47000 Ohms). As you already know, using a decimal point also forces the compiler to treat a number as float. Also I've got lots of explanatory comments and done it step by step to make it clear what's going on.
Last edited by RF_Developer on Fri Mar 06, 2015 3:51 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Fri Mar 06, 2015 3:14 am |
|
|
Yes.
There are fractional differences in CCS, but 'fractional'.
The one I know happens relates to 'signed'. If you put a signed value into a #define, it always seems to behave as a signed int16. However in the actual language, for PIC12/16/18, it will if the value is small enough, code as a signed int8.
It's a very small difference, but is why I can only say 'in general they behave very similarly'.
I suspect it is actually because the core code for this part, is common to the different compiler versions, with their different integer sizes.... |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Fri Mar 06, 2015 3:44 am |
|
|
Ttelmah wrote: |
There are fractional differences in CCS, but 'fractional'.
The one I know happens relates to 'signed'. If you put a signed value into a #define, it always seems to behave as a signed int16. |
Yes, as the pre-processor step is rolled into the compiler, there's room for "cheating" and playing fast and loose with syntax. With an old-fashioned separate pre-processor that's not possible as the compilation, and hence the syntax, can have nothing to do with the text substitutions as they are done by completely separate applications. CCS C shouldn't do that sort of thing, but in some cases does. It does that with quite a few things - naughty CCS, but I still much prefer it to the alternatives for PICs. |
|
|
ChicoDaRave
Joined: 21 Nov 2014 Posts: 10
|
|
Posted: Sat Mar 07, 2015 3:19 am |
|
|
Thanks for all answers.
I was 99,999% sure the problem was just lack of my knowledge about C and CCS.
Sorry for my lack of knowledge, If I had a lot knowledge for sure I would not bother you all with such newbie question.
My idea is to really use the compiler to do the math, not the microcontroller.
I want just to make the code easier for my head understand during the coding and later on updating/upgrading as I can quickly and smartly change the resistor values for example.
I want all math to be done as float in compiler, but in microcontroller code EVERYTHING will be integer.
I believe my only error was the lack of some float value in the math, right?
Casting looks a good solution.
Can I do this?
#define VTB(x) (int8)((ADCMaxVal/ADCVref)*(((float)R2/(R1+R2))*x))
Thank you again. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Sat Mar 07, 2015 4:02 am |
|
|
Almost.
Do this instead:
Code: |
#define VTB(x) (int8)((ADCMaxVal/ADCVref)*((R2/(R1+(float)R2))*x))
|
You always work out from the innermost thing. So if the operation R1+R2, is 'float', then the division will use float etc..
Now casting R2 where you show, may work, but the problem is that if the compiler elects to use int8 arithmetic for R1+R2, this value will overflow. It would in the real chip.
If you find the very innermost operation, and promote this to the higher type, and then everything else will use this higher type.
As a comment, 255, is not right.....
The PIC's ADC, though it returns an 8/10/12bit value (so apparently 255 'fence panels' for 8bits, versus 256 'fence posts'), is scaled internally so it reaches the full value a bit _below_ the full voltage, so the correct divisor is 256 (this was an idea first developed by Texas some years ago, to simplify the calculation). have a look at the published application notes about the transfer function. There are some very early PIC's where this is not true, but the current ones are all scaled this way.
On the real chip, if you were wanting to save processing as much as possible, you'd actually do what Alan said. Perform the multiplications first, using integer maths, and only perform one division at the end. So:
Code: |
#define VTB(x) (int8)((x*ADCMaxVal*(int32)R2)/((R1+(int16)R2)*ADCVref))
|
This then does the work in integer to give an integer result.
So (for example, using your values), for x=10, gives:
(10*255*47)/(267*5) = 119850/1335 = 89 |
|
|
ChicoDaRave
Joined: 21 Nov 2014 Posts: 10
|
|
Posted: Sat Mar 07, 2015 1:11 pm |
|
|
Ttelmah, I thank you for your answer, but my knowledge in C and in english is poor, so could not understand some things you wrote.
"You always work out from the innermost thing" Sorry, what you mean with this?
As I said, my knowledge is poor, I'm just trying to learn.
As I said, all this math will be done by the compiler, none in the real chip, I have no intend of doing this math in chip.
If compiler choses int8 for R1+R2 is because R1 and R2 are int8 isnt it?
I understand if I would do the math in the chip the ideal would be simplify the math at maximum and avoid divisions, but the case here is just make the code tidier and easier for a newbie like me code and understand the code later, also, learn something about C and CCS.
I will need some time to understand your calculation but I'm sure it is same calculation but simplified.
About the ADC, you already told me about that but I dont know how to workaround it, It is just put 256 instead of 255?
Thank you again. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Sat Mar 07, 2015 1:16 pm |
|
|
The top formula shows where to put the cast for the #define.
The second one shows you how to organise the function if you were calculating in the chip. This function does the maths doing integer arithmetic, by multiplying first. |
|
|
|
|
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
|