Batteriemonitor mit ATmega und ATtiny

Batteriemonitor

In den Beträgen Spannungsmessung mit dem Arduino und Präzise Spannungsmessung mit dem Arduino wurden die Spannungen von externen Stromquellen mit dem Arduino gemessen. Jedoch ist es manchmal hilfreich bei einem batteriebetriebenen Projekt die Spannung der eigenen Stromversorgung zu messen, um beispielsweise einen Brownout zu vermeiden oder auch nur eine Batterieanzeige zu realisieren.

Leider kann man nicht die Versorgungsspannung einfach an den Eingang des AD-Wandlers (=ADC) legen und dann diese einfach messen. Damit würde immer ein Wert von 1023 gemessen werden, denn der AD-Wandler verwendet die Versorgungsspannung als Referenz.

Funktionsweise

Unser Mikrocontroller (siehe Tabelle unten) erzeugt eine interne Referenzspannung von 1,1V, die sogenannte "Bandgap Voltage" VBG. Diese Spannung lässt sich auf den Eingang des AD-Wandlers legen. Damit kann man am Ausgangs des AD-Wandlers ablesen:
ValueADC = VBG × 1023 / VCC
Will man nun die Spannung (in mV) ermitteln, formt man diese Gleichung um und setzt die 1,1V (=1100mV) ein:
VCC = 1100 × 1024 / ValueADC
Mit dieser Methode können folgende Mikrocontroller zur Spannungsmessung verwendet werden:

MCU Messung möglich?
ATtiny 25/45/85 ja
ATtiny 24/44/84 ja
ATtiny 13 nein
ATtiny 4/5/9/10 nein
ATmega 48/88/168/328P ja
ATmega 8 ja

Verwendete Bauteile

Sketch

Die Messung findet in der Funktion getVcc() statt: zunächst wird der ADC so eingestellt, dass sowohl die Versorgungsspannung VCC als Eingang als auch die Bandgap-Spannung VBG als Referenzspannung für den ADC dienen. Nach einer kurzen Wartezeit von 5ms wird der ADC dann aktiviert und die Messung begonnen. Nach der Messung ist das Ergebnis dann in den Registern ADCL (untere 8-Bit) und ADCH (obere 2-Bit) enthalten und kann ausgelesen werden (als 10-Bit-Wert). Nun wird noch die Spannung anhand der Formel berechnet und auf der seriellen Konsole ausgegeben.
Will man diese Methode auf einem ATtiny anwenden, so müssen die entsprechenden Register (siehe Sketch) für den MCU gesetzt werden. Natürlich funktioniert beim ATtiny die serielle Konsole nicht und muss aus dem Sketch entfernt werden.

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    Serial.println(String(getVcc()/1000.0) + "V");
    delay(1000);
}

int getVcc()
{
    // reads internal 1V1 reference against VCC
    #if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny44__)
        // for ATtiny84
        ADMUX = _BV(MUX5) | _BV(MUX0);
    #elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
        // for ATtiny85/45
        ADMUX = _BV(MUX3) | _BV(MUX2);
    #elif defined(__AVR_ATmega1284P__)
        // for ATmega1284
        ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    #else
        // for ATmega328
        ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    #endif

    delay(5); // wait for Vref to settle

    ADCSRA |= _BV(ADSC); // reading ADC
    while (bit_is_set(ADCSRA, ADSC));

    uint8_t low = ADCL;
    unsigned int adcVal = (ADCH << 8) | low;

    // discard previous result
    ADCSRA |= _BV(ADSC); // Convert
    while (bit_is_set(ADCSRA, ADSC));
    low = ADCL;
    adcVal = (ADCH << 8) | low;

    return ((long)1024 * 1100) / adcVal;
}

Resultat

Ausgabe auf der seriellen Konsole
Abb.: Ausgabe auf der seriellen Konsole
zurück