Obtaining Vcc on 1284/644 using bandgap method

Following on from this old post http://forum.arduino.cc/index.php?topic=56750.0

Coding Badly and Retrolefty came up with a function to help recalculate Aref voltages for changing Vcc, based on the internal Reference.

I wondered if CB or RL, or anyone else, had modified this to achieve the same thing on the 1284 and 644 MCU's?

Well I haven't played with it sense way back when. However it should be pretty straight forward just requiring to select the proper mux channel to select the band-gap as a input source. First step is to get a copy of the datasheet and compare the mux selection section and compare it to the 1280/2560/328p

Lefty

Just had a quick look at the AdMUX section in the datasheet and it looks the same as for Mega2560.

I will see if I can get it working later today. 5.14am and my brain is complaining about being kept awake now! ;-)

tack: Just had a quick look at the AdMUX section in the datasheet and it looks the same as for Mega2560.

I will see if I can get it working later today. 5.14am and my brain is complaining about being kept awake now! ;-)

Great and let us know what you come up with as I too have a couple of boards that run the 644p/1284p

*** NOTE - while looking at this I noticed the 2560 section refers to MUX5, but there is no mention in the datasheet. When copying for 1284 it threw an error for undefined constant, as you’d expect, so I removed it. I will try and check on a 2560 but I suspect that it needs removing there too.

OK, I have it working, after a fashion…

It seems to return Vcc reasonably well. I am using a default bg ref of 1100L at the moment, so calibration can still be done, although this is sufficient for me to see if Vcc drops to approx 4.5v for an alarm…

However, when doing an analogRead(0) I appear to then return a value lower than Vcc. Aref is at Vcc as verified by measurement.

The reason I started looking at this is due to already appearing to have some spurious analogRead results for battery monitoring via a voltage divider. I use 3 analog pins (A0, A1, A2) to measure Battery1, Battery2 and Vin. The Battery tap offs are behind diodes so they can be monitored separately. The Vin tap off is in front of diodes and on the input to my VR.

Using this bg method I should be able to obtain Vcc, Vin, Batt1 and Batt2 and also roughly calibrate the last three for changes in Vcc.

Using the sketch below, supplied via USBASP on USB power, with 4.92V (measured with DMM) on Vcc, AVcc and Aref

Using 1284/644 Bandgap for calculations

Battery Vcc volts =  512
Analog pin 0 voltage = 30

Supplied via 12V battery, through onboard 5V Regulator, with 5.03V (measured with DMM) on Vcc, AVcc and Aref

Using 1284/644 Bandgap for calculations

Battery Vcc volts =  523
Analog pin 0 voltage = 430

You can see that Vcc is showing about +0.2V error (approx +4%) against what I measure with DMM, but Aref is completely wrong.

It seems quite obvious it is the following, where (0) is intended to refer to the Aref pin, but I don’t know if that is general or it’s a symptom of the other analogRead issues I have had playing with my voltage monitor. I will have to try on another board.

analogRead(0)

The modified sketch

// Function created to obtain chip's actual Vcc voltage value, using internal bandgap reference
// This demonstrates ability to read processors Vcc voltage and the ability to maintain A/D calibration with changing Vcc
// Now works for 168/328 and mega boards.
// Thanks to "Coding Badly" for direct register control for A/D mux
// 1/9/10 "retrolefty"
// 13/12/2013 added support for 644/1284 based boards

int battVolts;   // made global for wider availability throughout a sketch if needed, example a low voltage alarm, etc

void setup(void)
    {
     Serial.begin(115200);
     Serial.print("volts X 100");
     Serial.println( "\r\n\r\n" );
     delay(100);
    }
    
void loop(void)
    {
     battVolts=getBandgap();  //Determines what actual Vcc is, (X 100), based on known bandgap voltage
     Serial.print("Battery Vcc volts =  ");
     Serial.println(battVolts);
     Serial.print("Analog pin 0 voltage = ");
     Serial.println(map(analogRead(0), 0, 1023, 0, battVolts));
     Serial.println();    
     delay(1000);
    }

