ADC - Changing supply voltage

BCJKiwi

Senior Member
#1
There have been a number of recent posts concerning accuracy, resolution, battery level sensing and so on.
ReadADC(10) is described as ratiometric and referenced to the PICAXE supply voltage. This means that as the supply voltage changes, the ADC value of the analogue sensor being read will also change. This works well for simple analogue devices that function as a voltage divider would and are operated from the same supply as the PICAXE.
However if the analogue output from the device being monitored is independent of the PICAXE supply Voltage, this is no longer the case.
There are four different scenarios to be considered.
1. Fixed supply, voltage divider type sensor from PICAXE supply
2. Fixed supply, sensor output from separate (stable) supply
3. Changing supply, voltage divider type sensor from PICAXE supply
4. Changing supply, sensor output from separate (stable) supply
Cases 1 & 2 work as expected as the the ADC value will be consistent as the voltage of both the PICAXE and the device do not change.
Case 3 also works as expected as the supplies to the PICAXE and the analogue device both change together and the ratiometric effect means the ADC value will be the same for the same set point on the sensor even though the voltage has changed.
Case 4 however is different as the voltage delivered from the analogue device will be constant for a constant set point, but the PICAXE will read a different ADC value as it's supply voltage changes. A compensating calculation will be required for this arrangement.
Different calculations will be needed depending on whether the voltage or the ADC is required as the results of readADC may be used for different purposes;
a. Acted upon in further calculations - e.g.
shut down if supply too low,
turn on an indicator,
where the compensated ADC value is of interest,
or,
b. Used to indicate the voltage - e.g.
Battery level indicator.
where the compensated voltage is of interest.
It may be argued that no supply is fixed as even with regulated supplies, small variations will occur with changing loads and creep with temperature changes and shifting component values over time. When considering whether the additional overhead in program size and speed is worth the effort, the amount of change in PICAXE supply and the effect of integer math on every step of compensating calculations should be carefully considered as significant variations may arise in the math introducing inaccuracies which may be many times greater than minor variations in supply.
So how would we make the adjustment should we wish to do so?
A known fixed voltage reference is required, against which to compare the supply, and from which to make the adjustment.
The reference can be obtained either from
1. CalibADC(10) if an X1 chip is used,
or,
2. an external reference which requires an additional ADC port.
A 28X1 was used in developing the following code.
It was found that the 0.6V specified as the reference voltage was not very precise, varying from chip to chip by up to 8%.
It was further found that this value also changed with supply voltage.
These variations would be acceptable for establishing a cut off for a battery level but not very accurate for sensor compensation.
Also it was found that a simple Resistor/Diode reference was similarly affected.
In developing the following code, separate batteries were used for both the reference voltage, and the independent analogue signal.
The following circuit is an example of the Resistor/Diode reference. Note, that a relatively low value resistor must be used to drive sufficient current through the Diode to ensure it has a reasonably stable volt drop.
Code:
        V+ ----/\/\/\/\----+----|>|---- V0
                R = 1k     |   BAT85
                           |
                           |
                          ADC
                    Reference Voltage
          ReadADC10 here in place of CalibADC10
It was found in developing these code snippets, that a variety of techniques had to be applied to obtain usable results.
The issue is integer math.
Whenever a multiplication occurs, the result must be less than 65535, the maximum value of a word variable, else overflow will occur.
With Division, the result must contain enough significant digits to provide the accuracy/resolution for the application.
A number techniques have been applied but even so, some forms of calculation are not as precise as one might want.
a. Pre-calculate constants to provide suitably scaled numbers for subsequent use.
b. Multiply the number by a suitable factor to increase its size before dividing (and divide by the same factor later).
c. Use the modulus function to get the numbers after the decimal
d. Use 32bit math.
There are two blocks of code supplied. They show the use of these different techniques.
They also differ in that;
one block uses a reference voltage of 1.58V with an analogue sensor of 3.17V
The other uses a reference voltage of 3.17V with an analogue sensor of 1.58V
These code blocks are not fully interchangeable as some techniques provide better results that others depending on the size of the constants used (which depend on the size of the reference voltage).
The two blocks of code follow in the next two posts.
 
Last edited:

BCJKiwi

Senior Member
#2
ADC Techniques - Fixed reference Voltage > 2V

