View previous topic :: View next topic |
Author |
Message |
FJMSoft
Joined: 20 Oct 2013 Posts: 36
|
ADC Voltage <> integer values(compiler) |
Posted: Fri Nov 14, 2014 8:04 pm |
|
|
Hello.
This may be a very basic question, my english is poor so I will try my best to explain what I need.
I want to work with voltages using the ADC.
Several times I just want to check for a voltage using ADC then I need to do the math by hand, lets say 8bit ADC, want 4.2v:
4.2v ÷ (5v ÷ 256) = ~215
Then put in the code something like: if ADC > 215...
No problem with the math at all, but it consumes time and code gets less straightforward to understood.
My question is, there is a way to directly put directly the voltage value in the code (instead of the hand calculated integer) and without making the uC spend time calculating this?
I mean, make the compiler do this math.
Kinda: if ADC > 4.2v... or maybe
if ADC > (4.2/(5/256))...
Thank you. |
|
|
jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
|
Posted: Fri Nov 14, 2014 11:18 pm |
|
|
We often do this with a MACRO. Rather than burying constants deep in the code we do:
Code: | #define ADC_LEVEL ( 4.2 / ( 5 / 256 ))
.
.
.
if( ADC > ADC_LEVEL ) etc... |
You can then easily change the 4.2 to whatever you need to in the future. Showing the math helps to explain where the constant came from. The compiler will do the math for you the one time - it does not get done at execution time. Look up MACRO in a C language primer for all the fun things you can do with them.
Happy coding. _________________ Jürgen
www.jgscraft.com |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Sat Nov 15, 2014 1:51 am |
|
|
You can always find out 'what' the expansion codes to, by compiling and looking at the result.
If you try FJMSoft's suggestion, it won't work. Reason is that maths defaults to using integer, and 5/256 in integer, gives 0. So the compiler will complain about a divide by zero error.
However:
Code: |
#define int_adc(x) x/0.01953
#define int2_adc(x) x*51.2
//with you then using:
if (volt>int_adc(4.2))
//or
if (volt>int2_adc(4.2))
|
Will both simply replace intx_adc(4.2), with 215.
The reason is that the compiler 'knows' it can be solved at compile time, since 'x' is a constant in the expression. It then 'sees' the decimal point, and uses float maths _at compile time_ to solve this.
So:
Code: |
.................... adc_val=int2_adc(4.2);
035E: MOVLW D7
0360: MOVWF 20
|
'20' here is the address of the variable 'adc_val', and 0xD7, is 215 in hexadecimal. |
|
|
FJMSoft
Joined: 20 Oct 2013 Posts: 36
|
|
Posted: Sat Nov 15, 2014 9:27 am |
|
|
Wow!
VERY NICE!
Ttelmah code looks more interesting for me, more flexible and voltage value keeps exposed in the code.
So, I can securely make any math like this "#define int_adc(x) x/0.01953" and it will be placed as integer in the code?
Also, 5/0.01953 = 256... being 256 will not make a problem here?
Thank you very much! :D |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Sat Nov 15, 2014 10:24 am |
|
|
It'll mean that if you did use 5, it'd switch to doing an int16 comparison.
However since you can never test for the ADC reading being above 5v, it shouldn't apply.... |
|
|
FJMSoft
Joined: 20 Oct 2013 Posts: 36
|
|
Posted: Sat Nov 15, 2014 10:58 am |
|
|
I dont want to jump into int16.
Above 5v for sure not, but for exact 5v is possible.
There is a way to compare ADC to 5v without using int16?
Using 0.0196 instead of 0.01953 will add a small error but maximum comes back to 255. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Sat Nov 15, 2014 11:23 am |
|
|
You can't compare for 5v, with the ADC at all....
It'll 'read' it's maximum, 1.5bits below 5v.
Historically, if you had an 8bit ADC, you'd have to do division by 255, not 256. This makes it slow in a processor. So some years ago, Texas produced an ADC, that was scaled _as if_ it counted to 256, but then stopped at 255. Just about every ADC since has done the same. The PIC ADC behaves as if it counted to 256 (1024), but stops at 255 (1023). It gives 255, nearly 0.03v _below_ 5v, and then doesn't count any higher.
Just do a #define for (say) MAX_ADC, and MIN_ADC, and use these for the maximum and minimum values. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9245 Location: Greensville,Ontario
|
|
Posted: Sat Nov 15, 2014 12:03 pm |
|
|
There is a 'cheat' I did years ago on an 877 project....which should work for most 5V PICs.
Supply the PIC with a VDD of 5.25 volts.Since the VDD max is 5.5 , this is just below the max and the PIC is happy to function fine.
Another possible 'cheat' is to use the Vref option and ,again, feed that pin with 5.25 volts. This is within the spec of VDD+.3 as noted in the datasheets.
If you really,really need to read 5.00 volts this should work for you. Obviously the volts per bit will have to be recalculated, that exercise is left to you.
Jay |
|
|
FJMSoft
Joined: 20 Oct 2013 Posts: 36
|
|
Posted: Sat Nov 15, 2014 12:26 pm |
|
|
I got it.
Thank you very much! |
|
|
FJMSoft
Joined: 20 Oct 2013 Posts: 36
|
|
Posted: Sun Nov 23, 2014 9:06 pm |
|
|
Hello again.
Friends, I was trying to use this technique in my code, test code is like this:
Code: |
#define VoltToByte(x) x*(255/30) //Voltage will be divided by 6, so maximum will be 30v
#define ChargeCutV VoltToByte(14)
Int8 I;
I=ChargeCutV;
|
The resulting ASM code is:
Code: |
.................... I=ChargeCutV;
014B: MOVLW 70
014C: MOVWF 27
|
Compiler calculated 70h = 112 but...
255 / 30 = 8,5 * 14 = 119 or 77h
How can I solve this?
Thank you very much. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Nov 23, 2014 9:51 pm |
|
|
Get more accuracy with floating point math:
Quote: | #define VoltToByte(x) x*(255/30.0) |
|
|
|
FJMSoft
Joined: 20 Oct 2013 Posts: 36
|
|
Posted: Sun Nov 23, 2014 10:43 pm |
|
|
PCM programmer wrote: | Get more accuracy with floating point math:
Quote: | #define VoltToByte(x) x*(255/30.0) |
|
Now 77h = 119
NICE!
Thank you very much!!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Mon Nov 24, 2014 2:47 am |
|
|
Also, step back. Remember that comment that the ADC _behaves_ as if it has 256 'risers', in terms of it's actual transfer function.
Have a look at AN536 from Microchip. |
|
|
FJMSoft
Joined: 20 Oct 2013 Posts: 36
|
|
Posted: Mon Nov 24, 2014 10:41 am |
|
|
I was giving a little check in the ASM code:
Code: |
.................... if (ADCA<ChargeCutV);
014A: CLRF 29
014B: MOVF 24,W
014C: MOVWF 28
014D: CALL 032
014E: MOVF 23,W
014F: MOVWF 2B
0150: MOVF 22,W
0151: MOVWF 2A
0152: MOVF 21,W
0153: MOVWF 29
0154: MOVF 20,W
0155: MOVWF 28
0156: CLRF 2F
0157: CLRF 2E
0158: MOVLW 6E
0159: MOVWF 2D
015A: MOVLW 85
015B: MOVWF 2C
015C: CALL 04F
015D: BTFSS 03.0
015E: GOTO 15F
|
It is normal so much code for a simple IF?
I believe it is dealing with floating point numbers.
For test, when I remove all decimal points from VoltageToByte formula and also from the define value the IF code is MUCH shorter, like 4 lines.
Ttelmah, I remember what you told me but I dont know what to do in the code about that :( |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Nov 24, 2014 12:03 pm |
|
|
Well, what is one way to convert a float to an integer in C ?
Casting. Do the math in floating point, then cast the result back to an int.
Quote: | #define VoltToByte(x) (int8)((x*(255/30.0)) + 0.5) |
Your next question will be, what about rounding ?
To round off the float, add 0.5 to the result.
Quote: | #define VoltToByte(x) (int8)((x*(255/30.0)) + 0.5) |
|
|
|
|