int getBandgap(void) // Returns actual value of Vcc (x 100)
    {
        
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
     // For mega boards
     Serial.println(F("Using 1280/2560 Bandgap for calculations "));
     Serial.println();
     const long InternalReferenceVoltage = 1100L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc reference
        // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (0<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__)
     // For 1284/644
     Serial.println(F("Using 1284/644 Bandgap for calculations "));
     Serial.println();
     const long InternalReferenceVoltage = 1115L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc reference
        // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
#else
     // For 168/328 boards
     Serial.println(F("Using 168/328 Bandgap for calculations"));
     Serial.println();
     const long InternalReferenceVoltage = 1100L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc external reference
        // MUX3 MUX2 MUX1 MUX0  --> 1110 1.1V (VBG)         -Selects channel 14, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
       
#endif
     delay(50);  // Let mux settle a little to get a more stable A/D conversion
        // Start a conversion  
     ADCSRA |= _BV( ADSC );
        // Wait for it to complete
     while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
        // Scale the value
     int results = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L; // calculates for straight line value 
     return results;
 
    }

It’s a side issue, but related to why I’m playing with this method, but the PSU and monitoring circuit I’m using is attached.

This was drawn a while ago and I am wondering if the resistor values could be affecting this part. I know now that the sampling caps need to charge but does this only happen on a read, so my input impedance affecting the charge speed, or are they charged by a constantly applied value?

When I first started with things my thinking was to keep the monitoring circuit leakage current low; with maximum 12v / 2M = 2.5uA.

PSU + Monitoring.jpg

OK, I'm being a dumbass! analogRead(0) is the same as analogRead(A0) so it's trying to read the A0 pin.

This still raises an issue for me as I am currently testing another board and I am getting:-

Using 1284/644 Bandgap for calculations

Battery Vcc volts =  521
Analog pin 0 voltage = 124

For values of 4.99V Vcc and 5.00V on A0, clearly something wrong still with my analogReads.

I'll run a few more tests and also double check the pin corrections to the 644/1284 Bobuino boards.txt files, as there was an issue raised before on that but I'm sure I applied the changes.

When I first started with things my thinking was to keep the monitoring circuit leakage current low; with maximum 12v / 2M = 2.5uA.

You are dealing here with conflicting requirements, one having very low current drain caused by the high resistance values for the voltage dividers. This is desirable as it would be nice if the voltage dividers don't cause battery self draining beyond much more then the batterie's 'shelf-life.

The second requirement is that the arduino analog input pins are designed to read accurate when the input signal has a output impedance of 10K ohms of less. As they is only one sample and hold measurement capacitor in the ADC hardware, the mux must first select the desired input pin and allow some time for the sample and hold cap to charge or discharge to the level equal to the input pin voltage. You high divider resistor values is increasing the RC time constant required for the cap to have captured the still moving voltage value.

So you have to find a trade off that will allow your readings to be more accurate. One possible method is to wire a .1ufd cap from each analog input pin being used to ground. You should test this using an accurate DVM to validate the results.

Lefty

Thanks lefty, you've confirmed what I was coming to suspect.

The recommended 10k is the impedance in series with the input pin I assume, so the 2M resistor.

So, in theory if I could go to 10k / 7.5k then I'd end up with maximum 0.7mA drain @ 12V, per monitor line, or 2.1mA.

Let me just explain what I'm doing....

These board will run on battery power, from 7.2v to 12v input. Standard would be 5 x AA feeding, so 7.5V with 1500-2500mAh cells.

With usage of up to 250mA this gives me approximately 6-10 hours of expected life; my aim is for 8 hours on a single set.

I have the ability to use two sets of batteries to give flexibility for double the running time and/or swapping a set out without interrupting the running.

Now, the battery monitoring is intended to give a warning when a single battery set, or total power in, is getting low. This data will be contained in an RF 'heartbeat' via nRF, back to a base station to indicate if the batteries need changing. Being able to get the Vcc using this method also gives me a failsafe 'Code Red' warning if Vcc falls below 5V.

'Worst' case scenario is the device would be continuously running for 24 hours, so may need multiple battery swaps. Of course, the flexibility is also there to use a higher capacity SLA or LiPo type battery as a supply to mitigate this.

I will give the caps a try to see if that helps. The alternative is to decide whether the extra leakage will really affect my requirements. Even with 2 x 12V batteries then 2.1mA is less that 1% extra consumption, so would reduce a 6 hour running by less than 4 minutes.

Changing the 6 existing resistors on my boards would be easy enough. Adding the caps to the 10 boards I currently have would be more problematic, although could be incorporated into the next revision.

I'll breadboard a 1284 up and try the caps and different resistors to see if it fixes the problem.

Sounds like a man with a plan. ;)

Good luck; Lefty