AVR: Monitor power supply voltage, for free!

While apparently being a very simple task, measuring the voltage of the power supply of your micro-controller can be very important and critical, specially for battery powered applications.

There are many ways to monitor the battery voltage level, many of them propose to add some external components (like a zener diode). The solution we explain in this short tutorial don’t need any external components, for all the AVR micro controllers that have an internal ADC (that like every AVR micro controller that you will find around you!).

As an example in this article, we are going to use an ATMEGA48 micro-controller. The code is 100% compatible with ATMEGA88, ATMEGA168 and ATMEGA 328. It is compatible with minor changes to all ADC equipped AVR micro controllers.


In this article, I assume that you already have some basic AVR micro controller skills, and some basic C programming knowledge. The source codes given in this tutorial are written in C and can be compiled using the WIN-AVR GCC tool chain.

What is so complicated about measuring Vcc?

To answer this question, let’s first put it in context: You have a micro controller, you need to get the (analog) voltage level of the power supply converted to a (digital) value that can be exploited by the micro controller. In other words, you need an ADC (Analog to Digital Converter). Now, you could say:
Okay, so what’s the problems? Let’s just connect the battery positive terminal to an ADC input, and convert its voltage to a digital value!

You could be right, it could be that simple, but the problem is that in most cases, the battery voltage is the micro controller’s VCC, and can also be the micro controller’s ADC reference!

If you look at it from a mathematical point of view, you will surely understand the problem. In general, the relation between measured voltage and Digital converted value for an 8-bit ADC is as follows:

ADC_VALUE = V_measure * 255/V_REF

Now, if you take into consideration that “V_measure = V_REF = Vcc”, then the result of this equation will always yield 255, no mater what is the voltage of the Battery! And that’s exactly the case if you try to measure the voltage of the positive power supply when it is also the ADC reference.

Using the internal bang gap reference

So, to solve this problem, without adding any external components as we promised at the beginning of this tutorial, AVR micro controller have a very useful feature called “internal band gap reference voltage”. The band gap reference is a voltage of about 1.1V, which remains constant, even if the Vcc voltage of the micro controller changes. The B.G. (Band Gap) Voltage can be fed to the ADC like any other external voltage, except it can be done 100% programmatically, that is, with out any external connections.

While it was not designed for this specific purpose, we will use the band gap reference to deduce the VCC voltage of the micro controller.

First, let’s take a look at the logic behind it. if we recall the previous equation:

ADC_VALUE = V_measure * 255/V_REF

But decide to measure the band gap voltage (V_BG), it will be come:


And if we say that the Voltage reference of the ADC is VCC (this must be the case for this trick to work), then the equation becomes:

ADC_VALUE = V_BG * 255/Vcc

At this point, since we know that the V_BG = 1.1V, we can rewrite the equation in the following way:

Vcc = 1.1 * 255/ADC_VALUE

And that’s it, with this equation, you’ve got the actual value of the power supply voltage! Now it’s time to look at how this can be implemented in a short example project

Example project

Here is a little project to illustrate the usage of the band gap reference for Vcc voltage monitoring. In this example (which we actually applied in an older commercial project for a consulting client in USA) we have three cells NiCad battery powering the micro controller. Its voltage varies from 4.2 volts when fully charged to anywhere below 2 volts when fully empty. We need to turn ON a LED when the voltage drops below 3.2 volts.

For the sake of simplicity, the schematic below shows only the minimal components for our example to work.

And below is the AVR C source code that will monitor the Vcc voltage (battery voltage) and light the LED to signal low battery. Note that you need to create a proper AVR Studio project in order to compile this example.

#include <avr\io.h>
#include <avr\interrupt.h>

// Global variables

float vcc;//variable to hold the value of Vcc

void setup_adc(void)
    ADMUX = 0xE; //Set the Band Gap voltage as the ADC input
    ADCSRA = (1<<ADEN)|(1<<ADATE)|(1<<ADIE)|(1<<ADSC)|5;

ISR(ADC_vect) //ADC End of Conversion interrupt 
unsigned char adc_data;
adc_data = ADC>>2; //read 8 bit value
vcc = 1.1 * 255 / adc_data;

// ***********************************************************
// Main program
// ***********************************************************
int main(void) 
    DDRB = DDRB | (1<<PB0); //set PB0 as output (for the LED).
    sei(); //Activate interrupts

    setup_adc(); //setup the ADC

    while(1) { // Infinite loop
        if (vcc < 3.2)
            PORTB |= (1<<PB0);
            PORTB &= ~(1<<PB0);

Final tips

There are some reasons why your Band Gap based program will not work as you thought it would, and in 99% of the cases, there is one symptom and one solution: You may notice that the source code above will give you Vcc voltages that do not match the real voltage of the battery. This is due to the fact that this code needs some calibration in order to function correctly. The Band Gap voltage can be different from a component to another, especially if they do not come from the same batch. I’ve seen AVRs with a band gap of 1.2V and other of 1.01V.

So, If the Vcc that is calculated from the code above is not correct, here is what to do: Measure your Vcc with a volt meter, and using this equation, knowing the value of “ADC_VALUE”, retrieve the value of V_BG:

ADC_VALUE = V_BG * 255/Vcc

Then simply replace the “1.1″ band gap voltage with the one that you just calculated. You could even do it by trial and error, I leave this to your imagination!

The good news is that the band gap voltage is fairly stable with respect to Vcc voltage variation, so once it have be correctly set in the code, you’re good to go!

This concludes the tutorial about using the band gap voltage reference of your AVR micro controller to monitor battery voltage. If you have any questions of comments, please use the forum below.

Comments ( 6 )

Leave A Comment

Your email address will not be published. Required fields are marked *