Forum: Mikrocontroller und Digitale Elektronik Seltsames Verhalten vom ADC des ATMega32


von Timmo H. (masterfx)


Lesenswert?

Ich habe folgendes Problem. Ich wollte über den ADC einen 
Temperatursensor auslesen. Mein ATMega habe ich so beschaltet, dass ich 
über einen Spannungsteiler entweder VCC oder VCC/2 an AREF anliegen habe 
(per Jumper einstellbar). Warum ich das gemacht habe ist erstmal 
unerheblich.
An Kanal 7 des ADC habe ich ebenfalls einen Spannungsteiler worüber ich 
feststellen kann ob die Referenzspannung VCC oder VCC/2 ist. Jenachdem 
wie ich das eingestellt habe Berechne ich dann eben die Temperatur.

Nun zum Phänomen. Die Funktion zum auslesen der Temp. sieht so aus:
1
int get_temp(){
2
  int value;
3
  
4
  if(read_adc(7) < 500){  //REF = 5V
5
    value = read_adc(6);
6
    value -= 265;    // 0°C
7
    return (value*30)/5;
8
  }
9
  else{  //REF = 2,5V
10
    value = read_adc(6);
11
    value -= 536;    // 0°C
12
    return (value*190)/64;
13
  }
14
}
Für 21,7°C wird also 217 zurückgegeben damit ich eine Dezimalstelle 
behalte.
Wenn ich nun die Referenzspannung auf 5V stelle gibt er mir z.B. 20,3°C 
zurück. Stelle ich die Spannung auf 2,5V (also VCC/2) bekomme ich 
24,1°C.
Gut habe ich gedacht, ist bestimmt die Ungenauigkeit vom ADC oder 
Spannungsteiler, aber als ich dann die Funktion mit einem Delay versehen 
habe gehts perfekt:
1
int get_temp(){
2
  int value;
3
  
4
  if(read_adc(7) < 500){  //REF = 5V
5
    value = read_adc(6);
6
    value -= 265;    // 0°C
7
    return (value*30)/5;
8
  }  
9
  else{  //REF = 2,5V
10
    _delay_ms(15);
11
    _delay_ms(15);
12
    _delay_ms(15);
13
    _delay_ms(15);
14
    value = read_adc(6);
15
    value -= 536;    // 0°C
16
    return (value*190)/64;
17
  }
18
}
Das witzige ist, dass es nur bei VCC/2 auftritt nicht aber bei AREF=VCC. 
Meine read_adc-Funktion sieht so aus
1
unsigned int read_adc(unsigned char channel)
2
{
3
  unsigned char i;
4
  unsigned int result = 0;         //Initialisieren wichtig, da lokale Variablen
5
                               //nicht automatisch initialisiert werden und
6
                               //zufällige Werte haben. Sonst kann Quatsch rauskommen
7
  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);    // Teiler = 128
8
 
9
  ADMUX = channel;                      // Kanal waehlen
10
  ADMUX |= (0<<REFS0);      // externe Referenz nutzen 
11
 
12
13
  ADCSRA |= (1<<ADSC);              // Dummy-ADC-Wandlung 
14
  while ( ADCSRA & (1<<ADSC) );     // auf Abschluss der Konvertierung warten 
15
16
 
17
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
18
  for(i=0;i<4;i++)
19
  {
20
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
21
    while ( ADCSRA & (1<<ADSC) );   // auf Abschluss der Konvertierung warten
22
  
23
    result += ADCW;      // Wandlungsergebnisse aufaddieren
24
  }
25
  ADCSRA &= ~(1<<ADEN);  // ADC deaktivieren (2)
26
 
27
  result /= 4;           // Summe durch vier teilen = arithm. Mittelwert
28
 
29
  return result;
30
}
Kann sich das einer erklären?

von Gabriel W. (gagosoft)


Lesenswert?

probier mal den ADC nicht bei jeder Wandlung zu aktivieren und 
deaktivieren. Einfach ADEN eingeschaltet lassen.

von Timmo H. (masterfx)


Lesenswert?

Ok, kann ich mal machen, aber wieso verhält es sich dann unterschiedlich 
bei 5V und 2,5V? Der ADC dürfte davon ja gar nichts merken. Zudem warte 
ich ja auch immer brav bis er fertig ist. An Aref liegt dann auch 
definitiv VCC/2 an.
Oder ist er mit nem Teiler von 128 zu langsam? Wohl kaum oder?

