PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Akku messen ohne ext. Spannungsteiler



uwegw
09.10.2008, 18:53
Eine Methode, mit der ein AVR seine eigene Betriebsspannung messen kann, ohne einen externen Spannungsteiler zu benötigen.

Ich baue gerne besonders kleine Roboter. Mein aktuelles Projekt wiegt nur etwa 25g.
http://img230.imageshack.us/img230/8045/robenbd8.th.jpg (http://img230.imageshack.us/my.php?image=robenbd8.jpg)
Dafür braucht man natürlich auch sehr kleine Akkus.
Mittlerweile sind aber kleine LiPos recht günstig zu haben. Man muss bei ihnen jedoch aufpassen, sie nicht zu tief zu entladen, sonst gehen sie schenll kaputt. Also muss eine Überwachung der Akkuspannung her!

Die übliche Methode ist es nun, die Akkuspannung über einen Spannungsteiler herunterzuteilen und sie dann mit dem ADC zu messen, der mit der internen Referenzspannung betrieben wird. Der Nachteil dabei ist natürlich, dass man zwei Widerstände braucht, und wenn man auf 3,5x2,5cm einen Mega8, einen Motortreiber, diverse Steckverbinder, Elkos und andere Teile unterbringen muss, ist man für jedes Teil weniger dankbar.

Nun habe ich eine Methode gefunden, wie ein AVR seine Betriebsspannung messen kann, und zwar ohne externe Bauteile.
Die interne Referenzspannung von 2,56V im Mega8 wird aus der sogenannten "Bandgap-Referenz" mit einer Spannung von 1,23V erzeugt. Diese Spannung kann man als Eingang für den ADC auswählen. Und wenn man jetzt AVCC als Referenz für den ADC wählt, hat man folgende Situation am ADC:
-> Eingangsspannung bekannt, 1,23V
-> AVCC unbekannt, Betriebsspannung des AVR= Akkuspannung

Also die umgekehrte Situation wie bei einer normalen ADC-Messung, wo die Eingangsspannung unbekannt und die Referenz bekannt ist.
Das ist aber letztendlich nur ein mathematisches Problem, das leicht zu lösen ist. Man erhält für den ADC-Messwert zu einer gegebenen Akkusspannung:
ADCval= (1,23V/Uakku)*1024
Damit kann man sich nun zu einem Spannungs-Grenzwert den ADC-Wert berechen, um bei Überschreitung dieses Wertes Alarm auszulösen.
Überschreitung, weil die Zuordnung ADC-Wert->Spannung eine Antiproportionalität ist:
Uakku= (1,23V/ACDval)*1024

Die Umsetzung ist nicht weiter schwierig. Man muss bloß bei der Wahl des ADC-Kanals die MUX..-Kombination für Vbg (Siehe Tabelle der ADC-Eingänge im Datenblatt) auswählen. Beim Mega8 entspricht das Kanal 14. Außerdem muss natürlich die Referenz auf AVCC eingestellt sein. Und natürlich klappt es nur, wenn der AVR direkt vom Akku gespeist wird.

fhs
09.10.2008, 19:02
Hallo,

das ist genial! Vielen Dank für die Publikation Deiner Idee!

Gruß

Fred

Michael
09.10.2008, 19:47
Hallo uwegw,

ja, die Idee hat was ;)


Die interne Referenzspannung von 2,56V im Mega8 wird aus der sogenannten "Bandgap-Referenz" mit einer Spannung von 1,23V erzeugt. Diese Spannung kann man als Eingang für den ADC auswählen.
Per Software? Oder per Leiterbahn von Aref auf einen AD-Eingang?


-> Eingangsspannung bekannt, 1,23V
Welchen AVR meinst du dabei?
Ich kenne nur welche mit 2,56V oder 1,1V als interne Referenz.

Gruß, Michael

uwegw
09.10.2008, 20:00
Es wird alles intern geschaltet. Extern wird nur AVCC an VCC gelegt, der AREF-Pin bleibt frei bzw bekommt nen 100n nach GND.

Die 1,23V kann man über das ADMUX-Register auf den ADC-Eingang legen. Im Datenblatt ist ein Tabelle, die die Zuordnung ADMUX-Wert->ADC-Pin angibt. Darin steht neben den "normalen" Eingängen auch die Codes für Vbg. Beim Mega8 ist das die 0b1110. Meine ADC-Routine bekommt als Parameter die Kanalnummer übergeben. Normalerweise also 0 bis 7. Nun bekommt sie einfach die 14, und dann wird die Bandgap-Spannung gemessen.

Die 1,23V sind nur ein interne Zwischenspannung, die man sonst kaum zu Gesicht bekommt. Sie wird noch mal verstärkt, um die 2,56V zu erzeugen.

Getestet hab ich mit einem Mega8 und einem Mega32.

Code für den M8:


uint16_t ReadChannel(uint8_t mux)
{
uint8_t i;
uint16_t result = 0;
ADMUX = mux; // Kanal waehlen
ADMUX|=(1<<REFS0); //Referenz: AVCC
//ADMUX|=(1<<REFS1)|(1<<REFS0); //interne Referenz

/*"Dummy-Readout"*/
ADCSR |= (1<<ADSC); // eine ADC-Wandlung
while ( ADCSR & (1<<ADSC) ); // auf Abschluss der Konvertierung warten
/* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
for(i=0;i<4;i++)
{
ADCSR |= (1<<ADSC); // eine Wandlung "single conversion"
while ( ADCSR & (1<<ADSC) ); // auf Abschluss der Konvertierung warten
result += ADCW; // Wandlungsergebnisse aufaddieren
}
result /= 4; // Summe durch vier teilen = arithm. Mittelwert
return result;
} //end.ReadChannel


void akkutest(void)
{
uint16_t akkumess;

//Bandgap messen, Ref= AVCC = Akkuspannung
akkumess=ReadChannel(14);
if(akkumess<350) {/*...*/} //mehr als 3,5V
else if(akkumess<410) {/*...*/} //mehr als 3V
else {/*...*/} //weniger als 3V

}

Beim M32 ist der Code die 0b11110, also ReadChannel(30);

Michael
09.10.2008, 20:26
Hallo uwegw,

ah, ja, und als "Voltage Referenz Selections" setzt du RefS0 für "AVCC with external capacitor at AREF pin"
Das nenn ich mal tricky ;)

So ein Roboter hat ja so manche Analog-Sensorik an Board.
Wenn du jetzt aber noch andere AD-Kanäle lesen willst, schaltest du dann eine andere Spannung auf Aref?

Gruß, Michael

uwegw
09.10.2008, 21:25
Geplant sind fünf Liniensensoren. Dafür werde ich die Referenz auf AVCC lassen. Die Sensoren (TCRT1000) arbeiten als Spannungsteiler aus Fototransistor und Widerstand, der ebenfalls direkt vom Akku gespeist wird. Und wenn ich nun AVCC als Ref nutze, ändert sich der Messwert bei schwankender Spannung nicht.

Sollten mal Sensoren hinzukommen (ich kann die Sensorplatine einfach aufstecken), wo die absolute Spannung gebraucht wird, würde ich die interne Referenz nutzen.

Besserwessi
09.10.2008, 23:01
Die Methode ist gut, aber nicht wirklich neu.

Wenn für andere Messungen von AVCC auf die 2,56 V umschaltet muß man etwas Aufpassen. Es dauert relativ lange bis die Spannung am Kondensator am AREF Pin sinkt. Also entweder eine paar ms Warten oder den Kondensator eher klein (z.B. 10 nF) wählen.