Code:
' ADC Techniques - Fixed reference Voltage > 2V
' Fixed Ref    V = 3.17
' Fixed Sensor V = 1.58
#picaxe 28x1
SetFreq m4
#terminal 4800
'
'Define some constants to simplify and improve the resolution of follwing code
symbol RefV100 = 320  '(should be 317)          'RefV100 = Fixed Ref voltage * 100 (317 tweaked to improve accuracy)
symbol RefVADC = 3243                           'RefVADC = Fixed Reference voltage * 1023 (3.17 * 1023)
symbol Steps_V = 205                            'Steps_V = 1023 / 5 = 204.6
symbol VADC    = 648                            'VADC    = Fixed Reference voltage * Steps_V (3.17 *(1023 / 5))
symbol RefADC  = w2
symbol SensADC = w1
pause 1000
'
main:
' Reference ADC
    readadc10 2,RefADC                          '2 is the ADC pin# sensor output is connected to
sertxd ("Reference   Voltage    ADC ",#RefADC,cr,lf)
'
' Sensor ADC
    ReadADC10 1,SensADC                         '1 is the ADC pin# sensor output is connected to
sertxd ("Uncorrected Sensor     ADC ",#SensADC,cr,lf)
'
' Corrected Sensor ADC
    w3 = SensADC  *100 /RefADC *VADC
    w4 = SensADC **100 /RefADC *VADC            ' Use High word and Low word 32 bit math to maintain resolution
    w0 = w3 + w4 /100
SerTxd ("Corrected   Sensor     ADC ",#w0,cr,lf)
'
' Supply Voltage
    w3 = RefVADC *10  /RefADC * 100             ' *10/10 to improve integer math accuracy +- 0.02 RESULT is * 1000
    w4 = RefVADC *10 //RefADC
    If w4 > 99 then let w4 = w4/10 :EndIf
    w0 = w3 + w4
    b10 = w0 DIG 3
    b11 = w0 DIG 2
    b12 = w0 DIG 1
    b13 = w0 DIG 0
SerTxd ("Supply             Voltage ",#b10,".",#b11,#b12,#b13,cr,lf)
'
' Sensor Voltage
    w3 = SensADC  *100 /Steps_V
    w4 = SensADC **100 /Steps_V                 ' Use High word and Low word 32 bit math to maintain resolution
    w0 = w3 + w4 *10
    b10 = w0 DIG 3
    b11 = w0 DIG 2
    b12 = w0 DIG 1
    b13 = w0 DIG 0
SerTxd ("Uncorrected Sensor Voltage ",#b10,".",#b11,#b12,#b13,cr,lf)
'
' Corrected Sensor Voltage
    w3 = SensADC  *100 /RefADC *RefV100
    w4 = SensADC **100 /RefADC *RefV100         ' Use High word and Low word 32 bit math to maintain resolution
    w0 = w3 + w4 /10
    b10 = w0 DIG 3
    b11 = w0 DIG 2
    b12 = w0 DIG 1
    b13 = w0 DIG 0
SerTxd ("Corrected   Sensor Voltage ",#b10,".",#b11,#b12,#b13,cr,lf,cr,lf)
'
pause 1000
goto main
 

BCJKiwi

Senior Member
#3
ADC Techniques - Fixed reference Voltage < 2V

Code:
' ADC Techniques - Fixed reference Voltage < 2V
' Fixed Ref    V = 1.58
' Fixed Sensor V = 3.17
#picaxe 28x1
SetFreq m4
#terminal 4800
'
'Define some constants to simplify and improve the resolution of follwing code
symbol RefV100 = 158                            'RefV100 = Fixed Ref voltage * 100
symbol RefVADC = 1616                           'RefVADC = Fixed Reference voltage * 1023 (1.58 * 1023)
symbol Steps_V = 204                            'Steps_V = 1023 / 5 = 204.6
symbol VADC    = 322                            'VADC    = Fixed Reference voltage * Steps_V (1.58 *(1023 / 5))
symbol RefADC  = w2
symbol SensADC = w1
pause 1000
'
main:
' Reference ADC
    readadc10 2,RefADC
RefADC=341
sertxd ("Reference   Voltage    ADC ",#RefADC,cr,lf)
'
' Sensor ADC
    ReadADC10 1,SensADC                         '1 is the ADC pin# sensor output is connected to
SensADC=722
sertxd ("Uncorrected Sensor     ADC ",#SensADC,cr,lf)
'
' Corrected Sensor ADC
    w3 = SensADC *60  /RefADC *VADC /60
    w4 = SensADC *60 //RefADC *VADC /6000       'Improve resolution
    w0 = w3 + w4
SerTxd ("Corrected   Sensor     ADC ",#w0,cr,lf)
'
' Supply Voltage
    w0 = RefVADC *25 /RefADC * 100 /25          ' *25/25 to improve integer math accuracy +- 0.02 RESULT is * 1000
'To display on X1 chips
    b10 = w0 DIG 2 + "0"
    b11 = w0 DIG 1 + "0"
    b12 = w0 DIG 0 + "0"
SerTxd ("Supply             Voltage ",b10,".",b11,b12,cr,lf)
'To display on all chips    
'    b10= w0 /  100                 'Integer Math drops all values in the decimal portion of the result.
'    b11= w0 // 100                 'Note:- Modulus command does not display leading zeros.
'    If b11 < 10 Then: let b11 = "0" :EndIf
'    b12= w0 // 10
SerTxd ("Supply             Voltage ",b10,".",b11,b12,cr,lf)
'
' Sensor Voltage
    w0 = SensADC *50  /Steps_V *20              '(1023 / 5 = 204.6 = steps / volt) RESULT is * 1000 i.e. (*50*20)
    b10 = w0 DIG 3 + "0"
    b11 = w0 DIG 2 + "0"
    b12 = w0 DIG 1 + "0"
    b13 = w0 DIG 0 + "0"
    If b13 => 5 Then let b12 = b12 + 1 :EndIf  'Improve resolution
SerTxd ("Uncorrected Sensor Voltage ",b10,".",b11,b12,cr,lf)
'
' Corrected Sensor Voltage
    w0 = SensADC *50 /RefADC *RefV100 /5        'Sensor Voltage = SensorADC / RefADC * (Ref Volts*100) (result * 1000)
    b10 = w0 DIG 3 + "0"
    b11 = w0 DIG 2 + "0"
    b12 = w0 DIG 1 + "0"
    b13 = w0 DIG 0 + "0"
    If b13 => 5 Then let b12 = b12 + 1 :EndIf  'Improve resolution
SerTxd ("Corrected   Sensor Voltage ",b10,".",b11,b12,cr,lf,cr,lf)
'
pause 1000
goto main
 
#4
The situation below is exactly mine in my battery monitor project, and I need a bit of help please as maths is not my strong point.

I have an 08M monitoring a stable lm385 v ref 1.25v diode. The adc10 result of this changes as the battery V rises/falls no problem. I understand this but I need to pass the cell voltage (as accurate as possible) as a word variable over a serial link to a master monitor. The serial link is not an issue.

So lets assume my battery voltage is 2v and the pic adc10 conversion for the lm385 1.25v at this voltage is 350. I need to correct the adc result so that the variabale holding the result contains 200 (or 2,000 or 20,000 if that helps maintain accuracy) which is the corrected bat voltage.

Likewise if the bat voltage is 4v and the pic adc result is 600 i need to correct that to 400 (or 4,000 or 40,000 if that helps maintain the conversion accuracy.)

I could just measure the bat V at various voltages, note the adc result and build a lookup table but that seems crude and memory expensive, the conversion needs to be done by the 08M if possible.

I would like resolution to 3 digits if possible, i.e. 2.00V or 3.85V etc

Ideas example code? I note the code on this thread but it's for a 28X1

Thanks for help so far.

Case 4 however is different as the voltage delivered from the analogue device will be constant for a constant set point, but the PICAXE will read a different ADC value as it's supply voltage changes. A compensating calculation will be required for this arrangement.
Different calculations will be needed depending on whether the voltage or the ADC is required as the results of readADC may be used for different purposes;
 

BCJKiwi

Senior Member
#5
Without digging into all the detail (that I don't recall instantly), have substituted the relevant values and stripped out the non-08M code.

In theory this should give you what you want - untested but run thru the simulator with the 1.25 ref and 5V supply values.

Code:
' Reading the current Supply voltage using a fixed reference of 1.25V
' Fixed Ref    V = 1.25
#picaxe 08M
SetFreq m4
#terminal 4800
'
'Define some constants to simplify and improve the resolution of follwing code
symbol RefV100 = 125                            'RefV100 = Fixed Ref voltage * 100
symbol RefVADC = 1279                           'RefVADC = Fixed Reference voltage * 1023 (1.25 * 1279)
symbol Steps_V = 204                            'Steps_V = 1023 / 5 = 204.6
symbol VADC    = 255                            'VADC    = Fixed Reference voltage * Steps_V (1.25 *(1023 / 5))
symbol RefADC  = w2
symbol SensADC = w1
pause 1000
'
main:
' Reference ADC
    readadc10 2,RefADC                          '2 is the ADC pin# reference output is connected to
'''''RefADC=255     'ref @ 5V supply
sertxd ("RefV ",#RefADC,cr,lf)
'
' Sensor ADC
    ReadADC10 1,SensADC                         '1 is the ADC pin# sensor output is connected to
'''''SensADC=1023    'sense @ 5V
sertxd ("Raw SensV ",#SensADC,cr,lf)
'
' Corrected Sensor ADC
    w3 = SensADC *60  /RefADC *VADC /60
    w4 = SensADC *60 //RefADC *VADC /6000       'Improve resolution
    w0 = w3 + w4
SerTxd ("True SensV ",#w0,cr,lf)
'
' Supply Voltage
    w0 = RefVADC *25 /RefADC * 100 /25          ' *25/25 to improve integer math accuracy +- 0.02 RESULT is * 1000
'To display on all chips    
    b10= w0 /  100                 'Integer Math drops all values in the decimal portion of the result.
    b11= w0 // 100                 'Note:- Modulus command does not display leading zeros.
    If b11 < 10 Then: let b11 = "0" :EndIf
    b12= w0 // 10
SerTxd ("Supply V ",b10,".",b11,b12,cr,lf)
'
pause 1000
goto main
or so you can see it all at once;

' Reading the current Supply voltage using a fixed reference of 1.25V
' Fixed Ref V = 1.25
#picaxe 08M
SetFreq m4
#terminal 4800
'
'Define some constants to simplify and improve the resolution of follwing code
symbol RefV100 = 125 'RefV100 = Fixed Ref voltage * 100
symbol RefVADC = 1279 'RefVADC = Fixed Reference voltage * 1023 (1.25 * 1279)
symbol Steps_V = 204 'Steps_V = 1023 / 5 = 204.6
symbol VADC = 255 'VADC = Fixed Reference voltage * Steps_V (1.25 *(1023 / 5))
symbol RefADC = w2
symbol SensADC = w1
pause 1000
'
main:
' Reference ADC
readadc10 2,RefADC '2 is the ADC pin# reference output is connected to
'''''RefADC=255 'ref @ 5V supply
sertxd ("RefV ",#RefADC,cr,lf)
'
' Sensor ADC
ReadADC10 1,SensADC '1 is the ADC pin# sensor output is connected to
'''''SensADC=1023 'sense @ 5V
sertxd ("Raw SensV ",#SensADC,cr,lf)
'
' Corrected Sensor ADC
w3 = SensADC *60 /RefADC *VADC /60
w4 = SensADC *60 //RefADC *VADC /6000 'Improve resolution
w0 = w3 + w4
SerTxd ("True SensV ",#w0,cr,lf)
'
' Supply Voltage
w0 = RefVADC *25 /RefADC * 100 /25 ' *25/25 to improve integer math accuracy +- 0.02 RESULT is * 1000
'To display on all chips
b10= w0 / 100 'Integer Math drops all values in the decimal portion of the result.
b11= w0 // 100 'Note:- Modulus command does not display leading zeros.
If b11 < 10 Then: let b11 = "0" :EndIf
b12= w0 // 10
SerTxd ("Supply V ",b10,".",b11,b12,cr,lf)
'
pause 1000
goto main
 
#6
Thanks for that reply but I see you have an extra ADC measuring the battery/cell voltage I do not have that. I am not measuring a seperate cell/bat. I don't have a spare adc input to connect to it's own + rail and anyway would that not just read max every time?

My Pic is supplied from the bat/cell in question.
It's it's own supply V I need to obtain as it varies under an external/load/charge.

I have an adc measuring the 1.25v ref which changes it's result as the pic supply/cell/bat voltage varies.

Cannot the pic's own supply/bat/cell voltage be calculated from this changing relationship?

For instance at
at 2V supply 1.25v ref adc result is 300.
at 4V supply 1.25v ref adc result is 527.

I could test and note the adc result at every supply voltage from 2-4v 50mv intervals, these 40 readings could then form a look up table.

Does that make sense? Is there an easier way. Sorry if I am being thick.

In the example given it appears the pic is measuring an external voltage but it is being supplied from a varying voltage and the routine shown is compensating for that.

Sorry for leading you astray and claiming my scenario was as per no 4, I misread that.
 
Last edited:
#7
Your results can't be right!
Assuming Vref=1.25v (wrt 0v)
At Vcc=2v, the reading should be 1023/2*1.25=639
and at Vcc=4v, it should be 319.

Is your reference voltage correct and is it wrt 0v?
 

BCJKiwi

Senior Member
#8
Well that's the theory adapted from previous tests.

If I understood your requirements correctly you have a fixed reference voltage and wish to monitor the PICAXE supply voltage. I assumed that 'cell Voltage' was PICAXE supply voltage. Please confirm that is what you want to do or advise in more detail - i.e. schematic

The sample code monitored both the reference and the supply so they could be tracked and was intended to show the math of doing this and different techniques to improve resolution/accuracy.

To verify will have to set up an 08M and the voltages. It's just gone 1 am here and I'm off to bed so will have another look tomorrow - well later today!
 
Last edited:
#9
Thanks for your help so far.

Sorry I have been a bit of an Idiot, looking at the example code I see it is for a number of the scenarios. That's what confused me.

I have extracted from it what I believe is the portion I need and I'll try that on my setup tomorrow.

' Reading the current Supply voltage using a fixed reference of 1.25V
' Fixed Ref V = 1.25
'Define some constants to simplify and improve the resolution of follwing code

symbol RefVADC = 1279 'RefVADC = Fixed Reference voltage * 1023 (1.25 * 1279)
symbol RefADC = w2
pause 1000

main:
' Reference ADC
readadc10 2,RefADC '2 is the ADC pin# reference output is connected to
'''''RefADC=255 'ref @ 5V supply
sertxd ("RefV ",#RefADC,cr,lf)
'


' Supply Voltage
w0 = RefVADC *25 /RefADC * 100 /25 ' *25/25 to improve integer math accuracy +- 0.02 RESULT is * 1000
'To display on all chips
b10= w0 / 100 'Integer Math drops all values in the decimal portion of the result.
b11= w0 // 100 'Note:- Modulus command does not display leading zeros.
If b11 < 10 Then: let b11 = "0" :EndIf
b12= w0 // 10
SerTxd ("Supply V ",b10,".",b11,b12,cr,lf)
'
pause 1000
goto main
 
Last edited:
#10
Your results can't be right!
Assuming Vref=1.25v (wrt 0v)
At Vcc=2v, the reading should be 1023/2*1.25=639
and at Vcc=4v, it should be 319.

Is your reference voltage correct and is it wrt 0v?
Yes you are right I had added my own conditioning and inverting code to get a very innacurate guesstimate. Removed now and I'll experiment with the code given by the experts tomorrow. Thanks
 

hippy

Technical Support
Staff member
#11
If you're reading ADC and have the PICAXE connected to the PC serial port, don't forget to use the Advanced Download Interface.
 

BCJKiwi

Senior Member
#12
Peter, still don't have a clear picture of what you want to achieve.

In your case you are using a reference voltage of 1.25 V which is a 1/4 of 5V
If you start from a base of 1023 at 5V supply, then the ref would return 255. These are the numbers used in the test code post #5

However if the supply drops to 2.5V, then the 1.25V ref would return an ADC of 512.

So you need to set up an equation that tracks this inverse relationship, and, do it in such a manner that that you get a) resolution required AND accuracy. Its really easy to be reporting to umpteen decimal places (resolution) but that is of no value if accuracy has suffered. The real killer here is the integer maths inherent in PICs.

If you want further input, please give a bit more info on how your setup is going to work.
 
#13
Thanks for the help I have now sorted it and am able to read the pic's own supply voltage and display it on the picaxe lcd to quite a high degree of accuracy. +/- 25-50mv or so according to my cheapo multimeter.

The modified code I used follows to help any others.

Code:
symbol CellVoltage = w0		;w0=Corrected Cell Voltage 
symbol RefADC = w1		;w1=Raw Adc input data variable 0-1024 10bit
symbol RefVADC = 1279 		;RefVADC = Fixed Reference voltage * 1023 (1.25 * 1279)

`Measure voltage reference
	readadc10 2, RefADC


`*25/25 to improve integer math accuracy +- 0.02 RESULT is * 1000

	CellVoltage = RefVADC *25 /RefADC * 100 /25

'To display on Picaxe Lcd display AXE033 
	b10= w0 / 100 'Integer Math drops all values in the decimal portion of the result.
	b11= w0 // 100 'Note:- Modulus command does not display leading zeros.
	If b11 < 10 Then: let b11 = "0" :EndIf
	serout Lcd,N2400,(254,128,"Supply V ",#b10,".",#b11,"  ")
	serout Lcd,N2400,(254,192,"Raw ",#w0,"  ")
Thanks again to this great forum my BMS design can now progress. ;)
 
Top