-
        

Ergebnis 1 bis 5 von 5

Thema: nibobee: Akkuspannung mit ADC messen

  1. #1
    Neuer Benutzer Öfters hier
    Registriert seit
    05.03.2009
    Beiträge
    20

    nibobee: Akkuspannung mit ADC messen

    Anzeige

    Hallo,

    ich habe mich in letzter Zeit mit der Spannungsmessung der Batterien beschäftigt und möchte meine Erkenntnisse hier zur Diskussion stellen.

    Meiner Meinung nach funktioniert die Akku-Spannungsmessung so wie sie in der Nibobeelib implementiert ist nicht.
    Für die Messung der analogen Signale wird die AVcc als Referenzspannung benutzt, auch für die Messung der Akkuspannung. Da sich ein Spannungsteiler von 2*47K (R43,R44) am Eingang ADC4 (=VBAT) befindet, wird man immer den Wert von ca. 512 als Resultat der Akkuspannung erhalten.
    Das kommt daher, dass der ADC immer den Wert 1024 für den Vollausschlag für die Spannung an AVcc heranzieht. Woher soll also der ADC wissen, dass die Akkuspannung und damit AVcc in die Knie geht?
    Will man also die Akkuspannung messen, so braucht man eine Referenzspannung an AVcc, die konstant ist und von der Akkuspannung unabhängig ist. Dazu kann man die interne 2.56V Referenzspannung im ATMega16 benutzen.
    Ich hoffe, dass ich das bis hierher richtig wiedergegeben habe und möchte mal nachfragen, ob ihr das genauso seht.

    Nun habe ich hier mal ein kleines Programm geschrieben, das auf diese Weise zu den richtigen Ergebnissen führt. Da ich nicht in der Nibobeelib herumdoktern wollte, ist es gewissermassen ein "Workaround", um den Fehler zu korrigieren ohne die anderen ADKanäle zu stören oder zu verändern.
    Vielleicht wird ja der Fehler in einer neuen Nibobeelib behoben.
    Code:
    int Batterie(void)
    {
      unsigned char temp1,temp2;
      uint16_t ADCresult;
    
      while ((ADMUX & 0x07) != 0x04);
    
      cli();
    
      while (!(ADCSRA & (1 << ADIF)));   // wait for conversion complete
      ADCSRA |= (1 << ADIF);           	// clear ADCIF
    
      //Registerinhalte retten
      temp1 = ADCSRA;
      temp2 = ADMUX;
    
      ADMUX = (1 << REFS0) | (1 << REFS1) | ANALOG_VOLT;	//internal 2.56V reference with external capacitor
    
      //ADCSRA löschen und neu setzen
      ADCSRA = 0;
      ADCSRA |= ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0));	//ADC clock = Clock/128
      ADCSRA |= (1 << ADEN);			//Enable ADC (das aktiviert die 2.56V Referenz)
         
      //Warten bis Kondensator an ARef = 2.56V hat
      //Messung an ARef ergab 1,2msec
      delay(6);  // = ca. 5*Tau
         
      //1. ADC Wandlung starten und Ergebnis ignorieren
      ADCSRA |= (1 << ADSC);			// Start conversion
      while (!(ADCSRA & (1 << ADIF)));  // wait for conversion complete
      ADCSRA |= (1 << ADIF);           	// clear ADCIF
    
      //2. ADC Wandlung starten und Ergebnis übernehmen
      ADCSRA |= (1 << ADSC);			// Start conversion
      while (!(ADCSRA & (1 << ADIF)));  // wait for conversion complete
      ADCSRA |= (1 << ADIF);           	// clear ADCIF
    
      ADCresult = ADCL + (ADCH << 8);
          
      //Registerinhalte wiederherstellen
      ADMUX = temp2;
      ADCSRA = temp1 & ~(1 << ADSC);
    
      //Warten bis Kondensator an ARef = AVcc hat
      //Messung ergab sehr steile Flanke
      delay(2);  //nicht notwendig, nur zur Sicherheit
    
      ADCSRA |= (1 << ADSC);			// Start conversion
    
      sei();
    
      return ADCresult;
    }
    Nun bin ich mal auf eure antworten gespannt.

    Viele Grüsse
    Skroete

  2. #2
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.781
    Blog-Einträge
    8
    Hallo

    Sollte nach dem Zurückschalten auf die 5V-Referenz nicht auch noch eine Dummylesung erfolgen? Wieso funktioniert delay() bei gesperrten Interrupts? Wäre es nicht günstiger nur den ADC-Interrupt zu verhindern anstatt alle Interrupts zu sperren?

    Oh, da ist ja noch ein Klassiker: Im Datenblatt des Mega16 in der Beschreibung von ADIF (Seite 219):

    Alternatively, ADIF is cleared by writing a logical one to the flag. Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt can be disabled.
    Bedeutet ungefähr: Wenn das Flag nicht automatisch durch den Aufruf der ISR gelöscht wird, kann man es auch "von Hand" löschen. Aber man darf dabei keine "Read-Modify-Write"-Anweisung auf das ADSRA-Register anwenden. das bedeutet, das Flag sollte so gelöschte werden:

    ADCSRA = (1 << ADIF);

    Das gilt auch für manche andere Flags im Zusammenhang mit den Interrupts!

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  3. #3
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Noch dem Umschalten der Ref. Spannung muß man damit rechnen das die AD Wandlung nicht richtug geht. Das gilt vor allem für den Schritt on 5 V auf 2,5 V . Da kann es relativ lange dauern bis ein Kondensator an ARef entladen ist. Da besser nur 10 nF statt der sonst üblichen 100 nF nehmen.

  4. #4
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    20.08.2008
    Ort
    Kandel
    Alter
    29
    Beiträge
    1.220
    Es gibt noch eine andere Möglichkeit, die Akkuspannung zu messen, die ich hier im RN gelesen habe:
    Als Referenz wird VCC gewählt, und als zu messender Kanal die interne Referenzspannungsquelle von 1,1V oder 2,56V.
    Die tatsächliche Spannung errechnet sich dann durch Ref * Auflösung / Messwert, bei vollen 10 Bit Auflösung und 1,1V gewählter interner Referenzzspannung wäre dann Vbat = 1,1V * 1024 / ADC

    mfG
    Markus

  5. #5
    Neuer Benutzer Öfters hier
    Registriert seit
    05.03.2009
    Beiträge
    20
    Hallo,

    eigentlich hatte ich gehofft, dass sich jemand mal äussert, ob ihr auch der Meinung seit, dass die Messung der Akkuspannung nicht funktioniert, so wie sie in der Nibobeelib implementiert ist.

    Hier mal die Antworten auf die Fragen von radbruch.
    Sollte nach dem Zurückschalten auf die 5V-Referenz nicht auch noch eine Dummylesung erfolgen?
    Antwort:
    Das wird auch gemacht. Nach der Messung der Batteriespannung in der neuen Routine, wird die Interruptgetriebene ADC Wandlung mit einem erneuten messen der Batteriespannung wieder aufgenommen. Allerdings mit der Falschen ARef Spannung. Damit ist das Ergibnis sowieso Schrott aber es wurde eine Dummy-Messung durchgeführt.
    Deshalb warte ich in der Batterie-Routine bis der Kanal 4 (VBAT) dran ist, rette die Register, schreibe sie wieder zurück und mache genauso weiter als ob die Batterie-Routine nie aufgerufen worden wäre.

    Wieso funktioniert delay() bei gesperrten Interrupts?
    Antwort:
    Die delay(..) stützt sich auf der _delay_ms aus <util/delay.h> ab. Die arbeitet mit Zeitschleifen und braucht keinen Interrupt (siehe avr-libc-users-manual).

    22.28 <util/delay_basic.h>: Basic busy-wait delay loops
    22.28.1 Detailed Description
    #include <util/delay_basic.h>
    The functions in this header file implement simple delay loops that perform a busy waiting.
    They are typically used to facilitate short delays in the program execution.
    They are implemented as count-down loops with a well-known CPU cycle count per loop iteration. As such, no other processing can occur simultaneously. It should be kept in mind that the functions described here do not disable interrupts.
    In general, for long delays, the use of hardware timers is much preferrable, as they free the CPU, and allow for concurrent processing of other events while the timer is running. However, in particular for very short delays, the overhead of setting up a hardware timer is too much compared to the overall delay time.
    Two inline functions are provided for the actual delay algorithms.
    Functions
    • void _delay_loop_1 (uint8_t __count)
    • void _delay_loop_2 (uint16_t __count)

    Wäre es nicht günstiger nur den ADC-Interrupt zu verhindern anstatt alle Interrupts zu sperren?
    Antwort:
    Richtig


    Oh, da ist ja noch ein Klassiker: Im Datenblatt des Mega16 in der Beschreibung von ADIF (Seite 219):

    Zitat:
    Alternatively, ADIF is cleared by writing a logical one to the flag. Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt can be disabled.
    Bedeutet ungefähr: Wenn das Flag nicht automatisch durch den Aufruf der ISR gelöscht wird, kann man es auch "von Hand" löschen. Aber man darf dabei keine "Read-Modify-Write"-Anweisung auf das ADSRA-Register anwenden. das bedeutet, das Flag sollte so gelöschte werden:

    ADCSRA = (1 << ADIF);

    Das gilt auch für manche andere Flags im Zusammenhang mit den Interrupts!

    Antwort:
    Das oben gesagte bedeutet nur, dass man wissen muss, was man tut, bedeutet aber nicht, dass man es nicht tun darf.
    Ich lösche ADIF bevor ich ADCSRA in temp1 rette. Ich lösche ADIF am Ende meiner eingefügten Batteriespannungsmessung und somit einen (eventuell) pending ADC Interrupt. Eigentlich ist das aber nur zur Sicherheit, denn in der Initialisierung der Batteriespannungsmessung wird ADIE in ADCSRA bewusst nicht gesetzt. Also sollte ADIF eigentlich gar nicht kommen. Beim Zurückschreiben von temp1 in ADCSRA weiss ich also ganz genau, was ich zurückschreibe.
    Ich lösche auch ganz bewusst ADSC in ADCSRA beim zurückschreiben, damit die delay(2) auch wirklich abgearbeitet werden können, bevor ich danach mit ADSC=1 den gewohnten ADC Betrieb wieder auf nehme.

    Viele Gruesse
    Skroete

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •