Adding new bandgap to your code

Hi Folks,

Quick question, hoping someone can kindly help, I've lost my way a little bit...

I've a MEGA 2560 - and I have a project where I'm reading analogue voltages through voltage dividers - that part of the project is all set up fine.

The issue is, I'm moving this Arduino around so that I am powering the it via different USB busses and thus my Vcc is different each time - which then in turn affects my readings.

In search of a solution, I decided that if I could figure out my actual bandgap voltage, then this would be pretty stable and take care of my issue. I calculated it to 1.082V.

My problem is, I'm now not quite sure how to get this into my code on my particular Arduino, if it is possible at all...

Hope this makes sense!

Thx
B

The10 bit successive approximation ADC on the Mega2560 returns values 0-1023. The correct interpretation of these values is that the input voltage to the ADC is

voltage = AREF*value/1024

Use either the stated bandgap reference value for AREF, or if you have a better estimate, use that.

Special case: if value = 1023, then the best interpretation is

voltage >= AREF*value/1024

2 Likes

For voltage measurements you should always use one of the internal reference voltages. The Mega has two. INTERNAL1V1 and INTERNAL2V56. Both are stable, but not factory calibrated. For easy use, you can write the exact voltages on the board, or save them in the EEPROM of the chip. For ratiometric sensors, like pots, pressure, ADS712 etc., you always use default Aref.

Just switch to the choosen Aref in setup() with this line.
analogReference(INTERNAL2V56);
The 1024 A/D values will now be spread over 0 to 2.56volt.
Your voltage divider should be calculated for that 1.1volt or 2.56volt.
Leo..

3 Likes

First off, thank you both for your speedy replies and help.
Secondly - Apologies for my apparent ineptitude in trying to grasp this issue.

