Forum: Mikrocontroller und Digitale Elektronik ADC-Wert nicht linear beim Einlesen


von Martin (Gast)


Angehängte Dateien:

Lesenswert?

Nabend,

ich habe ein Problem mit meinem ADC am Atmega8.
Das Problem ist, dass der ADC dem Poti nicht linear folgt. Um die Frage 
vorweg zu klären, das Poti ist ein linear Poti Spindeltrimmer, 
Präzisions-Poti 10k (25 Gänge).
Habe das Poti unbelastet ohmisch mit dem Multimeter durchgemessen 
zwischen einem Pin und Schleifer. Das Poti ist definitiv linear und 
verhält sich beim ohmischen durchmessen auch so.

Am ADC verhält es sich ganz anders. Ich habe hierzu ein Display an Port 
C und teilweise Port D angeschlossen. Das Display zeigt mir den 
eingelesenen ADC-Wert an. Steht das Poti nahe 0 kann ich 6-8 Gänge 
drehen und der ADC-Wert steigt nur langsam am. Die Spannung am Poti ist 
entsprechend auch sehr gering (gemessen mit dem Multimeter). Doch dann 
steigt der ADC-Wert rapide an. Eine halbe Umdrehungen weiter und der 
Wert ist bereits bei 200. Noch eine halbe Umdrehung und er ist bei 600. 
Noch eine Umdrehung weiter und ich lande dann schon am Ende bei 1020.

Verwendet wird die Spannung an AVcc, welche = VCC = 5V ist aus einem 
Festspannungsregler. Prozessortakt ist 8Mhz, Vorteiler ADC auf 64, d.h. 
Abtastfrequenz 125kHz.

Anbei auch die Schaltung dazu. Ich muss dazu sagen, die Spule habe ich 
weggelassen, stattdessen AVcc direkt an Vcc. Das ich dadurch evtl ein 
Rauschen habe ok, aber die "Sprünge" bzw. das nicht-lineare Verhalten 
sollte das aber nicht ausmachen. Alle 100nF Kondensatoren sind dran wie 
gezeichnet und sämtliche sonstigen Anschlusse (also alle *GND und *Vcc 
sind entsprechend beschaltet).

Prozessor habe ich auch schon getauscht gegen einen anderen Atmega8, 
sogar ein Atmega88 und Atmega168 veruscht. Alle reagieren gleich.

Hat jemand eine Erklärung? ADC-Auslese-Funktion ist aus dem Tutorial 
(vorherige Version).

Und hier die main.c
1
#include <avr/io.h>        // avr Header File für IO Ports
2
#include <avr/pgmspace.h>    // Markos für PGM Space
3
4
// INCLUDES für Display
5
#include <stdio.h>
6
#include <stdlib.h>
7
#include <string.h>
8
#include "main.h"
9
#include "lcd.h"
10
#include <util/delay.h>
11
12
#ifndef F_CPU
13
#define F_CPU           8000000UL                   // processor clock frequency 8Mhz
14
#endif
15
16
17
uint16_t readADCWert (uint8_t mux);      // Liest den ADC-Wert ein und gibt den Wert zurück von mikrocontroller.net
18
void init_ports(void);            // Ports initialisieren    
19
20
21
int main(void)
22
23
{
24
25
char buffer [12];
26
uint16_t ADCWert = 0;
27
28
  // Ports initialisieren  
29
  init_ports();              
30
  
31
  // Display initialisieren
32
    lcd_init();
33
  lcd_clear();
34
35
36
  while (1){
37
  
38
  ADCWert = readADCWert (0);  // ADC-Wert einlesen
39
  
40
  // ADC-Wert ausgeben
41
  lcd_setcursor(0,0);
42
  lcd_string_P(PSTR("ADC-Wert:"));
43
  utoa( ADCWert, buffer, 10);
44
  lcd_string( buffer );
45
  
46
  } 
47
} 
48
49
50
51
52
void init_ports(void) {
53
  // Port B ungenutzt
54
  DDRB = 0x00;
55
  PORTB = 0xFF; // alle Pullups ein
56
  
57
  
58
  // ADC Port als Eingang, Pullups sicher ausschalten
59
  DDRC = (1 << DDC0);
60
  PORTC &= ~(1 << PC0);
61
  
62
  return;
63
}
64
65
uint16_t readADCWert (uint8_t mux) {
66
67
  uint8_t i;                    // Schleifenzähler für die 4-fach Messung (bildet Durchschnitt)
68
  uint16_t result;                  // Speichert das Ergebnis der Funktion und gibt dieses zurück. Wert zwischen 0...1023 (10-bit)
69
 
70
  ADMUX = mux;                              // wird der Funktion übergeben, entspricht dem Kanal der ausgelesen werden soll (0=ADC0, 1ADC1, etc.)
71
  ADMUX |= (1<<REFS0);               // Spannung am Pin AVcc dient als Referenz
72
 
73
  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS2);  // ADC wird aktiviert, Vorteiler 64 wird eingestellt
