Batteriemessung mittels Bandgap mit atmega4808

Hi,

ich versuche gerade bei meinem stromsparenden Board sowohl Strom und Teile zu sparen und möchte daher mit der Bandgap-Method die Batteriespannung messen. Es handelt sich um handelsübliche 2 AA Batterien mit einer aktuellen Spannung von 3041mV. Ich kann nun eine RAW-ADC-Wert auslesen, jedoch liegt die Referenzspannug laut Datenblatt bei 1,1v. Wenn ich aber ADC-Werte rausbekomme und die Referenzspannung so zurückrechne, um auf meine echte Batteriespannung zu kommen, dann müsste die Referenzspannung bei 1,47v liegen. Ich habe im Datenblatt gelesen und die Referenzspannung kann leicht abweichen von den 1,1V aber doch nicht so stark oder? Was sagt ihr dazu? Ich würde gerne wissen ob ich etwas falsch gemacht habe. Mein Code sieht so aus:

    uint16_t measureMillivolts()
    {
        // AC0 aktivieren
        AC0.CTRLA = AC_ENABLE_bm;

        // 1,1-V-Referenz aktivieren
        VREF.CTRLA = (VREF.CTRLA & ~VREF_AC0REFSEL_gm) | VREF_AC0REFSEL_1V1_gc;
        VREF.CTRLB |= VREF_AC0REFEN_bm;


        AC0.DACREF = BATTERY_DACREF_CODE;

        delay(2); // einschwingen lassen

        ADC0.CTRLA = ADC_ENABLE_bm;
        ADC0.CTRLB = ADC_SAMPNUM_ACC8_gc;
        ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV16_gc | ADC_REFSEL_VDDREF_gc;
        ADC0.CTRLD = ADC_INITDLY_DLY64_gc | 15;
        ADC0.SAMPCTRL = 31; 
        ADC0.MUXPOS = ADC_MUXPOS_DACREF_gc;

        delayMicroseconds(200);

        // Auf Ergebnis warten
        ADC0.INTFLAGS = ADC_RESRDY_bm;
        ADC0.COMMAND = ADC_STCONV_bm;
        while (!(ADC0.INTFLAGS & ADC_RESRDY_bm))
        {
        }

        uint16_t result = ADC0.RES / 8; // Mittelwert aus 8 Samples
#if WITH_DEBUG == 1
        Serial.print("ADC Rohwert=");
        Serial.println(result);
#endif

        ADC0.CTRLA &= ~ADC_ENABLE_bm;
        VREF.CTRLB &= ~VREF_AC0REFEN_bm;
        AC0.CTRLA &= ~AC_ENABLE_bm;

        if (result == 0)
            return 0;

        // VDD = DACREF_mV × 1024 / ADC-Ergebnis
        return (uint16_t)((BATTERY_DACREF_MV * 1024UL) / result);
    }

Man sieht auch dass ich in den Rohwert Ausgabe. Der liegt bei mir leider immer zu hoch. Ich habe auch mal unterhalb des Rohwertes einfach mal für verschiedene DACREF-Werte die Rohwerte ausgegeben, um zu schauen, ob ich überhaupt etwas plausibles aus dem ADC bekomme. Die Ausgabe ist folgende:

ADC=476
DACREF=0 -> ADC=1
DACREF=64 -> ADC=120
DACREF=128 -> ADC=239
DACREF=192 -> ADC=359
DACREF=255 -> ADC=476

Kann mir jemand sagen, wieso ich am ADC immer den falschen Wert auslese? Habe ich etwas falsch gemacht oder muss man mit der Bandgap-Methode den Wert immer kalibrieren, weil die Referenzspannung von Haus aus immer so weit weg liegt. Ich habe aber keine sehr hohen Ansprüche bzgl. der Genauigkeit bei der Messung. Im Datenblatt steht nämlich, dass die Referenzspannung von MCU zu MCU schwanken kann, der Wert dann aber an sich stabil ist.