Achja hab gerade nur einmal geenabled und das ADC "aus" rausgeschmissen. 
Nun sind die Werte noch schlimmer (noch höher). Bei 5V alles in Butter

von Gabriel W. (gagosoft)


Lesenswert?

sehr seltsam....
Hab bisher noch keine Probleme mit der Referenzspg gehabt, mir hat 
bisher nur der Multiplexer Probleme bereitet. Also die DummyMessung 
jedenfalls drinnen lassen. Ich initialisiere den ADC vor der Mailoop und 
dreh ihn in den meisten Applikationen nicht ab. Zum Abfragen verwend' 
ich dann dieses Macro:
1
#define adc_convert(channel)\
2
  ADMUX = (channel & 0x0F);\
3
  ADCSRA |= _BV(ADSC);\
4
  while(ADCSRA & _BV(ADSC));\
5
    ;\
6
  ADCSRA |= _BV(ADSC);\
7
  while(ADCSRA & _BV(ADSC));\
8
    ;
Teiler verwende ich nicht.

von Stefan W. (wswbln)


Lesenswert?

Timmo H. wrote:
>   ADMUX = channel;          // Kanal waehlen
>   ADMUX |= (0<<REFS0);      // externe Referenz nutzen

Mit Nullen verodern bringt nicht so furchtbar viel - Du wolltest 
wahrscheinlich eher sowas schreiben:

   ADMUX = channel & ~(1<<REFS0);  // Kanalwahl und externe Referenz

Nur mal so am Rande...


Nach dem Umschalten der Kanäle muss man etwas warten, damit sich die 
Eingangsschaltung des ADCs auf den neuen Wert einschwingen kann.

Dies gilt speziell, wenn man (bei den neueren Megas) den internen PGA 
nutzt und/oder differenzielle Messungen macht. Aus dem Datenblatt:
"Special care should be taken when changing differential channels. Once 
a differential channel has been selected, the gain stage may take as 
much as 125 µs to stabilize to the new value."

von Timmo H. (masterfx)


Lesenswert?

Also ich habe das bei jeder Messung ADC an/aus wieder reingemacht und 
das delay auch. Zusätzlich habe ich den Teiler auf 4 gestellt, doch nun 
liegen die Werte bei 5V auch zu hoch (eine einzelne Messung hingegen 
gibt gute Werte aus). Es scheint also an den schnell aufeinander 
folgenden Messungen zweier Kanäle zu liegen:
1
int get_temp(){
2
  int value;
3
  
4
  if(read_adc(7) < 500){  //REF = 5V
5
    value = read_adc(6);
6
    value -= 265;    // 0°C
7
    return (value*30)/5;
8
  }
9
  else{  //REF = 2,5V
10
    value = read_adc(6);
11
    value -= 536;    // 0°C
12
    return (value*190)/64;
13
  }
14
}
Also erst Channel 7 und gleich darauf 6. Mit Delay wie gesagt kein 
Problem.

von Spess53 (Gast)


Lesenswert?

Hi

Nach einer Kanalumschaltung ist eine Dummymessung sinnvoll. Also einmal 
messen, Wert verwerfen und richtige Messung starten.

MfG Spess

von Timmo H. (masterfx)


Lesenswert?

>Mit Nullen verodern bringt nicht so furchtbar viel - Du wolltest
>wahrscheinlich eher sowas schreiben:
...
Nee, ich hatte da vorher was anderes stehen. Das es nichts bringt weiß 
ich. Ist aber schneller ne 1 gegen eine 0 zu tauschen...

>Nach einer Kanalumschaltung ist eine Dummymessung sinnvoll. Also einmal
>messen, Wert verwerfen und richtige Messung starten.
Ja das mache ich doch jedes mal! Darum habe ich doch extra die 
Kommentare dahinter
1
ADCSRA |= (1<<ADSC);              // Dummy-ADC-Wandlung 
2
  while ( ADCSRA & (1<<ADSC) );     // auf Abschluss der Konvertierung warten 
3
4
 
5
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
6
  for(i=0;i<4;i++)
