Author Topic: accurate Moteino battery voltage monitoring  (Read 21182 times)

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #30 on: April 14, 2016, 08:46:52 PM »
@Whitehare, if you're running this test as soon as your system turns on, the culprit might be the cap on ARef settling into its final charge.  What happens if you don't run the test (leaving the ADC ref at its power on default) for a few milliseconds?

I had a similar thought, so to test that I inserted a "delay(1000)" (surely plenty of overkill, right?) at the very start of the setup() procedure on a stock Moteino R4 RFM69HW board (the earlier results I posted were from just an 8Mhz Pro Mini running at 3.3v).  On the Moteino, it turns out that with or without the added setup() delay, the first ADC sample takes the same 212uS either way:
Code: [Select]
0. raw relative bandgap voltage=386  Elapsed time=0uS
1. raw relative bandgap voltage=363  Elapsed time=212uS  deltaT=212uS
2. raw relative bandgap voltage=353  Elapsed time=324uS  deltaT=112uS
3. raw relative bandgap voltage=349  Elapsed time=436uS  deltaT=112uS
4. raw relative bandgap voltage=348  Elapsed time=548uS  deltaT=112uS
5. raw relative bandgap voltage=347  Elapsed time=660uS  deltaT=112uS
6. raw relative bandgap voltage=347  Elapsed time=772uS  deltaT=112uS
7. raw relative bandgap voltage=347  Elapsed time=884uS  deltaT=112uS
8. raw relative bandgap voltage=347  Elapsed time=996uS  deltaT=112uS
9. raw relative bandgap voltage=347  Elapsed time=1108uS  deltaT=112uS

So, the first surprise was that it made no difference.  The second surprise was that running the same code on a 16Mhz Moteino was barely any faster than on  an 8Mhz Pro Mini.  I haven't yet checked the datasheet to confirm, but it would seem the ADC moves at its own pace (?).

[Edit1:  That said, it is just a one time thing after power-up or reboot, at least while awake.  I confirmed that by putting the whole thing in a loop with a 30 second delay separating each batch of ADC reads.  However, I expect there will nonetheless be a similar price to pay each time the atmega328p wakes up from a full powerdown sleep.]