I'm now going to waffle for a bit. You probably already understand what i'm about to type, I am only doing so as a means of getting things clear in my own mind.
Normally when reading an analogue voltage on an analogue pin I would do something like:
(Vcc/1024)*readValue
Problem with this is my Vcc changes when I USB power the Arduino on different computers.
I could use the internal 1.1V but this too is not accurate on different USB busses.
What I need is an accurate reference voltage on my Arduino.
Fortunately on my arduino there is such a voltage that is resistant to change regardless of the temp and the Vcc. This voltage is the bandgap voltage and can be different on every Arduino, but will remain the same on the same Arduino as it is and inherent property of its MCU semiconductor makeup.
Thus, i found a sketch (somebody else's work) that figures out your MCU bandgap.
This is how i think it works.
Inside the MCU, the ADC has it's own set of registers governing its operation.
It has a function that allows us to compare voltages - and we just want to compare the bandgap voltage against the VCC.
When you read in the registers what the Arduino thinks its Vcc is, it might spit out something like 5.14V but if you measure the Vcc pin with a DMM, it might actually read 5.06V - so why the difference? I think because it's referenced against 1.1V and it's not really 1.1V.

ideally, as I jump between different USB busses and my Vcc changes, i'd like to know this Vcc, not the Vcc that the Arduino thinks it is - but the actual Vcc measured on the DMM

So anyway - Using the bandgap calculator code - I know that my bandgap is 1.082V for this particular Arduino board.

I'm thinking now that I'll somehow have to set this bandgap Voltage in my code or figure out some under the hood trickery, or in some way use it to back calculate (as my bandgap is fixed at 1.082V) my Vcc.

Ahhh, just a thought.

If, 1.1/Vcc = myBandgap(let's say 222)/1024

Then after transposing: Vcc = (1024/222)*1.1 = 5.07V

Seems right but, nah.. I'm not getting it though - how would I take this info and use it on a different USB bus, with a different Vcc?

Thanks all, for the help. I think i need to go away and do some more reading about using and manipulating the MCU registers.

Cheers!!!
B

You're talking about the same thing here.
1V1 and 2V56 are the bandgap voltages that come with the Mega chip. They are stable enough to measure a battery voltage that powers the Arduino, but don't expect too much from them.

If you're seeing different voltages on different USB supplies, then I suspect a built problem.
Shared ground of the voltage divider, wrong values, no buffer cap, other analogue inputs used, etc. If you want help with the problem, then give at least a full picture of your setup. Pictures, links to parts, code, and what you're measuring.
Leo..

This won't give the expected answer in C/C++ because of the integer divide (1024/222).

Use instead
Vcc = (1024.0/222.0)*1.1

Can you run a digital out pin with a 10k resistor into AREF and call analogReference(EXTERNAL)?

Aref voltage can be measured on the Aref pin.
Not sure if that only appears after an analogRead call.

I would rely more on the result from reading a voltage.

float voltage = analogRead(A0) * 1.075 / 1024;
Serial.println(voltage, 3);

Then change "1.075" until you get the correct voltage printed.
Leo..

1 Like

Thank you everybody for your time and input so far. I'm in work right now, but later I'll digest all that you said, make some further tests, and probably have a few more questions.

You're help is greatly appreciated :slight_smile:

Hi folks,

Thanks again for all the help so far...

Oh, I thought there was only one "true" bandgap, as in the innate property of the semiconductor make-up of the MCU on your own board. Each Arduino board has its own MCU, and they in turn have their own bandgap value, which sits somewhere between 1.0V and 2.0V. As why trying to use that bandgap value, as when I figured out what it was, I know that it stays the same regardless of the changing Vcc you get on different USB buses.

Anyway this is all moot really because my thinking and thus calculations were all out.

Yep, just typed that fast, i was actually using a point... honestly... but you're dead right.

Thx, not sure how that would help though... unless I'm missing something???

Yep, that would help :slight_smile:

So, I broke it all down and went back to basics... It's like I'm dealing with some crazy brain fog at the minute.

To make matters simple - I fed about 4V directly into an analogue input and read its output using the 5V as ref, the result was spot on.

I then added my voltage dividers. A 30K and a 5K. Where and why do I have these values?! Well I started off needing to read a 24V input, and the value was 30K and 7.5K, which would give me 25V max. I then realised that actually the PSU voltage I am reading could potentially supply up to 30V so I changed the second resistor to 7500 (yep i know i could have gone to 6000, it's what I had on hand).

Here's the thing - The voltage divider output, when measured with a DMM is outputting the correct voltage, and this is across different voltage input values - so, the circuit is working well, it's feeding the expected level into my Arduino, but when I look at my serial out, the reading is off somewhat, depending on the voltage that i fed in.
When i'm measuring a voltage input up around the 25V range (it's +/- 4mA) - but down at say a 5V input, it's quite a way out (+/- 20 or 30mA).

Thinking about it - am i just expecting too much for a 10 bit ADC? Is that what my issue here is?

What i'm trying to do. Well I need to read a 5V, a 12V, and a 24V (potentially 30V) signal, at the same time - however the plug I'm connecting into to take this reading could be wired wrong, and thus I need to keep all 3 voltage dividers the same for fear of getting the wrong voltage on the wrong divider and destroying my arduino. My dividers are all working as expected, they are outputting the correct voltage.

I'm hoping that someone could just confirm my issue and what I'm doing wrong. Also, why would the reading be more or less 'in' around the 25V input level but not at 5V.

Hope this all makes sense.

Thx
B

When you use a voltage divider, the MCU "sees" a voltage that's the "ratio of the divider" lower than what the voltage actually is. To display the original voltage, you must of course multiply with the same resistor ratio.
Not sure what the mA values have to do with voltage measurements.

If you use a voltage divider to 1volt, and use 1.1volt Aref, then the input can still safely take 5volt.
Say you use a 10k (to ground) and 39k (to 5volt) voltage divider, to measure 5volt. If you accidently connect 24volt to that divider, then there is still only 10k/(10k+39K)*24= 4.9volt on the pin.
Leo..

Google....

external vref arduino

Have you looked at the Arduino site and looked at the analogReference parameter?

Tom... :grinning: :+1: :coffee: :australia:

Again thanks for the replies...

Couple of kids, mortgage, wife, juggling 2 jobs... head is fried... I meant mV not mA :slight_smile:

Could I ask you to kindly take a look at my code and to see if i'm doing something stupid.

Forget everything else I've said, all I'm doing now is a simple voltage divider on a breadboard. Precision resistors, R1 = 30000 (the + of my variable PSU connects to this), R2 = 5000 (the - of my variable PSU connects to this, and my Arduino GND also connects to this point).

I have a calibrated fluke DMM to check all values.
My Arduino is powered by its own external PSU and 5V on the Arduino measures bang on 5V

First I set my variable PSU to 5.32V (verified with DMM)
I measured my voltage divider output with my DMM (whilst the output was also connected to the Arduino) = 0.76 (bang on what it should be)
My arduino read 5.20 to 5.23 (it skipped between the 2), so 9 - 12 mV out

Next I set my variable PSU to 24.84V (verified with DMM)
I measured my voltage divider output with my DMM (whilst the output was also connected to the Arduino) = 3.457 (bang on what it should be)
My Arduino read a steady 24.83, so 1mV out (not bad at all)

My question is, why does it read higher voltages with greater precision?

My code:

double r1{30000.0};
double r2{5000.0};
int readPin{A1};
double readVal{0};
double Vcc{5.003};
double readPinVoltage{0};
double finalVoltage{0};
int dt{500};

void setup() {
pinMode(readPin,INPUT);
Serial.begin(9600);
}

void loop() {
readVal=analogRead(readPin);
readPinVoltage=readVal*(Vcc/1024.0);
finalVoltage=readPinVoltage/(r2/(r1+r2));
Serial.println(finalVoltage,2);
delay(dt);
}

Thx for the heads up - will take a look :+1:

Precision (A/D steps) is actually ± 34mV, because of the 10-bit A/D and the 1:6 voltage divider (5.0/1024*7). The problem you're seeing seems to be the linearity of the A/D (2% off in the lower region). I think your expectations are too high from an MCU that's not meant to be a voltmeter.
If you want a voltmeter, then use an external A/D that's designed for absolute measurements, like an ADS1115 (15-bit single-ended). But don't buy a fake one from Ali/ebay/Amazon.
Leo..

2 Likes

Agreed... And brilliant. Thank you so much for the help and patience, especially as I went all around the houses. I initially thought that my bandgap calculations and using that to back calculate Vcc every time I used a different USB bus was the issue, but in fact it was this.

For now I can probably add some maths to get my readings closer. Using a separate ADC as you suggested is what I'll be doing next.

Again. Thank you so much :blush:

You can take and average multiple readings to get a more precise result as described here

You need to calibrate the converter to take account of gain error (dependent on the reference voltage and voltage divider)
and also (first) zero offset error - which is why your low measurements are less accurate.

You do that by applying a known low positive voltage (to get around 10 - 16 from the adc) and adding or subtracting from your result to get the right reading.
Then apply a large positive voltage (to get near 1000 from the adc) and tweak your scaling factor to get an accurate voltage.

1 Like

Thanks for the the link...

Wow, what a treasure trove of information... I'll be working through this over the weekend.

Ha, I'm not the sharpest tool in the box, I only just realised that was you. Thank you for documenting and sharing all of this, much respect :+1:

1 Like

I've now updated that page which hopefully makes it more accessible; and you will also find the new code that does all the calibration for you.

1 Like

Thank you John, your help is greatly appreciated :slight_smile:

1 Like

Did you mean 1100mV?

Nice explanation , Thanks.

1 Like