7
  {
8
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
9
    while ( ADCSRA & (1<<ADSC) );   // auf Abschluss der Konvertierung warten
10
  
11
    result += ADCW;      // Wandlungsergebnisse aufaddieren
12
  }

von Dieter W. (dds5)


Lesenswert?

> Mein ATMega habe ich so beschaltet, dass ich über einen
> Spannungsteiler entweder VCC oder VCC/2 an AREF anliegen habe.

Wie hochohmig ist denn der Spannungsteiler?
Und wie sieht es mit einen Kondensator von 100nF von Aref nach GND aus?

von Timmo H. (masterfx)


Lesenswert?

>Wie hochohmig ist denn der Spannungsteiler?
Besteht aus zwei 10 kOhm Widerständen.

>Und wie sieht es mit einen Kondensator von 100nF von Aref nach GND aus?
Bei externer Referenz scheint der C auf VCC zu gehen. Zumindest haben 
die das beim RN-Control auch gemacht und ich habe mir gedacht, dass die 
schon wissen was sie tun (siehe C18 unten links)
http://www.roboternetz.de/wissen/images/c/c6/Rncontrol1.4schaltplan.gif

von Timmo H. (masterfx)


Lesenswert?

Mmh, kann es irgendwie am S&H liegen? Das beim Umschalten des Muxers der 
Kondensator nicht so schnell entladen ist? Im Datenblatt konnte ich 
nichts entsprechendes finden und wenn mal auf FreeRun stellt gehts doch 
auch oder?
Wäre cool wenn jemand das bei sich mal testen könnte. Also erst einen 
Kanal auslesen und direkt danach einen anderen und gucken ob die Werte 
identisch mit denen sind, die man bekommt wenn man ihn "getrennt" 
abfragt.

von Hagen R. (hagen)


Lesenswert?

Beim Umschalten der AREF können Wartezeiten entstehen da der Kondensator 
an AREF erstmal auf die gewählte Spannung umgeladen werden muß. Versuche 
mal die AREF umzuschalten und gleich danach par µs Wartezeit einzulegen. 
Dann sampelst du aber dieses mal ohne Wartezeiten.
Oder du entfernst mal zu Testzwecken den Kondensator an AREF 
vollständig.

Gruß Hagen

von Timmo H. (masterfx)


Lesenswert?

So gesehen schalte ich AREF ja nie um. Das ding bleibt immer auf "0". 
Die 2,5V liegen ja schon zig Minuten an AREF an. Von daher kann ich es 
mir nicht vorstellen.
Das mit dem C-Entfernen wäre mal ne Idee.
Aber ich habe es ja auch nur zu Testzwecken auf meinem ATMega32. Später 
mache ich ne neue Platine mit einem ATMega48 bzw. ATMega88. Da werde ich 
den C von AREF auf GND legen, ich verstehe auch nicht ganz warum die das 
beim RN-Control nicht so gemacht haben. Ich habs damals einfach ohne 
überlegen nachgebaut.

von Timmo H. (masterfx)


Lesenswert?

So ich habe jetzt den Kondensator auf GND gesetzt anstatt auf VCC 
gesetzt. Auf jeden Fall sind die Unterschiede nicht mehr so extrem. Aber 
wenn ich bei AREF 5V einstelle messe ich einen Wert von 299. Stelle ich 
ihn auf 2,5V messe ich 608. Also 2% mehr. Ist das normal? Laut 
Multimeter macht der Spannungsteiler ziemlich genau VCC/2.
Vorher lagen die Werte (mit delay) ja viel näher aneinander. Wars also 
beim RN-Control doch richtig, AREF mit nem 100nF auf VCC zu setzen?

von Spess53 (Gast)


Lesenswert?

Hi

>wenn ich bei AREF 5V einstelle messe ich einen Wert von 299. Stelle ich
>ihn auf 2,5V messe ich 608. Also 2% mehr.

Da hat wohl jemand nicht so richtig in Mathe aufgepasst!

MfG Spess

von Timmo H. (masterfx)


Lesenswert?

Findst?
(5V/1024)*299 = 1,46V
(2,5V/1024)*608 = 1,484V

0,029V sind 2% von 1,46V. Daher habe ich fast 2% mehr.
Wer hatte jetzt in Mathe nicht aufgepasst?

