Forum: Mikrocontroller und Digitale Elektronik ATmega16: ADC0 Wert schwankt obwohl konst. Spannung anliegt


von David (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

versuche zur Zeit eine Messwerterfassung mit dem ATmeag16A zu 
realisieren.
Dabei versuche ich mittels Poti (10k) die Eingangsspannung von 0 .... 5 
V zu simmulieren um später z.B. Drucksensoren anschließen zu können.
Mit drei Poti's gehe ich auf die ADC - Eingänge ADC0, ADC1, ADC2.
2 der 3 Kanäle funktionieren auch soweit ganz gut aber bei einem der 3 
treten ziemlich starke Schwankungen auf, obwohl ich am Eingang eine 
konstante Poti - Spannung anliegen habe.
Habe schon versucht einen 100nF Kondensator zwischen Analogeingang 
(ADC0) und GND zu setzen, kein Erfolg.
Auch sonst habe ich einen weiteren 100nF Kondensator an AREF und 
zwischen AVCC und GND, kein Erfolg.

Kann mir vielleicht jemand helfen bei meinem Problem?


Danke schonmal im Voraus.

Gruß David

von Wusel D. (stefanfrings_de)


Lesenswert?

Ist AVCC konstant?
Du solltest auch vermeiden, irgendwelche I/O Pins zu verändern, während 
der ADC eine Messung durchführt.
Was meinst Du mit "schwanken"? Wie schnell und wie stark schwankt es?

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


Lesenswert?

David schrieb:
> aber bei einem der 3 treten ziemlich starke Schwankungen auf
Bei welchem?

Und warum schreibst du da so einen Spaghetti-Code? Du machst dreimal 
fast das selbe, da wäre ein Schleife oder ein Zustandsautomat die 
richtige Lösung. Und wenn du jetzt sagst "Zustandsautomat? Zu 
kompliziert!" dann lass dir gesagt sein: du hast schon einen gebaut. 
Aber eben einen umständlichen:
1
    if (ADMUX & (1<<MUX0))         
2
      :
3
      ADMUX &= ~(1<<MUX0);
4
      ADMUX |= (1<<MUX1);
5
    }  
6
    else
7
    {
8
      if (ADMUX & (1<<MUX1))
9
        :
10
        ADMUX &= ~(1<<MUX0);
11
        ADMUX &= ~(1<<MUX1);
12
        ADMUX |= (1<<MUX2);
13
14
      }  
15
      else
16
      {
17
        if(ADMUX & (1<<MUX2))
18
          :
19
          ADMUX &= ~(1<<MUX2);
20
          ADMUX |= (1<<MUX0);
21
          _delay_ms(100);
22
        }
23
      }
24
    }
Das ist ein Automat, weil bei jedem Schleifendurchlauf anhand der 
"Zustandvariablen" ADMUX ausgesucht wird, welcher Kanal jetzt gewandelt 
werden soll.


BTW, das finde ich allerliebst:
1
    if (ADMUX & (1<<MUX0))  // erster wert jeder Wandlung verwerfen, damit sich ADC Multiplexer einschwingen kann
Leider hast du da offenbar das Datenblatt nicht ganz oder nicht an der 
richtigen Stelle gelesen. Das wäre ja ein total vermurkster uC, wenn man 
nach dem Umschalten des MUX jedesmal eine Wandlung wegwerfen müsste...

von Thomas E. (thomase)


Lesenswert?

David schrieb:
> Eingänge ADC0, ADC1, ADC2.
Guck ins Datenblatt. Da ist eine Tabelle drin.
Da siehst du, was du dafür einstellen musst.
Und was du dafür bekommst:
ADMUX &= ~(1<<MUX0);
ADMUX &= ~(1<<MUX1);
ADMUX |= (1<<MUX2);

Kleiner Tip: MUX2 wird erst ab ADC4 interessant.

mfg.

von David (Gast)


Angehängte Dateien:

Lesenswert?

Also das Problem mit dem Schwanken habe ich denke ich hinbekommen bzw. 
meinen Fehler erkannt: Es lag an MUX 2 , danke Thomas ;)


Mit dem Automaten verstehe ich allerdings noch nicht so ganz, wie kann 
ich denn die ADC - Kanäle anders ansteuern?

