Forum: Mikrocontroller und Digitale Elektronik ADC ATMEGA644 teilweise unplausible Werte


von Marco G. (grmg2010)


Lesenswert?

Moin,

ich möchte den internen ADC einen ATMEGA644 nutzen. Prinzipiel klappt 
dies auch, allerdings habe ich mitunter etwas unplausible Werte. Es 
scheint mir, als ob diese zum Teil einen recht großen Offset aufweisen.

Hier die Initialisierung:
1
uint16_t first_result = 0;
2
    
3
    DIDR0 |= (1<<ADC0D) | (1<<ADC1D) | (1<<ADC2D) | (1<<ADC3D) | (1<<ADC6D) | (1<<ADC7D);
4
5
    ADCSRA = (1<<ADEN) | (1<<ADIE); // ADC aktivieren und Interrupt einschalten
6
    
7
    ADCSRA |= ((1<<ADPS1) | (1<<ADPS2));
8
      
9
    _delay_ms(1);
10
        
11
    //warm-up des ADC
12
    for (uint8_t i = 0; i < 5; i++)
13
    {
14
      ADCSRA |= (1<<ADSC);
15
      while (ADCSRA & (1<<ADSC))
16
      {
17
        
18
      }
19
      
20
      first_result = (uint16_t) (ADCL + (ADCH<<8));
21
      _delay_ms(10);
22
      first_result = 0;
23
    }

Die Port-Register müssen doch nicht noch anderweitig gesetzt werden? 
Auch das Abschalten der Digitalstufen ist soweit ich gelesen habe eher 
optional.
Ist meine Routine zur Initialisierung des ADCs schlüssig oder habe ich 
etwas signifikantes dabei vergessen?

Währen der Programmlaufzeit wird periodisch zwischen den Kanälen 0,1,2,3 
und 6 umgeschaltet. Sollte nach jeder Umschaltung ein Dummy read 
erfolgen oder kann der abgerufenen Wert dann als nutzbar gelten?

Gruß

von ADC (Gast)


Lesenswert?

Und was für Werte misst du? Und wie sind die SD-Eingänge beschaltet?

von Marco G. (grmg2010)


Angehängte Dateien:

Lesenswert?

Ich messe den Strom mit einer OP-Schaltung mit nachgeschaltetem 
Impedanzwandler und Serienwiderstand. Direkt am Ausgang des OPs ist der 
Wert (mit dem Multimeter gemessen) wie zu erwarten bei ca. 60mV. Am ADC 
liegen allerdings 0.42V an. Sowohl mit dem Multimeter als auch dem 
internen ADC gemessen.

von Falk B. (falk)


Lesenswert?

Marco G. schrieb:
> ich möchte den internen ADC einen ATMEGA644 nutzen. Prinzipiel klappt
> dies auch, allerdings habe ich mitunter etwas unplausible Werte. Es
> scheint mir, als ob diese zum Teil einen recht großen Offset aufweisen.

Wie groß? Zahlen.
Ist AREF extern mit 100nF beschaltet?

> Hier die Initialisierung:uint16_t first_result = 0;
>
>     DIDR0 |= (1<<ADC0D) | (1<<ADC1D) | (1<<ADC2D) | (1<<ADC3D) |
> (1<<ADC6D) | (1<<ADC7D);
>     ADCSRA = (1<<ADEN) | (1<<ADIE); // ADC aktivieren und Interrupt
> einschalten

Wozu die Interrupts einschalten, wenn du den nicht nutzt? Das geht 
schief, vor allem, wenn dazu keine passende ISR besteht, dann macht dein 
AVR einen Reset.

>
>     ADCSRA |= ((1<<ADPS1) | (1<<ADPS2));
>
>     _delay_ms(1);
>
>     //warm-up des ADC
>     for (uint8_t i = 0; i < 5; i++)

Dafür braucht es keine Schleife. Nur bei der ERSTMALIGEN Aktivierung des 
ADC bzw. beim Wechsel der Referenzspannung sollte man EINE ADC Messung 
machen und das Ergebnis wegwerfen.

>     {
>       ADCSRA |= (1<<ADSC);
>       while (ADCSRA & (1<<ADSC))
>       {
>
>       }

Jaja, schön lehrbuchartige Klammern ;-)

while (ADCSRA & (1<<ADSC));

reicht auch.

>       first_result = (uint16_t) (ADCL + (ADCH<<8));

Brauchst du beim avr gcc nicht, der liefert das Ergebnis als 16 Bit Wert 
mit ADCW und kümmert sich dabei um den richtigen Zugriff auf ADCL und 
ADCH.

first_result = ADCW.

>       _delay_ms(10);
>       first_result = 0;