von Spess53 (Gast)


Lesenswert?

Hi

Dann hättest du deine Rechnung gleich beifügen müssen. Deine Zahlen 
waren 299 und 608. Und da lassen sich nun wirklich keine 2% erkennen.
Wenn du wirklich eine genaue Messung machen willst, dann sind 
Betriebsspannung und Spannungsteiler nicht unbedingt die geeignete Wahl 
für die Referenzspannung. Genauer sind auf jeden Fall die interne(n) 
Referenzspannungen des AVR oder eine externe Referenzspannungsquelle.

MfG Spess

von Stefan (Gast)


Lesenswert?

Ich nehme mal an, dass Deine 2,5V Referenz zu hochohmig ist. Wenn die 
Referenz vor oder während der Messung einbricht, dann hat das einen zu 
hohen (!) Meßwert zur Folge.

Dass Du mit dem Multimeter den "richtigen" Wert misst, muss nichts 
bedeuten, ev. bricht die Spannung nur vor oder während der ADC-Messung 
geringfügig ein.

Ändere zum Testen mal den Spannungsteiler von 10k/10k auf 1k/1k.

Im Datenblatt ist übrigens ein RREF (Reference Input Resistance) von 
typisch 32kOhm angegeben. Was immer dieser Wert genau aussagen soll: 
anscheinend kannst Du denADC-Ref-Eingang als nicht beliebig hochohmig 
annehmen.


Viele Grüße, Stefan

von Timmo H. (masterfx)


Lesenswert?

Das es sich nicht um eine Spannung bei 608 handeln kann ist ja wohl 
logisch. Naja Mitdenken ist wohl nicht deine Stärke...Warum sollte ich 
hier einfachste Prozentrechnung vorrechnen? Weißt nicht wie das geht?
Es geht hier nunmal um ADC...

>Betriebsspannung und Spannungsteiler nicht unbedingt die geeignete Wahl
>für die Referenzspannung. Genauer sind auf jeden Fall die interne(n)

Dann habe ich das Problem, dass meine Berechnung von der 
Batteriespannung abhängt wenn ich eine feste Referenz nehme. Wenn ich 
die tatsächliche Betriebsspannung als Referenz nehme wird die Temperatur 
immer gleich bestimmt, da sich glücklicher Weise nicht die 
Spannungsverhältnisse ändern.
Da alles am Ende mit Batterien laufen soll wird sich die Spannung im 
Laufe mehrerer Monate schon erheblich ändern.

von Stefan (Gast)


Lesenswert?

>Dann hättest du deine Rechnung gleich beifügen müssen. Deine Zahlen
>waren 299 und 608. Und da lassen sich nun wirklich keine 2% erkennen.
>Wenn du wirklich eine genaue Messung machen willst, dann sind
>Betriebsspannung und Spannungsteiler nicht unbedingt die geeignete Wahl
>für die Referenzspannung. Genauer sind auf jeden Fall die interne(n)
>Referenzspannungen des AVR oder eine externe Referenzspannungsquelle.

Für eine Temperaturmessung ist eine exakte Referenzspannung überflüssig, 
da nur die Differenzen des Spannungsteilers (Thermistor/Festwiderstand) 
gemessen werden, die Referenzspannung kürzt sich in der Rechnung heraus.

Wichtig ist nur, dass der Temperatur-Spannungsteiler aus derselben 
Spannungsquelle gespeist wird wie die Referenz des ADC.


Stefan

von Timmo H. (masterfx)


Lesenswert?

>Wichtig ist nur, dass der Temperatur-Spannungsteiler aus derselben
>Spannungsquelle gespeist wird wie die Referenz des ADC.
Eben und genau das ist ja der Fall. Wenigstens einer versteht mich :-D

von Stefan (Gast)


Lesenswert?

Wenn Du das Ganze per Batterie betreiben willst, dann würde ich von dem 
Spannungsteiler an der Referenz komplett absehen. Zu hochohmig bereitet 
Dir mehr Messfehler als Du damit gewinnst, zu niederohmig braucht zuviel 
Strom.

Besser:
Messe die Temperatur mehrmals und bilde den Mittelwert.

Viele Grüße, Stefan

von Timmo H. (masterfx)