Vielen Dank für eure Hilfe

Ja!
+- 10% sind da drin.

Kenne deinen konkreten µC nicht.
Aber bei den alten stellt man auf Vcc als Refrerenz und misst dann die Bandgapspannung.

Hi,

Ich verstehe aber ich bin bei ca 30% unterschied und das finde ich etwas viel. Deswegen meine Frage.

Grüße

Hallo,

ich bin der Meinung, dass geht mit dem Atmega4808 & Co nicht.
Bei den alten µC wie ATmega328P kann man die Bandgap-Ref als Eingang an dem ADC konfigurieren. Bei den neueren µC gibt es das nicht. Man kann nur die Referenzspannung konfigurieren. Also den max. messbaren Spannungsbereich.

Vergleiche einmal die Manuals zwischen ATmega328P & Atmega4808.
Beim ATmega328P müsste man noch eine eigene Messreihe aufnehmen für einen Abgleich der dann nur für diesen µC gilt. Ob man damit zufrieden ist muss man selbst feststellen.

Würdest du sagen ich soll lieber von der Methode wegkommen und stattdessen einen hochohmigen spannungsteiler am ADC direkt nutzen. Dann wären meine Verlustleistung geringe und die Messung genauer?

Hallo,

wenn mir niemand einen undokumentierten Trick zeigt wie man doch die Bandgap Ref messen kann, dann musst du sogar von der Methode weg, es gibt ja keine. :wink:
Die interne Ref. ist relativ einfach aufgebaut und reagiert etwas auf Ub. Müsstest du eine Messreihe aufnehmen. Vergleiche das mit einer Z-Diode und Vorwiderstand. Messe Uz und verändere Ue. Die Stabilität kann für dich ausreichen, muss aber nicht. Kommt auf deine Anforderung an. Ansonsten wird eine bessere externe Referenzspannung + hochohmiger Spannungsteiler benötigt. Wieviel Strom benötigt die externe Referenzspannung? Müsstest du dir einmal durchrechnen.

Ist das vielleicht die fehlende Information, um die Referenzspannung einem ADC Eingang zuzuweisen:

Der Screenshot stammt aus dieser Quelle

https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega4808-09-DataSheet-DS40002173C.pdf

Kann mich täuschen...

Hallo,

ich sehe keine Auswahl einer Bandgap Referenz oder VDD wie bei der tinyAVR2 Serie auf einen MUX Eingang zulegen.
Was du machen könntest, eine niedrige VREF auswählen, Kapitel 18, sicher unter minimaler Versorgungsspannung, und mittels Spannungsteiler mit dem ADC deine VDD messen.
Wie genau das bleibt wenn sich VDD ändert müsstest du ausmessen. Notfalls für dein µC Exemplar spezifisch vermessen und eine Kennlinie hinterlegen.

Habe noch im Manual der AVRxEA und AVRxDB Serie geschaut, da gibt es die Auswahl des Bits VDDDIV10 im MUXPOS Register wie bei der tinyAVR2 Serie.

Nochwas. Falls du für Messzwecke oder für die Kennlinie etc. die eingestellte interne VREF Spannung messen möchtest, dann kannst du das am VREF Pin tun.

Bsp. Sketch
constexpr uint8_t VREF_0V55 {0};
constexpr uint8_t VREF_1V1 {VREF_ADC0REFSEL0_bm};
constexpr uint8_t VREF_2V5 {VREF_ADC0REFSEL1_bm};
constexpr uint8_t VREF_4V3 {VREF_ADC0REFSEL1_bm | VREF_ADC0REFSEL0_bm};
constexpr uint8_t VREF_1V5 {VREF_ADC0REFSEL2_bm};

