Versorgungsspannung ohne zusätzliche Bauteile messen
Die Methode ist nicht auf meinem Mist gewachsen. Google liefert eine ganze Reihe von Links. Google versorgungsspannung+messen+AVR+OR+atmega oder Google atmega+batteriespannung+messen, wo die Methode mehr oder weniger kompliziert erklärt wird. Hier ein konkreter Link in www.mikrocontroller.net.
Beim „normalen“ ADC-Messvorgang wird eine unbekannte Spannung, die an einem der ADC-Eingänge anliegt, gegen eine bekannte Referenzspannung gemessen. Bei der Messung der Versorgungsspannung (VCC) wird VCC als unbekannte Referenzspannung gegen die bekannte interne Bandgap-Spannungsquelle gemessen.
Diese interne Spannungsquelle liefert recht präzise 1,1 oder 2,56 Volt, je nach Prozessortyp. Das Datenblatt für den ATmega328 (Arduino) macht folgende Angaben:
Table 29-12. Reset, Brown-out and Internal Voltage Characteristics
Symbol | Parameter | Min. | Typ | Max | Units | |
---|---|---|---|---|---|---|
... | ||||||
VBG | Bandgap reference voltage | VCC=2.7 TA=25°C |
1.0 | 1.1 | 1.2 | V |
tBG | Bandgap reference start-up time | VCC=2.7 TA=25°C |
40 | 70 | µs | |
IBG | Bandgap reference current consumption | VCC=2.7 TA=25°C |
10 | µA |
Bei www.schramm-software.de habe ich folgende Tabelle gefunden, die Auskunft über die Qualität der internen Referenzspannung gibt:
Betr.spg.\ Controller | 2,5 V | 3,0 V | 3,8 V | 5,0 V |
---|---|---|---|---|
ATtiny13A | 1,10 V | 1,08 V | 1,07 V | |
ATtiny13A | 1,10 V | 1,09 V | 1,07 V | |
ATtiny13V | 1,07 V | 1,08 V | 1,08 V | 1,08 V |
ATtiny44V | 1,10 V | 1,10 V | 1,10 V | 1,10 V |
Die Tabelle zeigt, dass mit einer besseren Genauigkeit gerechnet werden kann, als im Datenblatt angegeben ist. Bei meinem Arduino Uno habe ich 1,105 V gemessen.
Da Referenz- und Eingangsspannung bei dieser Methode die Rollen tauschen, muss die Formel zur Berechnung der Werte umgestellt werden. Die im Datenblatt angegebene Formel
ADC = Vin * 1024 / Vref (Erwartungswert für ADC bei Vref = 5 V und Vin = 1,1 V: ca. 225)
wird zu
Vref = Vin * 1024 / ADC mit Vin = 1,1 V für den ATmega328 (Arduino).
Die folgenden Angaben zur Registereinstellung gelten für ATmega48 / 88 / 168 / 328: Zur Messung muss REFS1/REFS0 mit 01 belegt werden. VCC dient damit als Referenzspannung (Genau genommen wird AVCC zugeschaltet. In den meisten Schaltungen liegt jedoch AVCC auf dem gleichen Potential wie VCC).
Table 24-3. Voltage Reference Selections for ADC
REFS1 | REFS0 | Voltage Reference Selection |
---|---|---|
0 | 0 | AREF, Internal Vref turned off |
0 | 1 | AVCC with external capacitor at AREF pin |
1 | 0 | Reserved |
1 | 1 | Internal 1.1V Voltage Reference with external capacitor at AREF pin |
Die interne Referenzspannung (1,1 Volt) wird über den Eintrag 1110 am ADC-Input-Multiplexer zur Verfügung gestellt.
Table 24-4. Input Channel Selections
MUX3...0 | Single Ended Input |
---|---|
0000 | ADC0 |
0001 | ADC1 |
... | |
1101 | (reserved) |
1110 | 1.1V (VBG) |
1111 | 0V (GND) |
Die Messung erfolgt dann wie üblich. Wie der Wert für den ADC-Prescaler ermittelt werden kann ist hier beschrieben (ADC-Timing). Allerdings dauert es eine Weile, bis sich die interne Bandgap-Spannungsquelle genügend stabilisiert hat. Einige Dummy-Wandlungen vor der eigentlichen Messung sind angemessen (s. www.mikrocontroller.net/topic/98469#853068). Man kann die Wartezeit natürlich auch auf andere Art und Weise verbringen :-).
Hier ein komplettes Arduino-Programm:
/*
* Arduino AVR Project
*
* Vcc messen
*
* Created: 2015-04-27
* Author: Ulli
*
* 2024-02-20: Registerwerte für weitere CPUs hinzugefügt
*
*/
#include "Arduino.h"
#include "AppVersion.h"
// Prescaler ermitteln
#define ADC_CLK_MIN 50000UL
#define ADC_CLK_MAX 200000UL
#include "ADC-Timing.h"
#define DummyReads 10 // Anzahl der Dummy-Readouts nach Hinzuschalten der Bandgap-Referenz zu deren Stabilisierung
#define Vbg 1105UL // Bandgap-Spannung in mV
#define Samples 4 // Anzahl Messungen zur Mittelwertbildung
uint16_t ReadVcc(); // List die Betriebsspannung mit einer Genauigkeit von 10 mV aus.
// the setup routine runs once when you press reset:
void setup() {
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
Serial.print("Vcc: ");
Serial.println((float)ReadVcc() / 1000);
delay(2000);
}
uint16_t readVcc() { // Ergebnis: in mV
// Kanal, Referenz und Bandgap-Spannung für die verschiedenen CPUs
#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny44__)
ADMUX = _BV(MUX5) | _BV(MUX0);
int Vbg = 1100; // Bandgap-Spannung
#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__)
ADMUX = _BV(MUX3) | _BV(MUX2);
int Vbg = 1100; // Bandgap-Spannung
#elif defined(__AVR_ATmega1284P__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
int Vbg = 1100; // Bandgap-Spannung
#elif defined(__AVR_ATmega328__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega48__)|| defined(__AVR_ATmega88__)
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
int Vbg = 1100; // Bandgap-Spannung
#elif defined(__AVR_ATmega8__)
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
int Vbg = 1300; // Bandgap-Spannung
#else
#error Not defined for this CPU
#endif
uint8_t i;
uint16_t result = 0;
ADCSRA = (1 << ADEN) | ADC_PRESCALER_MASK; // ADC einschalten
// Dummy-Readout zur Stabilisierung der Bandgap-Spannungsquelle
for (i = 0; i < DummyReads; i++)
{
ADCSRA |= (1 << ADSC); // eine ADC-Wandlung
while (ADCSRA & (1 << ADSC)); // auf Abschluss der Konvertierung warten
}
// Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen
for (i = 0; i < Samples; i++)
{
ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1 << ADSC)); // auf Abschluss der Konvertierung warten
result += ADCW; // Ergebnisse aufaddieren
}
ADCSRA = 0; // ADC wieder ausschalten
uint32_t r = Vbg * 1024 * Samples / result + 5;
r = r / 10; // Runden auf 10mV
return r * 10;
}
Das Ergerbnis:
Vcc: 4.95
Vcc: 4.96
Vcc: 4.94