I’m using a custom board with an ATmega328PB to measure the voltage of a 7Sfett gedruckter Text Li-ion battery.
The battery voltage is measured via a voltage divider (R1 = 100 kΩ, R2 = 10 kΩ, C = 100 nF). Since I only need a coarse battery level indication (low / medium / high), I’m fine with not using the full ADC input range.
Problem:
Each time I revise my custom board and order a new batch, I measure a different battery voltage, even though the actual battery voltage is the same. From my research, the root cause seems to be that the 3.3 V LDO output feeding the ATmega (AVCC) is not exactly 3.3 V, and this directly affects the ADC result when using analogReference(DEFAULT).
I’m therefore considering switching to EXTERNAL AREF and would like to know if this is a good and reliable approach, especially for motor/PWM environments.
If so:
Is a direct trace from LDO 3.3 V to AREF + 100 nF to GND sufficient?
Or are additional components (RC, ferrite, buffer) recommended?
If USB voltage is used as reference: 5V ± 0.25V (5%)
If a voltage regulator is used ±1 or 2% (LP2985 on UNO R3: 3.3V ± 4%)
If you are using resistors without specified tolerances, they may be 5%
From the datasheets I don't see improvement for the 328PB in respect to the 328P: internal reference 1.1V ± 0.1V (10%)
(Usually, a bandgap reference is designed to achieve better accuracy, Microchip might be protecting itself against lawsuits here.)
If the goal is just a indication, use resistors 1% and internal reference 1.1V.
If you really need accuracy, use better (more expensive) resistors and a good reference source (not cheap) to AREF.
You already know that this does not give a reliable result:
Only if you have a reliable external voltage reference to connect to AREF. You know that your 3.3 LDO is not a reliable source. Do you have any other reference voltages in your circuit?
I'm currently working on a 328PB project which includes measuring the voltage of an RTC coin cell with respect to the 5V rail. To avoid needing extra parts, or precision parts, I'm using a two-step ADC process to get an accurate reading. It requires an initial calibration of each 328PB.
First I measure the voltage of the IRV (the internal ~1.1V bandgap reference voltage) with respect to Vcc. This gives me a backward calculation of what Vcc is. Then I measure the coin cell (about 3.1V) with reference to Vcc.
But this has to be calibrated for each 328PB. You select the value of the IRV that gives you the same answer as your meter, and save it in millivolts as an unsigned integer in the first two bytes of EEPROM. Then in the future, that value should continue to work with any LDO, or whatever. You just retrieve the IRV from EEPROM, and do the two-step ADC, and it should be quite accurate.
I have found this to be pretty stable, but it does vary just a little with significant differences in supply voltage, such as 5.2V vs 4.75V. It may also vary a bit with temperature, but I haven't tested that. Anyway, here's the code:
unsigned int IRV ; // millivolts
void doVoltage() { // calculate VCC with respect to IRV
EEPROM.get (0, IRV); // calibrated IRV stored in EEPROM
byte savADMUX = ADMUX;
ADMUX = (1 << REFS0) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1);
delay(50);
// Start a conversion
ADCSRA |= _BV( ADSC );
// Wait for it to complete
while ( ( (ADCSRA & (1 << ADSC)) != 0 ) );
// Scale the value
float VCC = (float)(((IRV * 1024.0) / ADC) / 1000.0);
ADMUX = savADMUX; // read battery voltage with respect to VCC
delay(50);
analogRead(A0); // toss first ADC reading
float volts = (float)analogRead(A0) * VCC / 1024.0;
}
I found that just calibrating to get a Vcc that agrees with the meter gave me a small error when later measuring the coin cell. So I calibrated on the final result. I don't know why the difference exists. Perhaps my calculation isn't quite right (such as divide by 1024 vs 1023, or rounding).
I think you are all right. Given the existing setup of my custom board, there is no perfect solution.
Since I only need to display the battery level as Low, Medium, or High, I will proceed with the default Analog Reference and resolve the issue through software. I will account for the tolerances of the minimum and maximum battery voltages to ensure the readings stay within range and approximately show the correct battery level.
You havent given an idea of the size of the differences. They should not be large,
(the internal reference ISNT calibrated, but it IS very stable)
The data sheet here quotes it as 1.1V +- 0.1V ATmega328P
I don't see any reason not to use the internal reference...
Is this "production" where you have multiple devices? If so you should probably make a calibration routine/procedure and save the calibration in EEPROM.
If it's a one-off project you can hard-code the calibration/correction in your program. ...I made an SPL loudness meter and since i only make one I hard-coded the calibration "variables" into setup(). (And it was just a temporary "experiment" project anyway.)
A standard straight-line calibration is an offset which is a number added/subtracted to the readings. Normally the offset is measured/applied at zero and the offset is done first. (If zero is read correctly, which should be the case with this setup, the offset is zero so the readings aren't altered)
And a slope (a multiplication factor) which is measured/calibrated at the maximum reading or at the "expected" reading. (i.e. if no correction is needed the calibration factor is 1.0.)
If you are calibrating, the resistor tolerances are not important since any errors are linear and they will be calibrated-out anyway.
Since I only need to display the battery level as Low, Medium, or High
In that case you probably don't need a lot of accuracy.
For Li-ion batteries you might want a threshold for switching off the load. Even if you have protection in your battery pack, it switches off at an absolute minimum and switching a bit sooner may improve life span significantly.
As an alternative to the calibration from @DVDdoug you can measure the 1.1V from the bandgap reference at the AREF pin (during and after a measurement with INTERNAL 1V1), put it in your sketch and re-upload (this doesn't compensate for tolerances in the resistors).
How bad IS your 3.3V regulator? The popular lm1117 is supposed to be withing something like 4% of the target voltage, and it seems unlikely that that would interfere with your low/medium/high thresholds!