void setup()
{  
  VREF.CTRLB = VREF_ADC0REFEN_bm;
  ADC0.CTRLC = ADC_REFSEL_VREFA_gc;
  
  VREF.CTRLA = VREF_0V55;
  delay(2000);  
  VREF.CTRLA = VREF_1V1;
  delay(2000);  
  VREF.CTRLA = VREF_1V5;
  delay(2000); 
  VREF.CTRLA = VREF_2V5;
  delay(2000);  
  VREF.CTRLA = VREF_4V3;
}

void loop()
{  }

oder verwendest analogReference()

Du kannst die Vref vom AC0 am MUX einstellen. Mir ist nur nicht klar ob das vor oder nach dem Spannungsteiler vom AC0 abgegriffen wird.

Hallo,

die Aufgabe ist bzw. war, die eigene Ub vom Controller unabhängig von allen anderen Einflüssen zu messen. Das kann man mit genannten Controllern mittels Bandgap-Ref direkt messen. Bei der megaAVR0 Serie besteht diese Möglichkeit nicht. Letzterer hat auch eine Auswahl an VREFs, wie alle anderen auch, aber nicht die eigene Bandgap direkt zu messen.

Zur Frage VREF und Spannungsteiler. VREF für einen ADC ist wie der maximale bzw. manuell eingestellte Messbereich eines Multimeters. Die zu messende Spannung an einem MUX Eingang darf VREF nicht überschreiten.

Der Gedanke mit niedriger VREF (bspw. 2.5V oder 1.5V oder 1.1V) war, dass man eine einigermaßen relativ stabile unabhängige VREF zu VDD hat. Natürlich wird sich diese je nach VDD mit verändern. Stabiler wie eine Z-Diode wird es nicht sein. Jedoch besser als nichts. Irgendwelche 10% Toleranzen kommen noch dazu. Deswegen Spannungsteiler vor den ADC Eingang damit VREF nicht überschritten wird. Dann muss man alles für diesen einen Controller ausmessen und eine Kennlinie hinterlegen. Das kann man noch Temperaturabhängig aufnehmen. Muss man wissen ob das alles den Aufwand wert ist.

Um die Illusion zu nehmen. Auch mittels direkter Bandgapmessung muss man mit Toleranzen leben und Controllerspezifisch eine Kennlinie aufnehmen. Es fällt nur die äußere Beschaltung weg und man hat einen Einfluss weniger. Nur abgleichen muss man seine Werte dennoch, wenn man genau messen möchte. Ob es Sinn macht eine präzisere externe Vref + Spannungsteiler zu verwenden muss jeder für sich entscheiden. Egal wie, man muss sich in allen Fällen einlesen und tüfteln.

@Doc_Arduino Kannst du mir einmal erklären was MUXPOS = 0X1C für Auswirkungen hat?

Hallo,

vorweg, ich habe damit noch nichts gemacht. Ich wurde erstmal stutzig, weil ein megaAVR0 keinen DAC hat und habe erstmal im AVRxDB Manual gelesen und verglichen. Ich denke es verstanden zu haben. Der Begriff DACREF ist wahrscheinlich komisch gewählt oder er wurde von anderen Serien mit DAC 1:1 übernommen und erscheint hier ungünstig. Behaupte ich jetzt einmal. Egal.

DACREF hängt hier mit dem AC (Analog Comparator) zusammen. Man kann damit für den AC einen Schwellwert einstellen, dass ist das DACREF Register. Diesen eingestellten Wert kann man als Eingang an den ADC legen und einlesen. Mit dem jetzigen Blick scheint das keinen praktischen Sinn zu ergeben, weil man den Wert bzw. alle Registereinstellungen schon vorher kennt. Für irgendwas wird das schon gut sein, auch wenn ich es noch nicht sehe. Man hat einen DAC aber keinen analogen Ausgang herausgeführt. Im Grunde dient DACREF nur für den AC. Vielleicht liest man den analogen Wert mittels ADC zu Vergleichszwecken ein.
Mehr kann ich dazu nicht sagen.