Hallo zusammen.
Ich habe mich mit der Messung der Betiebsspannung mit Hilfe der internen Referenzspannung von einem ATTiny85 beschäftigt. Grundlage dafür ist dieser Forenbeitrag https://www.arduinoforum.de/arduino-Thread-Messen-der-eigenen-Betriebsspannung-mit-dem-Arduino
Mein Problem ist, dass die interne Referenzspannung eben gar nicht konstant ist. Wenn ich die Referenzspannung, wie in dem verlinken Artikel beschrieben, bei einer Betriebsspannung von 5V ermittle, bekomme ich einen Referenzwert von 986mV angezeigt.
Messe ich die Betriebsspannung bei 5V mit dem unten gezeigten Programm, passt das Ergebnis. Senke ich aber die Betriebsspannung ab, verfälscht sich das Ergebnis immer mehr.
Durch ausprobieren, habe ich einen Wert von 1086mV bei 3V Betriebsspannung ermittelt. Verwende ich diesen Wert als internen Referenzwert, so passt das Ergebnis perfekt bei 3V. Erhöhe ich die Betriebsspannung auf 5V so ermittelt das Programm einen Wert von 5532mV.
Ich habe eine Messreihe erstellt, indem ich die ermittelten Werte von drei Volt aufwärts (in 500mV Schritten) in eine Tabelle eingetragen habe. In ein Diagramm umgesetzt kann man schön sehen, wie die vom Programm ermittelten Werte mit steigender Betriebsspannung immer mehr von der Realität abweichen.
Nun stellt sich mir die Frage, mache ich etwas grundsätzliches falsch? Ist das schlicht ein Gain - Error vom ADC? Hat jemand eine Idee, was da verkehrt läuft?
Das Programm:
#include <Arduino.h>
#include <util/atomic.h>
#include <SendOnlySoftwareSerial.h> // https://github.com/nickgammon/SendOnlySoftwareSerial
SendOnlySoftwareSerial soSerial(PIN_PB3); // TX = PB3
void initADC0() {
// Read 1.1V reference as input with reference operating voltage vcc
// Reference Vcc and analog input = internal reference 1.1V
// Initialise ADC with REFS[2:0] is 0 = VCC as Ref, MUX[3:0] 1100 = Vbg as Input,
ADMUX = _BV(MUX3) | _BV(MUX2);
ADCSRA = _BV(ADPS2) | _BV(ADPS1); // Samplingrate = 125kHz with 8Mhz Corecycles
ADCSRA |= _BV(ADEN); // Enable,
delay(5); // Wait until the reference has stabilized
// After activating the ADC, a "dummy readout" is recommended.
// In other words, a value is read and discarded to allow the ADC to "warm up"
while ((ADCSRA & _BV(ADSC))) { ; }
(void)ADCW; // Discard dummy readout..
}
constexpr uint16_t INTERNAL_REF {1086}; // determined per IC
constexpr uint32_t INERNAL_REFxRESOLUTION {INTERNAL_REF * 1024UL};
uint16_t measurementVCC() {
constexpr uint8_t COUNT {10};
uint16_t sum {0};
for (uint8_t i = 0; i < COUNT; ++i) {
ADCSRA |= _BV(ADSC); // Start conversion
while ((ADCSRA & _BV(ADSC))) { ; } // measure
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { sum += ADCW; }
}
sum = sum / COUNT;
soSerial.print("ADC: ");
soSerial.println(sum);
return static_cast<uint16_t>(INERNAL_REFxRESOLUTION / sum);
}
void setup() {
soSerial.begin(9600);
soSerial.println(F("Start"));
initADC0(); // Set ADC to interal reference voltage as input (Vbg)
}
void loop() {
// static char buffer[sizeof(uint16_t) + 1];
static char buffer[5];
// uint16_t val = measurementVCC();
// buffer[0] = val >> 8;
// buffer[1] = val;
// buffer[2] = 0;
// soSerial.print(buffer);
// soSerial.print("\n");
itoa(measurementVCC(), buffer, DEC);
soSerial.print(buffer);
soSerial.print("\n");
delay(2000);
}
So sieht der Schaltungsaufbau aus:
Der Microcontroller ist auf eine interne Taktfrequenz von 8Mhz eingestellt.