Wer seinen Arduino oder einen ATmega bzw. ATtiny über eine Batterie mit Spannung versorgt muss immer damit rechnen, dass die irgendwann einmal leer ist und dann der Mikrocontroller ohne Vorwarnung den Dienst einstellt. Hier ist es hilfreich, die Versorgungsspannung zu überwachen. Sollte sie unter einen bestimmten kritischen Wert fallen, kann man sich zum Beispiel durch anschalten einer LED warnen lassen.
Die Überwachung der Spannung kann ohne zusätzliche Hardware erfolgen. Möglich macht es der AD-Wandler. Dieser arbeitet ja bekannterweise nach folgender Formel:
WERT_ADC = V_EINGANG * 1023/V_REFERENZ
Nun könnte man natürlich meinen: "Ich lege einfach meine Versorgungsspannung an den Eingang des AD-Wandlers und kann diese so messen". Das funktioniert leider nicht, da die Referenzspannung des AD-Wandler ebenfalls die Versorgungsspannung ist. Der Wert des AD-Wandlers wäre als immer 1023.
Doch es gibt einen Trick: jeder Mikrocontroller erzeugt eine interne Referenzspannung, die so genannte "Bandgap Voltage" V_BG. Diese beträgt beim ATmega328, der im Arduino Uno zum Einsatz kommt, 1.1V. Diese Spannung lässt sich auf den Eingang des AD-Wandlers legen. mit V_EINGANG = V_BG und V_REFERENZ = V_CC ergibt sich dann:
WERT_ADC = V_BG * 1023/V_CC
oder umgeformt:
V_CC = V_BG * 1023/WERT_ADC
Setzt man nun ein V_BG = 1100 (in mV) ergibt sich:
V_CC = 1100 * 1023/WERT_ADC
V_CC ist nun der Wert der Spannungsversorgung in mV
Umgesetzt in Code sieht das dann so aus:
int led = 13; //Pin der LED int adc_low, adc_high; //Zwischenspeicher für die Ergebnisse des ADC long adc_result; //Gesamtergebnis der Messung des ADC long vcc; //Versorgungsspannung void setup() { pinMode(led, OUTPUT); ADMUX |= (1<<REFS0); //VCC als Referenzspannung für den AD-Wandler ADMUX |= (1<<MUX3) | (1<<MUX2) | (1<<MUX1); //1.1V Referenzspannung als Eingang für ADC delay(10); //warten bis sich die Referenzspannung eingestellt hat ADCSRA |= (1<<ADEN); //ADC aktivieren } // the loop routine runs over and over again forever: void loop() { ADCSRA |= (1<<ADSC); //Messung starten while (bitRead(ADCSRA, ADSC)); //warten bis Messung beendet ist //Ergebnisse des ADC zwischenspeichern. Wichtig: zuerst ADCL auslesen, dann ADCH adc_low = ADCL; adc_high = ADCH; adc_result = (adc_high<<8) | adc_low; //Gesamtergebniss der ADC-Messung vcc = 1125300L / adc_result; //Versorgungsspannung in mV berechnen (1100mV * 1023 = 1125300) //wenn Spannung kleiner als 5V if (vcc < 5000) { digitalWrite(led, HIGH); //schalte LED an } //wenn größer oder gleich 5V else { digitalWrite(led, LOW); //schalte LED aus } delay(500); }In der setup()-Funktion wird zuerst der AD-Wandler so eingestellt, dass zum einen die Versorgungsspannung V_CC als Referenz und zum anderen die Bandgap-Spannung V_BG als Eingang für den AD-Wandler dienen. Nach einer kurzen Wartezeit wird der AD-Wandler dann aktiviert.
In der loop() wird dann die Messung durch setzen des Bits ADSC gestartet. Ist die Messung beendet (ADSC ist wieder 0) wird das Ergebnis der Wandlung aus den Registern ADCL (die unteren 8 Bit) und ADCH (die oberen 2 Bit) ausgelesen und zu einem 10-Bit-Ergebnis zusammengesetzt. Aus diesem wird dann nach der obigen Formel die Versorgungsspannung bestimmt. Liegt sie unter 5V, wird eine LED eingeschaltet.
Diese Methode funktioniert prinzipiell mit allen ATmegas oder ATtinys. Je nach verwendetem µC müssen allerdings in den Registern des AD-Wandlers andere Bits gesetzt werden. Welche das genau sind, kann dem Datenblatt entnommen werden.
Hallo Udo
AntwortenLöschendas ist ja eine interessannte Funktion. Ich habe sie mal ausprobiert allerdings kommen da sehr stark schwankende Werte heraus.
Das ligt , so denke ich , an den Registern die ich noch nicht an meinen AT-Mega 328P angepasst habe.
Da ich in der Programierung noch nicht so bewandert bin dies leider noch nicht beheben können.
Auch eine Internetrecherche hat mir nicht weitergeholfen.
Könntest Du mir diese Register nennen die ich eindstellen muss.
Vielen Dank Rudi
Hallo Rudi,
Löschendie Register sollten eigentlich identisch sein. Inwiefern schwanken die Werte?
Viele Grüße,
Timo
Hallo Udo,
AntwortenLöschenich fange gerade erst an µC zu programmiren und habe mir ein kleines Projekt als Ziel vorgenommen. Unter anderem muss mein Arduino auch seine Versorgungsspannung messen (1S LiPo) und mir die momentan anliegende Spannung mittels 5 LED's anzeigen.
Dein oben beschriebenes Programm überblicke ich soweit (nicht bis ins Detail) habe aber doch Probleme dieses für den Ardu Nano umzusetzen.
Könnetst Du mir dabei ein bisschen unter die Arme greifen?
Gruß
Jens
Hi,
Löschenja klar. Bei was brauchst du denn genau Hilfe?
Gruß, Udo
Hallo Udo,
AntwortenLöschenich hatte Dir eine Email geschrieben. Ist die angekommen?
Gruß
jens
Hallo Udo!
AntwortenLöschenDas ist eine interessante Idee.
Wie funktioniert das beim Arduino Pro mini mit 3,3V?
Die Spannung wird mit 1100mV gemessen - müssen die Register anders eingestellt werden?
Grüße,
Chris
Hallo Udo,
AntwortenLöschenbei mir kommt da beim Arduino Uno immer raus vcc=2241, sowohl am USB als auch beim Anschluß der Batterien. Wie kann das sein?
Grüße
Max
Hallo Udo,
AntwortenLöschendein Programm funktioniert bei mir auf einem Mega2560 einwandfrei (zeigt zwar bei Betrieb an USb ca. 3800an, aber vorerst nicht so wichtig). Kopiert auf ein Nano (ATmega 328)habe ich bei ADCL =0 bei ADCH=0 und beim Ergebnis vcc= -1
Wo könnte der Unterschied sein?
Gruß Axel
Hallo bei meinem ATtiny45 werden 1100mv gemessen, müssten die Register anders eingestellt werden und wie mache ich das?
AntwortenLöschenHallo Udo,
AntwortenLöschenIch habe dein Programm für die Spannungsüberwachung von meinem Arduino UNO verwendet, bekomme aber keine sinnvollen Ergebnisse. Vermutlich muss ich in den Registern des AD Wandlers Änderungen vornehmen. Ich habe bereits mehrere Datenblätter angesehen, wurde aber leider auch nicht schlauer. Mein µC wäre der Atmega 328P-PU.
Ich wäre sehr froh, wenn du mir dabei helfen könntest!
Hi, vielen Dank für den Code, funktioniert bei mir problemlos. Du könntest vll. noch hinzufügen, dass nach einem "analogRead()" der Code im Setup erneut ausgeführt werden muss, um die Betriebsspannung wieder messen zu können.
AntwortenLöschenIch habe dazu deinen Code in Setup und Loop zusammen in eine readVcc()-Funktion gepackt, die nun problemlos gemischt mit analogRead() verwendet werden kann.
lg, couka
Hi,
Löschenhabe den setup und loop Teil auch in eine Funktion ausgelagert.
Funktioniert prima, zusammen mit analogRead() liefert es aber nur -1.
Um es trotzdem mit analogRead() zu verwenden musste ich noch den ADLAR Bit auf 0 setzen.
Gruß, Sebastian
Sorry aber sobald AREF auf 1,1V liegt dann ist die maximal messbare Spannung ebenfalls 1,1V. Es kann also nie eine Batteriespannung von 3,3V o.ä. direkt gemessen werden, sondern es muss immer noch ein Spannungsteiler eingesetzt werden, der die Batteriespannung in den messbaren Bereich (0V -> 1,1V) bringt. Dann geht's aber!
AntwortenLöschenHallo Udo, super interressant alles. Ich würde gerne die Betriebsspannung von einem Lipo 1s mit einem Attiny85 messen. Ich habe das Programm mal auf einen Arduino Nano geladen da funktioniert es perfekt. Beim Attiny messe ich leider immer die 1,1 V. Du schreibst ja auch was von anderen Bits für Attinys, ich kann mir da nur leider nicht viel darunter vorstellen.
AntwortenLöschenVielen dank im Vorraus.
Gruß Gösta
Hallo Udo, ich nutze das Programm derzeit an meinem Arduino Pro Mini und es funzt wunderbar. Ich stell mir nur gerade die Frage weshalb eigentlich denn die Versorgungsspannung liegt nicht direkt am Atmega an denn dieser wird über einen davorgeschalteten Spannungsregler versorgt (wie auch bei anderen Boards...)...mach ich da einen Denkfehler ?
AntwortenLöschenGrüße
Micha
Super klappt so einfach wie es aussieht. Arduino mit einem 3,7VLipo betreiben bei 3,4V Warnung bei 3,3 Abschaltung. Mit einem Laderegler (MCP73831T-2ACI/OT) wird der Akku wieder geladen und geht nicht mehr kaputt. DANKE!
AntwortenLöschenHallo,
AntwortenLöschenich habe für meinen Heizungsraum einen kleinen Gasalarm programmiert,
dieser soll aber auch Alarm schlagen, wenn die Spannung der Battarie zu Niedrig wird.
Ich habe ihn erst heute in den Dienst geschickt und hatte daher so eine Situation noch nicht.
Im moment mache ich einen analogRead von a3, der mit dem vin pin verbunden ist.
Geht das so, wie ich es programmiert habe?
#include
int State = 0;
LiquidCrystal lcd(11, 12, 22, 23, 24, 25);
void setup() {
pinMode(A1, INPUT);
pinMode(A3, INPUT);
pinMode(22, OUTPUT);
pinMode(23, OUTPUT);
pinMode(24, OUTPUT);
pinMode(25, OUTPUT);
pinMode(26, OUTPUT);
pinMode(27, OUTPUT);
pinMode(28, INPUT);
lcd.begin(16, 2);
Serial.begin(9600);
int Alarm_State = 0;
}
void loop() {
Serial.println(analogRead(A1));
Serial.println(analogRead(A3));
if(State == 0){
lcd.setCursor(0, 0);
lcd.write("Alarm Gas");
lcd.setCursor(0, 1);
lcd.write(" ");
lcd.setCursor(6, 1);
lcd.write("kein Alarm");
digitalWrite(27, HIGH);
digitalWrite(26, LOW);
}
if(analogRead(A1) > 400){
State = 1;
}
if(analogRead(A3) < 1023 and State == 0){
State = 2;
}
if(State == 1){
digitalWrite(26, HIGH);
digitalWrite(27, LOW);
lcd.setCursor(0, 1);
lcd.write("Alarm,Gashahn zu");
}
if(State == 2){
digitalWrite(26, HIGH);
digitalWrite(27, LOW);
lcd.setCursor(0, 1);
lcd.write("Batterie leer ");
delay(500);
digitalWrite(26, LOW);
delay(10000);
}
}
Hallo Udo,
AntwortenLöschendanke für Deine Arbeit.
Dass Programm funktioniert wie es soll, zeigt mir aber die Spannung hinter dem Spannungsregler des Arduinos an (also knappe 5V).
Gibt es auch irgendeinen Weg die Spannung VOR dem Regler zu messen? Ich Betreibe den Arduino mit 9V und würde gerne erkennen, wenn die Batteriespannung abfällt.
Gruß Jens
oder ich nehme einen Spannungsteiler um wieder auf die max 5V zu kommen
LöschenTolles Programm, aber beim Attiny85 funktioniert es leider gar nicht. Vielleicht fehlt noch eine Anpassung für diesen Chip.
AntwortenLöschen