Wozu die Verzögerung NACH der Messung? Willst du den AVR nicht so 
hetzen?

> Die Port-Register müssen doch nicht noch anderweitig gesetzt werden?

Nein.

> Auch das Abschalten der Digitalstufen ist soweit ich gelesen habe eher
> optional.

Ja.

> Ist meine Routine zur Initialisierung des ADCs schlüssig oder habe ich
> etwas signifikantes dabei vergessen?

Mit deinen Einstellung musst du an AREF von außen eine Referenzspannung 
einspeisen. Die 100nF an dem Pin braucht es auch. Hast du beides?

> Währen der Programmlaufzeit wird periodisch zwischen den Kanälen 0,1,2,3
> und 6 umgeschaltet.

Dann tu das, mit der gescheiten Funktion. Sprich, nur den MUX 
umschalten, den Rest im Register nicht verändern. Siehe 
Bitmanipulation.

> Sollte nach jeder Umschaltung ein Dummy read
> erfolgen

Nö.

> oder kann der abgerufenen Wert dann als nutzbar gelten?

Sicher.

Schwankende ADC-Werte können folgende Ursachen haben

Der Wert schwankt WIRKLICH
Es fehlt ein kleiner Kondensator am ADC-Eingang, so 1-10nF.

von Falk B. (falk)


Lesenswert?

Marco G. schrieb:
> Ich messe den Strom mit einer OP-Schaltung mit nachgeschaltetem
> Impedanzwandler und Serienwiderstand. Direkt am Ausgang des OPs ist der
> Wert (mit dem Multimeter gemessen) wie zu erwarten bei ca. 60mV. Am ADC
> liegen allerdings 0.42V an. Sowohl mit dem Multimeter als auch dem
> internen ADC gemessen.

C12 ist ARG groß. Kann man machen, ist aber eher unsinnig. Wenn am 
OPV-Ausgang der richtige Wert anliegt, am ADC-Eingang aber 420mF, ist 
was faul. Da müßte ja durch R20 ca. I= U / R = (420-60)mV/324R = 1,1mA 
fließen. Da müßte ein 5k Pull Up gegen +5V am ADC-Eingang geschaltet 
sein. Die internen Pull-Ups sind ca. 10 mal so groß. Klingt nach einem 
Löt- oder Schaltungsfehler.

von Marco G. (grmg2010)


Lesenswert?

An AREF wird eine Spannung 2,048V eingespeist. Der 100n ist dort 
vorhanden.
Den Interrupt nutze ich zur Auswertung.

Ich hatte erst nur das Auslesen eines Wertes, hatte zur Sicherheit die 
Schleife für mehrere Werte eingebaut. Die Umschaltung der Kanäle klappt 
auch problemlos. Die Info war lediglich für die Frage nach dem 
Dummy-Read nach dem Umschaltung gedacht.

Das die Werte leicht schwanken ist ok, das erwarte ich auch in gewissen 
Umfang. Allerdings kommt mir der Offset an einem Kanal (siehe vorheriger 
Beitrag) komisch vor.

von Falk B. (falk)


Lesenswert?

Marco G. schrieb:
> An AREF wird eine Spannung 2,048V eingespeist. Der 100n ist dort
> vorhanden.
> Den Interrupt nutze ich zur Auswertung.

Den sehen wir aber nicht!

> Ich hatte erst nur das Auslesen eines Wertes, hatte zur Sicherheit die
> Schleife für mehrere Werte eingebaut. Die Umschaltung der Kanäle klappt
> auch problemlos. Die Info war lediglich für die Frage nach dem
> Dummy-Read nach dem Umschaltung gedacht.
>
> Das die Werte leicht schwanken ist ok, das erwarte ich auch in gewissen
> Umfang.

In WELCHEM!!!!

Ich habe mehrere Schaltungen mit AVRs und dessen ADC gebaut, die waren 
immer ERSTAUNLICH ruhig, da war teilweis kaum mehr als 1 LSB Rauschen 
drauf!

> Allerdings kommt mir der Offset an einem Kanal (siehe vorheriger
> Beitrag) komisch vor.

Ist er auch. Muss man suchen. Siehe Fehlersuche.

von Marco G. (grmg2010)


Lesenswert?

Einen

Falk B. schrieb:
> C12 ist ARG groß. Kann man machen, ist aber eher unsinnig. Wenn am
> OPV-Ausgang der richtige Wert anliegt, am ADC-Eingang aber 420mF, ist
> was faul. Da müßte ja durch R20 ca. I= U / R = (420-60)mV/324R = 1,1mA
> fließen. Da müßte ein 5k Pull Up gegen +5V am ADC-Eingang geschaltet
> sein. Die internen Pull-Ups sind ca. 10 mal so groß. Klingt nach einem
> Löt- oder Schaltungsfehler.

