HI All,
From various places I have learnt that to reduce current consumption on a voltmeter, and to reduce the effect on the measuring circuit one should use high impedance Resistors.
As this is a low power thing, and I have not found a definitive design, thought I would ask.
Tom has posted https://lowpowerlab.com/forum/index.php/topic,924.msg7240.html#msg7240 (https://lowpowerlab.com/forum/index.php/topic,924.msg7240.html#msg7240) which is similar to Jeelabs; http://jeelabs.org/2013/05/16/measuring-the-battery-without-draining-it/ (http://jeelabs.org/2013/05/16/measuring-the-battery-without-draining-it/)
As with both I plan a 0.1 µF capacitor to keep the noise down.
I want to measure a 3.7Lipo and a 12V supply (could be upto 15v plus) on different ADC pins. On both I need to assume some over volt protection (+30%?). There is a risk that someone plugs the 12V supply into 24V so I thought I should double the over volt protection here (ie assume 30V).
- For the 3.7 10M+10M - 3.7 gives 1.85v at ADC pin and consumes only 0.185uA
- For the 12V 33M+3.6M - 30v gives 2.951v at ACV pin and consumes only 0.446uA
Any comments on this?
I am breadboarding this with a view to maybe making a through hole PCB.
Some resistors I can easily get in decent packaging numbers (I only want a few!)
33M http://uk.rs-online.com/web/p/through-hole-fixed-resistors/4844488/ (http://uk.rs-online.com/web/p/through-hole-fixed-resistors/4844488/)
3.6M http://uk.rs-online.com/web/p/through-hole-fixed-resistors/6833657/ (http://uk.rs-online.com/web/p/through-hole-fixed-resistors/6833657/)
10M http://uk.rs-online.com/web/p/through-hole-fixed-resistors/6832945/ (http://uk.rs-online.com/web/p/through-hole-fixed-resistors/6832945/)
Any thoughts appreciated.
Thanks
Greg
Greg,
IMO, unless you are running from a 3V coin cell battery, worrying about anything below 1uA is pointless. Even on a 100mAH battery, you're only going to affect the battery life by 1/100000.
The reason I mention this is that the capacitor I referenced isn't to reduce noise, although it does, but that it's necessary to handle the instantaneous capacitive load that occurs when the ADC MUX is changed. A 0.01 is sufficient to effectively counteract this switched capacitance (which is only a few pF), but going any higher than 0.01 will introduce an increasing amount of leakage current that will affect the accuracy of the reading.
If you really want to use the lowest possible current, than follow Joelucid's (also Jeelabs's) technique of reading the ADC value twice, discarding the first reading. With this technique you don't need a cap at all.
However, I believe a reasonable approach is to keep the current domain around 1uA, use a 0.01uF cap, and, to make the math simple, use a 3.3M from the ADC pin to ground and then pick the high side resistor according to the equation R = ((Vin (+ any margin you want) - 3.30)/1.0uA
Easy peasy...
Tom
PS: Re margins, bear in mind that a 3.7V LiPo is really 4.2V when charged and a 12V boat battery could be as high as 16.2 when being heavily charged.
Thanks Tom.
Sorry - typo on my part - 0.01uF is what I meant and others (inc yourself) have suggested.
I will plan on 5v for the LiPo and 30v for the 12v battery in case someone plugs it into a 24v system.
I only need .1v accuracy as I will be round anyway (limited display/message sizes).
Quote from: gregcope on May 31, 2015, 01:51:16 PM
<snip>
I only need .1v accuracy as I will be round anyway (limited display/message sizes).
This is certainly good enough to know when you need to replace the batteries. Be conservative on LiPo replacement since the cliff is pretty steep and the protection circuit will cut it off around 3.2V, I believe...
Again, have fun boating! It's that time of year!
Tom
Why choosing a 0.01uF capacitor? In all the articles they speak about 0.1uF. Is that a mistake or is there a reason for?
Quote from: ulli on May 31, 2015, 03:00:35 PM
Why choosing a 0.01uF capacitor? In all the articles they speak about 0.1uF. Is that a mistake or is there a reason for?
I will confess that I haven't read
all the articles but I have read ones that recommend values between 0.001 to 0.1uF.
In my case the math is simple, the S/H cap in the Moteino is 14pf. If it was fully discharged to ground and had to be fully charged to 3.3V then, to get 1LSB accuracy, you'd need a 0.014uF cap so maybe at least 0.022 would be prudent. However, rarely is this the case, the S/H cap has 'some' charge on it (most likely VCC/2 if the ADC hasn't been used in a while) so 1LSB accuracy for full scale reading would be 0.0007uF. I'll agree that 0.1uF is better from a capacitance perspective, but IMO 0.01uF is sufficient.
Your point is valid, however, in that I shouldn't be so prescriptive about 0.01uF and, instead, suggest a range, which, I believe, would be no less than 0.01 and no more than 0.1uF.
Tom
I have a issue which matches good to that topic, so I hope its fine not posting it as own thread.
My Motino clone is driven by 2xAA batteries directly connected to the 3,3V VCC of the Atmega328p and the RFM69HW. (no regulators for ultra low power consumption)
I want to track the current batterie voltage. I have a 0.1µF capacitor between ADC6 and GND. No voltage devider resistors.
But how can the batterie voltage be measured? The following code will of course not be working because it require to know the reverence supply voltage.
uint16_t myAVR::getADCValue(uint8_t aPin) {
uint16_t batteryReading = analogRead(BATTERY_PIN);
uint16_t battmV = (((float)batteryReading*3.3*1000)/(1024));//((float)batteryReading * 3.3 * 9)/(1023*2.976);
return battmV;
}
I also tried to use the internal voltage reference and not using an ADC pin, but the accuracy is not satisfying.
(I have a 0.1µF capacitor between AREF and GND.)
uint16_t myAVR::getVCC(void) { // gibt tatsächlichen Wert Vcc x 100 aus.
// Wert auf die spezifische internen Bandgap-Referenz z.B. 1,071V x 1000 = 1071 anpassen.
// REFS1 REFS0 --> 0 1, AVcc internal reference -Selects AVcc external reference
// MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) -Wählt den Kanal 14, um die Bandgap-Spannung zu messen
byte oldMUX = ADMUX;
byte oldADCSRA = ADCSRA;
ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
TIMING::delay(2); // Wait for Vref to settle
ADCSRA |= (1<<ADPS2) | (1<<ADPS1); // Den ADC aktivieren und Teilungsfaktor auf 64 stellen
// Den ADC initialisieren und einen sog. Dummyreadout machen
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));
uint16_t result = 0;
for(uint8_t i=0; i<3; i++) {
ADCSRA |= (1<<ADSC);
while(ADCSRA & (1<<ADSC));
result += ADC;
}
ADMUX = oldMUX;
ADCSRA = oldADCSRA;
result /= 3;
result = (((AVR_InternalReferenceVoltage * 1024L) / result) + 5L) / 10L; // Wert scalieren / Berechnung für geraden Wert
return (uint16_t)result*10L;
}
How would you recommend to measure the VCC voltage level?
How about calibrating the 1.1V (+/- 10%) internal reference using a voltmeter?
Ulli,
I see two differences between the way you read your VCC and the way I do (and others, since I 'borrowed' the code). First, we use a 20mS delay for the input mux to settle, rather than 2mS you use - 2mS should be ok, but it is a difference. The other thing, that's probably more significant, is that the following code has 'result' as an unsigned long rather than the uin16_t that you use, but equally significantly, you divide BEFORE multiplying - big loss of precision here... In this code's case, the 'division' is built into the coefficient that is used to multiply 'result'. Consequently max precision is maintained.
Finally, I'm not sure what accuracy you're looking for, I'm satisfied with this code's accuracy for measuring the battery voltage...
/******************************************************************************
*
* readVcc()
*
******************************************************************************/
unsigned int readVcc() {
unsigned long result;
byte saveADMUX;
saveADMUX = ADMUX;
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284P__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
#if defined(__AVR_ATmega2560__)
/****
it took me a while to figure-out the problem, but on MEGA 2560, immediately after analogRead(A8), ADCL started returning zero.
So every attempt to read from A8-A16 on Arduino MEGA will damage the functionality of readVcc().
I've resolved the problem by adding:
ADCSRB = 0;
just before
delay(2);
**********************************/
//ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560
ADCSRB = 0;
#endif
delay(20); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
result = (high<<8) | low;
PRINT("BatteryResult="); PRINT(result);
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 (because...= 3300*1023/3 since 1.1 is exactly 1/3 of 3.3V)
PRINT(", after calculation:"); PRINTLN(result);
return result; // Vcc in millivolts
}
Quote from: TomWS on June 01, 2015, 05:05:57 PM
<snip> I'm satisfied with this code's accuracy for measuring the battery voltage...
As a further explanation of this statement, I am satisfied for two very important reasons:
1. To try to get any better accuracy would require additional precision hardware that I have no interest in adding to a Moteino because...
2. I know enough about battery performance curves that, for a given battery technology, it's less about the actual voltage and more about the 'curve'. If I care about how much longer my battery is going to last, all I need to see is the curve. I have a pretty good idea when to replace the batteries whether it says 3.204volts or 0.965% of nominal voltage.
Tom
Hi Tom,
Thanks for you explenations, very helpfull. I will try your code soon and give a feedback.
Therefore you are just using the internal referance with a capacitor at the AREF pin and no ADC pin is needed?
Regards, ulli
Quote from: ulli on June 02, 2015, 03:40:13 AM
<snip>
Therefore you are just using the internal referance with a capacitor at the AREF pin and no ADC pin is needed?
Yes, that is correct. Since there is no VR, referencing VCC directly is virtually identical to measuring at the battery. I just use the existing capacitor on the Moteino AREF pin.
I also use this function to calibrate any ADC measurements that relate to a specific voltage, scaling the reading by VCCmeasured/3300 using float variables.
Tom
Some comments:
* you did save the ADMUX config, but you never write the old values back
* the idea to calibrate with that measurement result the other ADC readouts is awesome!
* I used your function and got a VCC value of 2.660V, which is stable when I do a few readouts, but the voltmeter measures a VCC value of 2.910V.
Do you know why I get such a bad accuracy?
Before I set the ADCSRA byte(These bits determine the division factor between the system clock frequency and the input clock to the ADC.) to a division factor of 64. If you are running the default config you have a division factor of 2.
Do you think that makes a difference?
Quote from: ulli on June 03, 2015, 04:49:05 PM
Some comments:
* you did save the ADMUX config, but you never write the old values back
LOL! Well, at least I covered HALF the problem! Good catch!
Quote from: ulli on June 03, 2015, 04:49:05 PM
* the idea to calibrate with that measurement result the other ADC readouts is awesome!
yeah, it helps when you're running directly from a battery. If running from a VR, it's probably not that useful.
Quote from: ulli on June 03, 2015, 04:49:05 PM
* I used your function and got a VCC value of 2.660V, which is stable when I do a few readouts, but the voltmeter measures a VCC value of 2.910V.
Do you know why I get such a bad accuracy?
Before I set the ADCSRA byte(These bits determine the division factor between the system clock frequency and the input clock to the ADC.) to a division factor of 64. If you are running the default config you have a division factor of 2.
Do you think that makes a difference?
To be honest, I've never checked the ADPS bits. I guess I expected them to be set to the proper value for a given CPU frequency. I'd be surprised if it's anything other than a prescaler of 128 (which should be default for 16MHz processor). I'll have to check this. I'm not sure why your reading is so far off. You are running with an 8MHz clock, correct?
Tom