Lesenswert?

>Wenn Du das Ganze per Batterie betreiben willst, dann würde ich von dem
>Spannungsteiler an der Referenz komplett absehen. Zu hochohmig bereitet
>Dir mehr Messfehler als Du damit gewinnst, zu niederohmig braucht zuviel
>Strom.
Problem ist, das ich dann an Genauigkeit verliere (Spannung zwischen 1,2 
und 1,9V => -20°C-70°C). Wenn ich nen OPV vorschalte habe ich wieder 
einen Verbraucher mehr.

>Messe die Temperatur mehrmals und bilde den Mittelwert.
Ich messe ja schon 4 mal in meiner read_adc (siehe erster Beitrag)

von Stefan (Gast)


Lesenswert?

Oh sorry, Deine Mittelwertbildung habe ich übersehen (war ja auch ganz 
unten im Posting ...).

Probier mal, den Mittelwert aus wesentlich mehr Messwerten zu bilden.
Die anschliessende Division durch die Anzahl der Messwerte ist übrigens 
nicht unbedingt notwendig - wenn Du 64 mal misst, kannst Du die 
Ergebnis-Addition auch als 16-Bit-Messwert interpretieren (zugegeben, 
von einem sehr schlechten 16-Bit-Wandler).

Um die Genauigkeit zu steigern, halte ich die REF-Halbierung für 
ungeeignet. Ev. ist es sinnvoller, den Festwiderstand im Spss-Teiler an 
den interessierenden Temperaturbereich anzupassen: Wenn Dich der Bereich 
-20 bis -70 Grad interessiert, dann lege den Spgs.Teiler dafür aus, 
dafür bist du dann bei hohen Temperaturen nicht so genau.
Das dürfte mathematisch auf dasselbe herauslaufen wie die 
REF-Halbierung.


Ggf. sind auch 2 Thermistoren eine Alternative - einer für niedrige und 
einer für hohe Temperaturen - Nachteil: doppelter Abgleichaufwand.

Viele Grüße, Stefan

von Timmo H. (masterfx)


Lesenswert?

Eigentlich war das gar nicht das Problem. Am Anfang als der C noch gegen 
VCC geschaltet war hatte ich nur ungenauigkeiten wenn ich bei AREF=2,5V 
zwei verschiedene Kanäle schnell hintereinander auslese. Mit Pause waren 
die Werte von AREF=5 und AREF=2,5 praktisch identisch. Dann habe ich mal 
den C von AREF nach GND geschaltet und nun habe ich einen Offset drin.

Ich muss das eh noch alles neu machen, da ich jetzt nur auf meinem Board 
mit dem ATMega32 Teste, später wirds ein ATMega48 oder wenn 4KB nicht 
reichen ein 88er. Dennoch fand ich das Verhalten des ADC doch sehr 
seltsam.
Ich werde mir deine Tipps auf jeden Fall mal zu Herzen nehmen.
Wahrscheinlich muss ich eh ein OPV verwenden, da ich noch nen Ducksensor 
habe und ich ja nur den wichtigen Spannungsbereich (für 950 hPa-1150 
hPa) messen will. Da muss ich mir wohl nen Subtrahierer basteln.
Ein weiteres Problem ist, das die Spannung des Ducksensors auch von der 
VCC abhängig ist (MPX4115A). Daher bräuchte ich da wieder eine Konstante 
Referenz, ggf. hol ich mir doch nen LM336 dazu. Das werde ich dann noch 
sehen.

Der Ablauf meiner Wetterstation wollte ich so machen, dass ich so wenig 
Strom wie möglich verbrauche. Ich habe an den Timer dafür einen 
Uhrenquarz angeschlossen der die Echtzeituhr steuert. Jede Sekunde wird 
ein Interrupt ausgelöst und weckt den AVR aus dem Sleep Modus und 
aktualisiert die Zeit. Alle 5 Minuten werden die Sensoren abgefragt und 
jede Stunde werden über das RFM12 die Daten an die Innenstation 
übertragen und auf einer SD-Karte gespeichert. Bin ja gepannt ob das 
alles so passt. Den Temperatursensor tausche ich ggf. auch noch gegen 
einen LM335, dann spare ich mir den Kram mit der externen 
Referenzspannung.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.