74
                               
75
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
76
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
77
   
78
  ADCSRA |= ( 1 << ADSC );                   // startet eine ADC-Wandlung 
79
  while ( ADCSRA & (1<<ADSC) ) {
80
  ;                         // auf Abschluss der Konvertierung warten, das bit im ADSC Register wird nach erfolgreicher Wandlung gelöscht, Schleife somit beendet
81
  }
82
  result = ADCW;                    // ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten Wandlung nicht übernommen. (Ergebnis wird im Register ADCW gespeichert)
83
 
84
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
85
  result = 0;                     // Dummy Readout Messwert verwerfen und result zu 0 setzen für neue Messung
86
87
  for( i=0; i < 4; i++ ){      // Schleife für 4 aufeinanderfolgende Werte die in result zunächst aufaddiert werden
88
    ADCSRA |= (1<<ADSC);                   // eine Wandlung "single conversion" startet
89
    while ( ADCSRA & (1<<ADSC) ){
90
      ;                     // auf Abschluss der Konvertierung warten
91
    }
92
   result += ADCW;                    // Wandlungsergebnisse aufaddieren
93
  }
94
  ADCSRA &= ~(1<<ADEN);                     // ADC deaktivieren
95
  result /= 4;                      // Summe durch anzahl_messungen teilen = arithm. Mittelwert
96
97
  return result;                  // Funktion gibt bei Aufruf die Variable "result" zurück. Diese enthält den Mittelwert aus den ADC Messungen
98
}

von Magnus M. (magnetus) Benutzerseite


Lesenswert?

Der Code macht nicht das, was im Kommentar steht.

  // ADC Port als Eingang, Pullups sicher ausschalten
  DDRC = (1 << DDC0);
  PORTC &= ~(1 << PC0);

von Uwe (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Martin,

ja das ist so, bitte lies mal die entsprechnden Notes von Atmel dazu:

AVR120: Characterization and Calibration of the ADC on an AVR

AVR121: Enhancing ADC resolution by oversampling

von Rolf Magnus (Gast)


Lesenswert?

Martin schrieb:
> // ADC Port als Eingang, Pullups sicher ausschalten
>   DDRC = (1 << DDC0);
>   PORTC &= ~(1 << PC0);

Damit machst du den Pin zum Ausgang, nicht zum Eingang.

Und

> Abtastfrequenz 125kHz.

dürfte auch Auflösung kosten, in Anbetracht dessen, daß das Datenblatt 
über den ADC sagt: "Up to 15 kSPS at Maximum Resolution"

von Martin (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Martin schrieb:
>> // ADC Port als Eingang, Pullups sicher ausschalten
>>   DDRC = (1 << DDC0);
>>   PORTC &= ~(1 << PC0);
>
> Damit machst du den Pin zum Ausgang, nicht zum Eingang.

Oh mein Gott, natürlich. So ist es, wenn man sich darauf verlässt, was 
man in das Kommentar schreibt, dann findet man den Fehler nicht.
Habe es geändert und siehe da, ADC verhält sich linear.

Vielen Dank!

Rolf Magnus schrieb:
>> Abtastfrequenz 125kHz.
>
> dürfte auch Auflösung kosten, in Anbetracht dessen, daß das Datenblatt
> über den ADC sagt: "Up to 15 kSPS at Maximum Resolution"

Dazu hätte ich aber noch eine Frage. Im Tutorial steht nämlich 
folgendes:
"Der ADC-Takt sollte zwischen 50 und 200kHz liegen.
Der Vorteiler muss also so eingestellt werden, dass CPU-Taktfrequenz 
dividiert durch den Teilungsfaktor einen Wert im Bereich (50-200)kHz 
ergibt"

Daran habe ich mich gehalten und für meine 8Mhz eben 125kHz eingestellt, 
sprich Vorteiler 64.

von Eumel (Gast)


Lesenswert?

Martin schrieb:
> Daran habe ich mich gehalten und für meine 8Mhz eben 125kHz eingestellt,
> sprich Vorteiler 64.

Das ist die ADC Frequenz und nicht die Abtastfrequenz.

von Martin (Gast)


Lesenswert?

Danke, dann habe ich das verwechselt. 125kHz sind dann nicht die 
Abtastfrequenz, sondern die ADC Frequenz, also auch ein falsches 
Kommentar...

von Eumel (Gast)


Lesenswert?

Martin schrieb:
> also auch ein falsches
> Kommentar

wo?

von Martin (Gast)


Lesenswert?

Martin schrieb:
> Vorteiler ADC auf 64, d.h.
> Abtastfrequenz 125kHz.

In meinem Beitrag hier, s.o.

von Eumel (Gast)


Lesenswert?

Martin schrieb:
> Martin schrieb:
>> Vorteiler ADC auf 64, d.h.
>> Abtastfrequenz 125kHz.
>
> In meinem Beitrag hier, s.o.

Achso ;)

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.