Den Kondensator kann ich testweise verkleinern. Bei der Betrachtung 
unter dem Mikroskop ist mir kein Lötfehler aufgefallen, sah soweit gut 
aus. Schaltungsfehler würde ich ausschließen, da genau dieselbe 
Beschaltung an einer andern Stelle wie erwartet funktioniert.

von Marco G. (grmg2010)


Lesenswert?

Hier die vorläufige ISR
1
ISR(ADC_vect)
2
{
3
  adc_value = ADCW;  
4
  
5
  adc_result = calculate_adc(adc_value);
6
  
7
  if (adc_modus == 0)
8
  {    
9
    Temperature = calculate_temp(adc_result);
10
    TempAvailable = 1;    
11
  }
12
  else if (adc_modus == 1)
13
  {
14
    RTCVoltageAvailable = 1;
15
    RTCVoltage = calculate_RTCVoltage(adc_result);
16
  }
17
  else if (adc_modus == 2)
18
  {
19
    CurrentAvailable = 1;
20
    Current = calculate_current(adc_result);    
21
  }
22
  else if (adc_modus == 3)
23
  {
24
    VoltageAvailable = 1;
25
    Voltage = calculate_Voltage(adc_result);
26
    
27
  }
28
  else if (adc_modus == 6)
29
  {
30
    BatVoltageAvailable = 1;
31
    BatVoltage = calculate_BatteryVoltage(adc_result);
32
  }  
33
}

Die Weiterverabeitung der Werte erfolgt dann in der Main.

Ich meinte, dass die Werte von Natur aus schwanken. Die Stromaufnahme 
die ich messe ist nicht konstant. Daher die erwartbaren Schwankungen der 
Messwerte.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Marco G. schrieb:
> Hier die vorläufige ISRISR(ADC_vect)
> {
>   adc_value = ADCW;
>
>   adc_result = calculate_adc(adc_value);
>
>   if (adc_modus == 0)
>   {
>     Temperature = calculate_temp(adc_result);
>     TempAvailable = 1;
>   }

diese vielen if() kann man sinnvollerweise durch ein switch() ersetzen. 
Das aber nur nebenbei.

>   else if (adc_modus == 1)
>   {
>     RTCVoltageAvailable = 1;
>     RTCVoltage = calculate_RTCVoltage(adc_result);
>   }
>   else if (adc_modus == 2)
>   {
>     CurrentAvailable = 1;
>     Current = calculate_current(adc_result);
>   }
>   else if (adc_modus == 3)
>   {
>     VoltageAvailable = 1;
>     Voltage = calculate_Voltage(adc_result);
>
>   }
>   else if (adc_modus == 6)
>   {
>     BatVoltageAvailable = 1;
>     BatVoltage = calculate_BatteryVoltage(adc_result);
>   }
> }
>
> Die Weiterverabeitung der Werte erfolgt dann in der Main.

Wozu dann die Umrechung in der ISR? Das kann man auch in Main machen. 
Mit der ISR gewinnt man kaum was. Eine einfache Abfrage mit Warten auf 
den ADC ist hier vollkommen ausreichend.

> Ich meinte, dass die Werte von Natur aus schwanken. Die Stromaufnahme
> die ich messe ist nicht konstant. Daher die erwartbaren Schwankungen der
> Messwerte.

Ja was denn nun? Wenn ich einen ADC auf Rauschen und Drift testen will, 
dann lege ich ein DEFINITIV KONSTANTES Signal an, und kein Wald und 
Wiesen Signal, das halt irgendwie schwankt.

von Achim H. (pluto25)


Lesenswert?

Wird in der Isr auch der Mux geändert braucht die Main sich nicht mehr 
um das Messen zu kümmern (drauf zu warten) und hat stets aktuelle Werte.
Dummymessungen werden sinnvoll/nötig wenn die Adc Eingänge hochohmig 
beschaltet sind. ( z.B. 100kOhm Ntc)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Marco G. schrieb:
> Sollte nach jeder Umschaltung ein Dummy read erfolgen
Nein. Du musst einfach nur dafür sorgen, dass deine AD-Beschaltung 
ausreichend niedeohmig ist.

Marco G. schrieb:
> Am ADC liegen allerdings 0.42V an.
> Sowohl mit dem Multimeter als auch dem internen ADC gemessen.
Dann funktioniert der ADC doch wie er soll.

> Direkt am Ausgang des OPs ...
... solltest du mal mit einem Oszi messen. Mich würde nicht wundern, 
wenn dieser GHZ-Bolide einfach nur schwingen würde. Wozu einen OP, der 
zehntausendmal schneller als nötig ist?


