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.
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 |
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;
}