Mein Porblem was ich jetzt habe ist diese hier:

if((ADMUX & (1<<MUX0)) && (ADMUX & (1<<MUX1)))
.
.
.

Was daran ist falsch?


Sorry habe noch nicht so viel Ahnung von µC, bin noch nicht so lange 
dabei.

von Wusel D. (stefanfrings_de)


Lesenswert?

Falsch ist das nicht, nur unelegant. Weil du mehrmals fast identischen 
Code wiederholt hast. Wirklich elegant wäre, den ADC Interrupt zu 
nutzen:
1
#include <avr/interrupt.h>
2
#include <stdint.h>
3
4
// Werte vom ADC, für die vier Kanäle
5
static volatile uint16_t adc_value[4];
6
7
// Unterbrechung vom ADC
8
ISR(ADC_vect) {
9
    // Lese den ADC aus
10
    uint8_t channel = ADMUX & 7;  
11
    adc_value[channel] = ADC;
12
13
    // Schalte auf den nächsten Kanal um.
14
    if (++channel > 3) {
15
        channel=0;
16
    }
17
    ADMUX=(ADMUX & ~7) | channel;
18
19
    // Starte nächste Messung
20
    ADCSRA |= (1<<ADSC); 
21
}
22
23
void main() {
24
    // Initialisiere den ADC
25
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADIE) | (1<<ADSC); 
26
    ADMUX = REF_VCC;
27
28
    // Erlaube Unterbrechungen
29
    sei();
30
31
    uint8_t channel=0;
32
    while(1) {
33
       _delay_ms(100);
34
       // gib den Wert von adc_value[channel] hier aus
35
       ...
36
       // Zum nächsten Kanal wechseln
37
       if (++channel > 3) {
38
        channel=0;
39
       }
40
    }
41
}

von spess53 (Gast)


Lesenswert?

Hi

>Wirklich elegant wäre, den ADC Interrupt zu nutzen:

Noch eleganter wäre es mit Timergesteuertem Autotrigger.


>    if (++channel > 3) {
>        channel=0;

Er braucht aber nur Channel 0,1 und 2.

@David (Gast)

Den Prescaler würde ich auf 128 erhöhen. Damit liegst du dann im 
empfohlenen Bereich von 50...200kHz für den ADC-Takt.

MfG Spess

von Wusel D. (stefanfrings_de)


Lesenswert?

> Er braucht aber nur Channel 0,1 und 2.

Oh, hab ich übersehen. Aber das macht nichts, mein Codebeispiel ist ja 
leicht auf beliebige Anzahl von Kanälen umstellbar.

von David (Gast)


Angehängte Dateien:

Lesenswert?

Perfekt!


@Stefan Frings

Danke das hat echt geholfen.

Der Programmcode ist jetzt um einiges zusammengeschrumpft.

Danke euch allen.

von David (Gast)


Lesenswert?

Eine letzte Frage vielleicht noch:

Kann mir jemand erklären was das hier aufgeschlüsselt bedeutet?


ADMUX=(ADMUX & ~7) | channel;





Danke im Voraus.


Gruß David

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


Lesenswert?

David schrieb:
> Danke das hat echt geholfen.
DAS würde mir zu denken geben. Denn es wurde ja gar nicht dein Problem 
behoben, sondern dein Code überarbeitet...

David schrieb:
> ADMUX=(ADMUX & ~7) | channel;
Da werden zuerst 3 Bits gelöscht ("und nicht"), anschließend der 
channel gesetzt ("oder"). Siehe Bitmanipulation

von Wusel D. (stefanfrings_de)


Lesenswert?

> ADMUX=(ADMUX & ~7) | channel;

Oder in deutsch formuliert: Stelle die Kanalnummer ein und lasse dabei 
die anderen Bits in dem ADMUX Register unverändert.

von Andreas K. (hammerhead)


Lesenswert?

Stefan Frings schrieb:
> ADMUX=(ADMUX & ~7) | channel;

Wieso hab ich nicht selber dran gedacht m(
Danke dir!

Schon lustig, wie man per Zufall die Lösung zu nem Problem findet, an 
dem man vor ner Weile gegrübelt hat.

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.