A. H. schrieb:
> Dummymessungen werden sinnvoll/nötig wenn die Adc Eingänge hochohmig
> beschaltet sind. ( z.B. 100kOhm Ntc)
Und genau da (relativ langsame Temperaturänderungen) kann man einfach 
einen 100nF zwischen ADSC-Pin und GND klemmen um die Impedanz zu 
reduzieren. Dann ist die Leermessung unnötig, denn dieser externe 100nF 
Kondensator lädt in der Sampling-Phase des ADC einen 
15pF-Sample-Kondensator auf. Und weil das Verhältnis 100n/15pF = 6666 
ist, bricht die Spannung am ADC-Pin nicht spürbar maximal um 1/6666 ein.

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

A. H. schrieb:
> Wird in der Isr auch der Mux geändert braucht die Main sich nicht mehr
> um das Messen zu kümmern (drauf zu warten) und hat stets aktuelle Werte.

Tut sie aber nicht.

von Falk B. (falk)


Lesenswert?

Lothar M. schrieb:
>> Direkt am Ausgang des OPs ...
> ... solltest du mal mit einem Oszi messen. Mich würde nicht wundern,
> wenn dieser GHZ-Bolide einfach nur schwingen würde. Wozu einen OP, der
> zehntausendmal schneller als nötig ist?

AUA! LTC6252, 720MHz GBP. Wenn's scheeeeee macht! ;-)

https://www.analog.com/en/products/ltc6252.html

Aber das erklärt nicht die Spannungsdifferenz. Der OPV spuckt ja 
anscheinend die richtige Spannung aus, aber am ADC-Eingang ist die 
Spannung HÖHER!

von Peter D. (peda)


Lesenswert?

Falk B. schrieb:
> aber am ADC-Eingang ist die
> Spannung HÖHER!

Könnte an dem Pin noch ne LED mit Vorwiderstand nach VCC hängen?

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Peter D. schrieb:
> Könnte an dem Pin noch ne LED mit Vorwiderstand nach VCC hängen?

Beitrag "Re: ADC ATMEGA644 teilweise unplausible Werte"

von Marco G. (grmg2010)


Lesenswert?

Vielen Dank für die kontruktiven  Vorschläge. Die Umschaltung des MUX 
soll auch noch in die ISR wandern.

Folgendes war das Problem:
1
PORTA |= (1<<PC2) | (1<<PC5) | (1<<PC6);

Das macht natürlich keinen Sinn und muss natürlich
1
PORTC |= (1<<PC2) | (1<<PC5) | (1<<PC6);

heißen. Mit dem korrigierten Eintrag ist der Offset auch nicht mehr 
vorhanden, die gemessenen Werte sind jetzt wie erwartet.

Macht es denn wie hier Sinn, die Umrechnung der Werte in der ISR 
vorzunehmen oder sollte dies lieber in die Main verlagert werden?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Marco G. schrieb:
> Macht es denn wie hier Sinn, die Umrechnung der Werte in der ISR
> vorzunehmen
Ich mache das so: in der ISR (= Unterbrechung des üblichen, normalen 
Programmablaufs) werden die Werte nur aus dem ADC-Register abgeholt und 
als Rohwert gespeichert. Später werden aus diesen gespeicherten 
Rohwerten die nötigen Ergebnisse ausgerechnet.

Denn auch ich möchte gerne, wenn ich z.B. grade beim Essen sitze oder 
einen netten Film anschaue (= aktueller Programmablauf), nicht unnötig 
lange dabei unterbrochen werden.
Ich gehe also kurz raus zum klingelnden Postboten (= Interrupt), hole 
die Rechnung (= ADC-Wert) ab, überweise aber den Rechnungsbetrag (= 
Bearbeitung des ADC-Werts) nicht sofort, sondern erst nach dem Essen 
oder dem Film.

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

Marco G. schrieb:
> Macht es denn wie hier Sinn, die Umrechnung der Werte in der ISR
> vorzunehmen oder sollte dies lieber in die Main verlagert werden?

Eine gute Strategie ist, Rechnungen zum spätest möglichen Zeitpunkt zu 
machen, z.B. vor der ersten Verwendung.

Außerdem sind float und Division auf dem AVR teuer und da der AVR keine 
Interruptlevel kann, mehrfach teuer. Andere Interrupts könnten störend 
ausgebremst werden.

von Marco G. (grmg2010)


Lesenswert?

Ok, also am besten den Rohwert in eine zugehörige variable speichern und 
dann erst in der Main verarbeiten. Dann werde ich das dahingehen ändern.

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.