[Edit2: Anyhow, the main upshot from doing these time measurements was to motivate me to do an ADC noise reduction sleep whlie taking the ADC readings.  Noise doesn't presently seem to be an issue, but if doing that, then I'm hopeful (though haven't confirmed) that the somewhat lengthy ADC sampling will not tally up over time into a significant drain on battery life.]

[Edit3: By the way, the approach I'm taking to measuring bandgap voltage for calibration purposes is simply to power VCC during calibration with a known voltage source (whose value I input just once during the calibration using the serial console) and then automatically impute the true bandgap voltage by measuring the bandgap against it.  The above is part of the code which is leading to that.  Like others have done, I'm planning to store this validated bandgap voltage constant somewhere TBD in the eeprom memory and use that instead of just a "typical" value for bandgap voltage.  I expect that doing this will probably improve the accuracy of reported voltage measurements.]
« Last Edit: April 14, 2016, 10:30:00 PM by WhiteHare »

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #31 on: April 15, 2016, 09:27:54 AM »
I think I found a workable solution.   :)  According to the datasheet, "When the ADC is turned off and on again, the next conversion will be an extended conversion."  So, I simply start a conversion as the very first step in the setup() function by adding two lines of code at its very beginning:

Code: [Select]
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  ADCSRA |= _BV(ADSC); // Convert

I don't have the atmega328p waste time waiting for the conversion to complete, but rather just have it move on from there as per usual.  At least on the 8Mhz Pro Mini, doing that gets the first "extended conversion" speed bump entirely out of the way before the regular sampling starts:

Code: [Select]
0. raw relative bandgap voltage=312  Elapsed time=0uS
1. raw relative bandgap voltage=325  Elapsed time=120uS  deltaT=120uS
2. raw relative bandgap voltage=328  Elapsed time=240uS  deltaT=120uS
3. raw relative bandgap voltage=329  Elapsed time=360uS  deltaT=120uS
4. raw relative bandgap voltage=330  Elapsed time=480uS  deltaT=120uS
5. raw relative bandgap voltage=330  Elapsed time=600uS  deltaT=120uS
6. raw relative bandgap voltage=330  Elapsed time=720uS  deltaT=120uS
7. raw relative bandgap voltage=330  Elapsed time=840uS  deltaT=120uS
8. raw relative bandgap voltage=330  Elapsed time=960uS  deltaT=120uS
9. raw relative bandgap voltage=330  Elapsed time=1080uS  deltaT=120uS

On the 16Mhz Moteino, the results at first don't appear to be as good:
Code: [Select]
0. raw relative bandgap voltage=388  Elapsed time=0uS
1. raw relative bandgap voltage=363  Elapsed time=156uS  deltaT=156uS
2. raw relative bandgap voltage=352  Elapsed time=268uS  deltaT=112uS
3. raw relative bandgap voltage=348  Elapsed time=380uS  deltaT=112uS
4. raw relative bandgap voltage=347  Elapsed time=492uS  deltaT=112uS
5. raw relative bandgap voltage=346  Elapsed time=604uS  deltaT=112uS
6. raw relative bandgap voltage=346  Elapsed time=716uS  deltaT=112uS
7. raw relative bandgap voltage=346  Elapsed time=828uS  deltaT=112uS
8. raw relative bandgap voltage=346  Elapsed time=940uS  deltaT=112uS
9. raw relative bandgap voltage=346  Elapsed time=1052uS  deltaT=112uS
but I think that's probably because at 16Mhz it finishes with the rest of setup() before the conversion initiated at the start of setup completes.  So, rather than resetting the "extended conversion" that's already in progress, I'm hypothesizing that the ADC makes the mcu wait (156-112=44uS) until the "extended conversion" completes before initiating a new sample.  In real life, using the method of running on its 8Mhz internal resonator to gain the benefits of a 3.8uS wake-up, I expect the Moteino results will be the same as the 8Mhz Pro Mini above.
« Last Edit: April 15, 2016, 09:45:15 AM by WhiteHare »

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: accurate Moteino battery voltage monitoring
« Reply #32 on: April 15, 2016, 12:43:22 PM »
It's interesting that in the 8MHz case the bandgap reading is now increasing for the first few, rather than dropping as I would have expected with the charging cap scenario.  This still appears to be the case in the 16MHz case so seems consistent with my expectations.

Tom

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #33 on: April 15, 2016, 04:30:34 PM »
Good catch.  It does seem strange, especially when you consider that the same pro mini in the earlier sketch (reply #28)  had a falling voltage.

As a cross-check, I plugged a different pro mini into the same usb-to-ttl adapter as used by the Moteino, and it also demonstrates a rising voltage when running the most recent sketch.  So, I don't know why these Pro Mini's are behaving differently in that respect, when running the latest version of the sketch.

Changing the subject, here's some food for thought: if on the Moteino I add a 330uSec delay before starting the regular sampling loop (but still having launched the extended conversion as before at the start of the setup() function), then over several trial runs the ADC appears to nail the final bandgap voltage on its first try every time:

Code: [Select]
0. raw relative bandgap voltage=346  Elapsed time=0uS
1. raw relative bandgap voltage=346  Elapsed time=116uS  deltaT=116uS
2. raw relative bandgap voltage=346  Elapsed time=228uS  deltaT=112uS
3. raw relative bandgap voltage=346  Elapsed time=340uS  deltaT=112uS
4. raw relative bandgap voltage=346  Elapsed time=452uS  deltaT=112uS
5. raw relative bandgap voltage=346  Elapsed time=564uS  deltaT=112uS
6. raw relative bandgap voltage=346  Elapsed time=676uS  deltaT=112uS
7. raw relative bandgap voltage=346  Elapsed time=788uS  deltaT=112uS
8. raw relative bandgap voltage=346  Elapsed time=900uS  deltaT=112uS
9. raw relative bandgap voltage=346  Elapsed time=1012uS  deltaT=112uS
That leads me to wonder whether running the multiple samples without any pause at the beginning, as before, is of any benefit, or worse yet, may actually slow down the sampling capacitor from reaching its final settled voltage level.

[Edit: I don't know that 330uSec is the magic number that would work on all Moteino's in all conditions, but perhaps it is, or perhaps there is a more conservative number like, say, 400uSec, that is such a magic number, and where you come out ahead (at least on average) by using it.  I really don't know, as I just now stumbled across this.]
« Last Edit: April 15, 2016, 04:44:50 PM by WhiteHare »

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: accurate Moteino battery voltage monitoring
« Reply #34 on: April 15, 2016, 04:44:02 PM »
It does seem strange, especially when you consider that the same pro mini in the earlier sketch (reply #28)  had a falling voltage.
It's not a falling 'voltage', it's a falling reading on the bandgap due to a rising voltage on the ARef pin.  That the reading is now rising on the first few at 8MHz is odd.
Quote
Changing the subject, here's some food for thought: if on the Moteino I add a 330uSec delay before starting the regular sampling loop (but still having launched the extended conversion as before at the start of the setup() function), then over several trial runs the ADC appears to nail the final bandgap voltage on its first try every time:
I don't understand where this 330uS delay is.  It's between what and what?

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #35 on: April 15, 2016, 04:52:22 PM »
Here's the sketch:

Code: [Select]
// number of microseconds for ADC to setttle before taking a measurement
#define ADC_SETTLE_MICROSECONDS 330
#define NUM_ADC_SAMPLES 10

uint16_t getRelativeBandgapVoltage() {
  uint16_t rawBandgapMeasurement;
 
  // Read bandgap voltage reference (~1.1V) against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);    //need to do this for every sample or just the first?
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  rawBandgapMeasurement = ADCL;  //get the low order bits of ADC
  rawBandgapMeasurement |= ADCH<<8;  //combine with high order bits of ADC
  return rawBandgapMeasurement;
}

void setup() {
  long timeInMicroseconds[NUM_ADC_SAMPLES];
  uint16_t rbgv[NUM_ADC_SAMPLES];  // relative bandgap voltage
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);   
  ADCSRA |= _BV(ADSC); // Convert

  Serial.begin(115200);
 
  delayMicroseconds(ADC_SETTLE_MICROSECONDS);
  for (int i=0;i<NUM_ADC_SAMPLES;i++) {
    timeInMicroseconds[i] = micros();
    rbgv[i] = getRelativeBandgapVoltage();
  }
  for (int i=0;i<NUM_ADC_SAMPLES;i++) {
    Serial.print(i);
    Serial.print(F(". raw relative bandgap voltage="));
    Serial.print( rbgv[i]);
    Serial.print(F("  Elapsed time="));
    Serial.print(timeInMicroseconds[i] - timeInMicroseconds[0]);
    Serial.print(F("uS"));
    if (i>0) {
      Serial.print(F("  deltaT="));
      Serial.print(timeInMicroseconds[i]-timeInMicroseconds[i-1]);
      Serial.print(F("uS"));
    }
    Serial.println();
    Serial.flush();
  }
}

void loop() {
}


ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #36 on: January 12, 2017, 05:13:41 PM »
Forgive me for necro'ing this thread but picking up on WhiteHare's work, I've been able to get pretty good readings much faster by cranking up the ADC from 125kHz to 1Mhz.  The datasheet says not to run it faster than 200kHz for 10-bit precision and then basically says if you run it at 1Mhz you don't lose much precision.  Much more information here: http://www.microsmart.co.za/technical/2014/03/01/advanced-arduino-adc/

Actually, on my TH Nodes, I've been able to take battery voltage readings in 0 extra time!  I wake up and start a conversion and rather than twiddling my thumbs waiting for the ADC to finish, I start issuing commands to my HTU21D.  Right before going to sleep to wait for my first no hold temperature measurement I grab the results from the ADC.  Issuing a command on the fastest possible I2C bus takes around 92us which is longer than the ADC needs for an extended measurement when the prescaler is cranked up to 32 instead of the default 128.  Net result, I get my battery voltage for free except the extra current to keep the ADC on for 100us.

Code: [Select]
startT = micros();           // ==================== START THE CLOCK ====================
    sbi(ADCSRA, ADSC);  // start a conversion
    issueCommand(WRITE_USER_REGISTER, ELEVEN_BIT_TEMP);    // this conversation takes 88uS - plenty long enough for the ADC
    issueCommand(TRIGGER_TEMP_MEASURE_NOHOLD,0);
    Vcc = ADCL;
    Vcc |= ADCH<<8;
    //Vcc = 112296/Vcc;  // 16-bit math takes way too long adding around 40us to this timed loop
    LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF);
    readRaw(&data[0]);
    issueCommand(WRITE_USER_REGISTER, EIGHT_BIT_RH);
    issueCommand(TRIGGER_HUMD_MEASURE_NOHOLD,0);
    LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF); 
    readRaw(&data[2]);
    stopTWI();
    elapsed = micros()-startT;  // ==================== STOP THE CLOCK ====================
« Last Edit: January 12, 2017, 05:18:21 PM by ChemE »

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #37 on: January 12, 2017, 06:59:31 PM »
Nice!  Thank you for your follow-up post.

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #38 on: February 07, 2017, 02:15:34 PM »
Maybe it's thanks to the atmega328p's now very fast 4usec wake-up from sleep, but I was finding the ADC a bit slow at getting up to speed.

Prior to sleeping the atmega, I am turning off the ADC with this:
Code: [Select]
ADCSRA = 0; // disable ADC
which is also how Nick Gammon does it.

Presently I'm using TomWS's method of reading the ADC until I get two of the same values in a row.  It seems like a good approach.  However, I'm finding that if the atmega328p doesn't do one or the other of the the below code snippets to reanimate the ADC immediately after the 4usec wake-up, then TomWS's method often seems to yield erroneous values.

I noticed that ADCSRA has the value 144 just prior to turning it off, so now I wake the ADC up with:
Code: [Select]
  
ADCSRA=144;  //restore ADC settings from just prior to sleeping
as soon as the atmega328p awakens.  That does seem to help.

Instead of that, I've seen code where some people do:
Code: [Select]
  ADCSRA |= (1<<ADEN);  //Power up the ADC
  ADCSRA |= (1<<ADSC);  //Start converting

At the moment, I'm not sure which is better. 

What are you all doing in this respect?
« Last Edit: February 09, 2017, 02:35:10 PM by Felix »

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #39 on: February 07, 2017, 03:50:44 PM »
144 is setting bits 7 (ADEN) and 4 (ADIF) which according to the datasheet is cleared by writing a logical 1 to this bit:

•   Bit 4 – ADIF: ADC Interrupt Flag
This bit is set when an ADC conversion completes and the Data Registers are updated. The ADC Conversion
Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set. ADIF is cleared by hardware when
executing the corresponding interrupt handling vector. Alternatively, ADIF is cleared by writing a logical one to
the flag. Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt can be disabled. This also
applies if the SBI and CBI instructions are used.

You are also setting the ADC clock div to 2 by zeroing out bits 2:0 so you are running your ADC at 8MHz assuming you are running the 328p at 16MHz anyway.

To make your code more readable I'd change your wakeup to:

Code: [Select]
ADCSRA |= (1<<ADEN) | (1<<ADIF);  // Power up the ADC and reset the interrupt flag

EDIT: Thinking about it a little further, you are reading 144 just prior to sleeping because a conversion was finished.  Setting the ADIF bit doesn't actually set it so just setting the ADEN bit should be completely equivalent to setting ADCSRA = 144.
« Last Edit: February 07, 2017, 03:56:36 PM by ChemE »

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #40 on: February 07, 2017, 04:01:55 PM »
Sorry, I failed to answer how I do it.

Code: [Select]
#define       sbi(sfr, bit)           (_SFR_BYTE(sfr) |= _BV(bit))
#define       cbi(sfr, bit)           (_SFR_BYTE(sfr) &= ~_BV(bit))
#define       ENABLE_ADC              cbi(PRR, PRADC); sbi(ADCSRA, ADEN);  // Enable the ADC
#define       DISABLE_ADC             cbi(ADCSRA, ADEN); sbi(PRR, PRADC); // Disable the ADC to save power

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #41 on: February 07, 2017, 04:34:34 PM »
You are also setting the ADC clock div to 2 by zeroing out bits 2:0 so you are running your ADC at 8MHz assuming you are running the 328p at 16MHz anyway.

I'm running the atmega328p at 8mhz on its internal resonator, because that's the only way I know of to wake it up from a deep sleep in less than 4usec.  So, from your comment, I guess that means I'm running the ADC at 4Mhz then.  However, that's still 4x faster than the 1Mhz maximum you seem to be recommending in your earlier post.  Should I increase the prescaler and reduce it down to 1Mhz?
« Last Edit: February 07, 2017, 04:36:24 PM by WhiteHare »

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #42 on: February 07, 2017, 06:10:40 PM »
If you are getting accurate or at least repeatable results at 4MHz then I wouldn't bother slowing down.  I ran at that speed because it was still fast enough to get a conversion started and completed in the time it takes to issue a command on the I2C bus.  In my application I didn't have a need to clock the ADC faster since it was no longer rate limiting for me.

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: accurate Moteino battery voltage monitoring
« Reply #43 on: February 09, 2017, 03:10:01 PM »
Just gave this a try and my readings all seem to take double the amount I see from WhiteHare,
Any ideas why this could be?
(I'm using WhiteHare's code on a Moteino modded to 8mhz, mixed with some other radio code that doesn't touch the ADC or anything)

Code: [Select]
0. raw relative bandgap voltage=333  Elapsed time=0uS
1. raw relative bandgap voltage=333  Elapsed time=416uS  deltaT=416uS
2. raw relative bandgap voltage=333  Elapsed time=640uS  deltaT=224uS
3. raw relative bandgap voltage=333  Elapsed time=864uS  deltaT=224uS
4. raw relative bandgap voltage=333  Elapsed time=1088uS  deltaT=224uS
5. raw relative bandgap voltage=333  Elapsed time=1312uS  deltaT=224uS
6. raw relative bandgap voltage=332  Elapsed time=1536uS  deltaT=224uS
7. raw relative bandgap voltage=333  Elapsed time=1760uS  deltaT=224uS
8. raw relative bandgap voltage=333  Elapsed time=1984uS  deltaT=224uS
9. raw relative bandgap voltage=333  Elapsed time=2208uS  deltaT=224uS

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: accurate Moteino battery voltage monitoring
« Reply #44 on: February 09, 2017, 03:26:01 PM »
When I made that post I was running the code on a 16Mhz Moteino.   ;)