View previous topic :: View next topic |
Author |
Message |
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
Can I do this conversion without the use of floats....??? |
Posted: Fri May 02, 2014 7:06 am |
|
|
Hi,
I have an LM34 temperature sensor connected to my ADC input thru a 2X amplifier. I'm converting the analog input to temperature like this:
Code: |
TempC[0] = (float)((ReadADCValue(0) * 5)/1023.0)/2;
|
'ReadADCValue' is a subroutine that returns an INT16 averaged value on the specified analog input.
The code shown is working as expected, but it's chewing up an awful lot of available ROM (in a 16F88 device). The reality is that only need the whole integer value for temp. (ie. '23', or '26'), and don't need the fractional part.
Can I do this purely with integers to save ROM space? I've seen references to 'scaled integers', but so far I haven't been able to make that work My compiler is v4.050.
Thanks,
Jack |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri May 02, 2014 7:12 am |
|
|
answer -yes-
and is much simpler if you use 1024 instead of 1023
as in
(5*adcvalue)>>11;
i would UPDATE the compiler version
while at it. |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Fri May 02, 2014 9:04 am |
|
|
Hi,
Well, that suggestion doesn't work, and more importantly I have no idea what you are trying to accomplish with the bitwise shift. Would you care to elaborate on this technique so that I may hopefully learn something from this exercise?
Jack |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri May 02, 2014 9:07 am |
|
|
2^11 =2048
/1024/2=/2048
x>>11 =/2048
and btw
1023/1024=.1% error |
|
|
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Fri May 02, 2014 9:33 am |
|
|
asmboy,
If I look at the formula again it should probably be 1024/2.
thus 512
JAM2014
>> are divided by 2 for each right shift
thus
(5*adcvalue/1024)/2 are the same as 5*adcvalue/512
or
(5*adcvalue)>>9
2^9 are 512
Regards |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri May 02, 2014 9:50 am |
|
|
hmm lets see.....
i have to go by what the OP wrote:
as -is .the core math is screwed anyway as a simple integer value
----
scaling up by 100 would be smarter.
an example from the OP
lets go 3/4 scale on a 10 bit adc
(5*768)=3840
3840/1024=3.75="3"
3/2=1.5=1
now *100
38400/1024=37.5=37
37/2=18.5=18
lastly does the following help?
http://www.ccsinfo.com/forum/viewtopic.php?t=50354 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19512
|
|
Posted: Fri May 02, 2014 10:47 am |
|
|
The old 'fencepost' argument.
The earliest ADC's, were trying to be linear from their Vref+ to Vref-. On these a 10bit ADC would give 0 to 1023 (1024 steps) and require /1023 (1024 fenceposts 1023 actual 'panels'). However quite early on in the design of ADC's manufacturers started deliberately offsetting the values, so that a binary division could be used TI introduced this about 30 years ago...). The PIC ADC, actually reaches it's full scale, one count 'early', so effectively behaves as if is has 1025 steps, and a 1024 division is the right value to use. However they also offset the conversion step points by half a bit. This results in the result always rounding down by half a bit.
The 'best fit' conversion, is to add one, and then divide by 1024. |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Fri May 02, 2014 1:02 pm |
|
|
Hi All,
OK, I understand the concept. It seems like the best strategy then is to start off with the most 'signal' I can get with my analog front end, and then scale the A/D value in my code to obtain temperature result using integer math?
The LM34 output is about 0.7V at room temperature, and I'd like to be able to measure up to about 100+ F, so it seems that the maximum scale factor I could use in hardware is about 4 to stay within the 0 - 5V range of the A/D.
Jack |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1907
|
|
Posted: Fri May 02, 2014 1:23 pm |
|
|
There are 2 avenues to explore/exploit when it comes to this type of problem (reading an analog voltage with the greatest precision you can muster over the expected output range of your sensor).
1) Using a gain stage to amplify the sensor's output to occupy the widest possible swing in A/D input. (Which you're doing)
2) Using an external precision voltage reference(s) to your PIC's A/D.
Regarding 2), you could, for instance, forego the amplifier and instead use a 2.048V reference to your A/D converter instead. At an A/D resolution of 10 bits, 1 A/D count would be precisely 2mV into your A/D. Doing something like this makes the math quite a bit easier.
It is also possible to use a different external reference for the A/D's lower voltage bound as well. Consult the PIC's data sheet for more details, but just keep in mind that the A/D on some PICs will not accept some voltages. For example (and this is from memory so it may not be entirely accurate), I believe that the 18F2680, if powered from a 5V rail, cannot accept a Vref+ of less than 3V. |
|
|
gpsmikey
Joined: 16 Nov 2010 Posts: 588 Location: Kirkland, WA
|
|
Posted: Fri May 02, 2014 3:54 pm |
|
|
With a slight modification on #2 - some PICs have built in precision (band gap) references that you can use for the A/D. Not all have it, but some of them do 18f26k22 for example has one - from the 18(l)F2x/4xK22 data sheet for the FVR (fixed voltage reference):
Quote: | The Fixed Voltage Reference, or FVR, is a stable
voltage reference, independent of VDD, with 1.024V,
2.048V or 4.096V selectable output levels. The output
of the FVR can be configured to supply a reference
voltage to the following:
• ADC input channel
• ADC positive reference
• Comparator positive input
• Digital-to-Analog Converter (DAC)
The FVR can be enabled by setting the FVREN bit of
the VREFCON0 register.
21.1 |
I have not checked recently, but in the past the CCS compiler did not understand how to select the FVR correctly for the A/D so you had to set the config register yourself. Hopefully that has been fixed. _________________ mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3 |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri May 02, 2014 6:23 pm |
|
|
Quote: |
2.048V reference to your A/D
|
true in this instance, BUT
one needs to read the deep specs on each different pic model.
not all pics work with a vref that low.
read min ADC Vref spec in DC section of datasheet to know
if it was the popular 16F887 it would be thus:
AD06A MIN VREF Reference Voltage(3) 2.2 / 2.7
— —
Absolute minimum to ensure 1 LSb
search for AD06 in YOUR Datasheet to find what it is.
in my work, i try not to skate close to the limits .
bad for biz 2 do otherwise |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Tue May 27, 2014 1:21 pm |
|
|
Hi All,
I've made a few changes to my hardware, and now I'm back at this problem. Unfortunately, I'm now having trouble getting my 'scaled integers' working as I expect.
Code: |
int32 TestTemp = 0;
TestTemp = (Int32)(ReadADCValue(0) * 500)/4096;
fprintf(PC, "Sensor 1 Temp: %Lu\n\r", TestTemp;
|
ReadADCValue(0) = 640, so I expect a TestTemp result of 78. I've tried this with and without the 'Int32' casting.
The 'problem' is the value of (Int32)(ReadADCValue(0) * 500). This value is wrong even before the divide.
Compiler is PCM v4.050.
Jack |
|
|
complex72
Joined: 10 May 2014 Posts: 11
|
|
Posted: Tue May 27, 2014 1:32 pm |
|
|
you coluld also use the abs which computes the absolute value of a number.
value = abs(x)
for instance if......
TestTemp=25.63
int16 value = abs(TestTemp)
value will be 25 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue May 27, 2014 1:41 pm |
|
|
You're doing 16-bit math, which generates a 16-bit (ie, wrong, truncated)
result. Then you cast it to 32 bits, but you're casting a 16-bit result.
What you want to do, is to make it do 32-bit math. Don't cast the result.
Cast one of the multiplication operands to 32 bits. Then it will work. |
|
|
JAM2014
Joined: 24 Apr 2014 Posts: 138
|
|
Posted: Tue May 27, 2014 1:52 pm |
|
|
Hi PCM programmer,
Ugh, I had the 'right' idea to 'cast' at least one of the operands, but I mistakenly put the cast outside the (), rather than inside! It's working as expected now!
For completeness:
Code: |
TestTemp = ((Int32)ReadADCValue(0) * 500)/4096;
|
Jack |
|
|
|