Forum: Mikrocontroller und Digitale Elektronik ADC bei Atmega32


von Christoph A. (shadowrunner93)


Angehängte Dateien:

Lesenswert?

Hi Leute!

Ich versuche schon seit geraumer Zeit den internen ADC des Atmega32 zum 
laufen zu bekommen.

Ich benutze das STK500. Die Frequenz ist bei internen 4 Mhz.

Bei diesem Code habe ich testweise die interne Referenzspannungsquelle 
ausgewählt, um zu sehen obs klappt. Aber an dem AREF Pin liegt keine 
Spannung an. Woran könnte das liegen?

Hier ist mein Code:

1
#include <avr/io.h>
2
3
#define LOOP 5
4
#define COMM_ANODE_R 1
5
#define COMM_ANODE_M 1
6
#define COMM_ANODE_L 1
7
8
uint16_t readout();
9
void sev_segm_l(uint16_t number);
10
void sev_segm_m(uint16_t number);
11
void sev_segm_r(uint16_t number);
12
13
unsigned char digits[] = { 0b00111111,    // 0
14
                           0b00000110,    // 1
15
                           0b01011011,    // 2
16
                           0b01001111,    // 3
17
                           0b01100110,    // 4
18
                           0b01101101,    // 5
19
                           0b01111101,    // 6
20
                           0b00000111,    // 7
21
                           0b01111111,    // 8
22
                           0b01101111,    // 9
23
                           0b01000000     // alles andere
24
                         };
25
26
27
28
void main()
29
{
30
31
  DDRB=0xFF; // als Ausgang
32
  DDRC=0xFF; 
33
  DDRD=0xFF; 
34
  
35
  uint16_t erg;
36
37
  erg=readout();
38
  //einmal zum Warmlaufen
39
  
40
  while(1)
41
  {
42
43
    for(int i=0;i<LOOP;i++)
44
      erg+=readout();
45
  
46
    erg/=LOOP;  //Durchschnittswert aus LOOP Durchgängen
47
48
    erg/=10;
49
50
    sev_segm_l(erg/100);
51
    // die letzte Stelle des Ergebnises
52
    sev_segm_m((erg%100)/10);
53
    // die mittlere Stelle...
54
    sev_segm_r(erg%10);
55
    // usw.
56
57
  }
58
}
59
60
uint16_t readout()
61
{
62
  
63
  uint16_t erg;
64
  
65
  ADCSRA |= (1<<ADEN); // ADC wird aktiviert
66
67
  ADCSRA |= (1<<ADPS2) | (1<<ADPS0); // Frequenzteiler von 32 (4Mhz/32=125khz)
68
  
69
  ADMUX |= (1<<REFS0) | (1<<REFS1); // Interne Referenz
70
71
  ADCSRA |= (1<<ADSC); // "Warmlaufrunde" für ADC
72
73
  while(ADCSRA & (1<<ADSC) )
74
  {
75
    //auf Abschluss der Konvertierung warten  
76
  }
77
  
78
  erg=ADCW;
79
  
80
  ADCSRA |= (0<<ADEN);//ADC deaktivieren  
81
82
  return erg;
83
}
84
85
86
void sev_segm_l(uint16_t number)
87
{
88
89
  if(COMM_ANODE_L)
90
  {
91
    if( number < 10 )
92
      PORTD = ~digits[number];
93
    else
94
      PORTD = ~digits[10];
95
  }
96
  else
97
  {
98
    if( number < 10 )
99
      PORTD = digits[number];
100
    else
101
      PORTD = digits[10];
102
  }
103
}
104
105
106
void sev_segm_m(uint16_t number)
107
{
108
  if(COMM_ANODE_M)
109
  {
110
    if( number < 10 )
111
      PORTC = ~digits[number];
112
    else
113
      PORTC = ~digits[10];
114
  }
115
  else
116
  {
117
    if( number < 10 )
118
      PORTC = digits[number];
119
    else
120
      PORTC = digits[10];
121
  }
122
}
123
124
void sev_segm_r(uint16_t number)
125
{
126
127
  if(COMM_ANODE_R)
128
  {
129
    if( number < 10 )
130
      PORTB = ~digits[number];
131
    else
132
      PORTB = ~digits[10];
133
  }
134
  else
135
  {
136
    if( number < 10 )
137
      PORTB = digits[number];
138
    else
139
      PORTB = digits[10];
140
  }
141
}

Mfg

von Tobi (Gast)


Lesenswert?

ADCSRA |= (0<<ADEN);//ADC deaktivieren

diese Zeile macht nicht, was sie soll!

Ein Bit wird so gelöscht:
  ADCSRA &= ~(1<<ADEN);//ADC deaktivieren

MfG

von Christoph A. (shadowrunner93)


Lesenswert?

Tobi schrieb:
> ADCSRA |= (0<<ADEN);//ADC deaktivieren
>
> diese Zeile macht nicht, was sie soll!
>
> Ein Bit wird so gelöscht:
>   ADCSRA &= ~(1<<ADEN);//ADC deaktivieren
>
> MfG

Sry, da hab' ich nicht nachgedacht...

Aber an dem liegts nicht...
Dann ist halt der ADC die ganze Zeit eingeschaltet ;)

Mfg

von gast (Gast)


Lesenswert?

>Aber an dem AREF Pin liegt keine
>Spannung an. Woran könnte das liegen?
das sie intern ist. versuch doch einfach mal was einzulesen und das mit 
einer messung mit multimeter zu vergleichen.

von Christoph A. (shadowrunner93)


Lesenswert?

gast schrieb:
>>Aber an dem AREF Pin liegt keine
>>Spannung an. Woran könnte das liegen?
> das sie intern ist. versuch doch einfach mal was einzulesen und das mit
> einer messung mit multimeter zu vergleichen.

Das verstehe ich nicht ganz. Wird die zu messende Spannung am AREF-Pin 
ausgegeben? Wohl eher nicht oder? ;)

Nach meinem Code gibt der ATmega32 eine Dezimale 8 aus, unabhängig davon 
was ich einlese. Habe ich im Code irgendeinen Denkfehler?

Mfg

von spess53 (Gast)


Lesenswert?

Hi

>das sie intern ist. versuch doch einfach mal was einzulesen und das mit
>einer messung mit multimeter zu vergleichen.

VREF liegt am entprechenden PIN an (wenn richtig eingestellt), und muss 
dort auch zu messen sein.

MfG Spess

von Spezi (Gast)


Lesenswert?

Hallo,

Du solltest die Initialisierung und den Wandler-Vorgang des ADC 
voneinander trennen. Es macht keinen Sinn, bei jedem Aufruf von 
"readout()" den Wandler neu zu initialisieren. Das macht man einmal beim 
Start des Programms in "main". Auch das Abschalten des ADC nach jeder 
Wandlung ist nicht sinnvoll; er bleibt an und ist so lange inaktiv, bis 
eine neue Conversion (ADEN=1) gestartet wird.
Nur wenn sich der Eingangs-Kanal ändert, wird ADMUX mit dem neuen 
Kanalwert beschrieben.

von Christoph A. (shadowrunner93)


Lesenswert?

spess53 schrieb:
> Hi
>
>>das sie intern ist. versuch doch einfach mal was einzulesen und das mit
>>einer messung mit multimeter zu vergleichen.
>
> VREF liegt am entprechenden PIN an (wenn richtig eingestellt), und muss
> dort auch zu messen sein.
>
> MfG Spess

Genau deshalb wundere ich mich ja dass an dem AREF-Pin keine Spannung 
anliegt.
Stimmen die Einstellung die ich vorgenommen habe?

Meine derzeitige Konfiguration:

An Vcc --> +5V
An GND --> GND
An AVcc -->+5V
An AGND --> GND

So müsste es doch klappen wenn ich als Referenzquelle AVCC einstelle 
oder irre ich mich?

Wie sieht es mit den Fusebits usw aus? Habe ich die richtig gesetzt? (im 
1. Beitrag habe ich Screens angehängt)

Mfg
Christoph

von Christoph A. (shadowrunner93)


Lesenswert?

Spezi schrieb:
> Hallo,
>
> Du solltest die Initialisierung und den Wandler-Vorgang des ADC
> voneinander trennen. Es macht keinen Sinn, bei jedem Aufruf von
> "readout()" den Wandler neu zu initialisieren. Das macht man einmal beim
> Start des Programms in "main". Auch das Abschalten des ADC nach jeder
> Wandlung ist nicht sinnvoll; er bleibt an und ist so lange inaktiv, bis
> eine neue Conversion (ADEN=1) gestartet wird.
> Nur wenn sich der Eingangs-Kanal ändert, wird ADMUX mit dem neuen
> Kanalwert beschrieben.

Okay, danke für den Tipp.

Aber an es müsste doch trotzdem funktionieren oder? xD

Mfg

von holger (Gast)


Lesenswert?

>So müsste es doch klappen wenn ich als Referenzquelle AVCC einstelle
>oder irre ich mich?

Wenn du AVCC einstellst kommt an AREF nichts raus.
Wozu auch?

>#define COMM_ANODE_R 1

>  if(COMM_ANODE_R)
>  {
>  }
>  else
>  {}

Schönen toten Code hast du dir da gebastelt.
if(COMM_ANODE_R) ist immer wahr. Das else kommt
nie zum Zug. Bestenfalls optimiert der Compiler
den else Zweig komplett weg.

>  while(1)
>  {
>
>    for(int i=0;i<LOOP;i++)
>      erg+=readout();

erg in der main() Loop sollte auch irgendwann
mal wieder auf 0 gesetzt werden. Sonst addierst du
da immer wieder Reste deiner Rechnung mit auf.

von Spezi (Gast)


Lesenswert?

> ADMUX |= (1<<REFS0) | (1<<REFS1); // Interne Referenz

Damit wird die interne Referenz-Spannung ausgewählt (2.56V). AVcc als 
Referenz ist eine andere Einstellung. (REFS0 = 1, REFS1 = 0)

> VREF liegt am entprechenden PIN an (wenn richtig eingestellt), und muss
> dort auch zu messen sein.

Dies gilt für Vref = AVcc und für Vref = 2.56V intern. Siehe 
Blockschaltbild des ADC im Datenblatt.

Hast Du den richtigen A/D-Eingangskanal ausgewählt, der für das STK500 
passt? Zumindest sind laut Software alle Pins von Port A auf Eingang 
geschaltet ...

Läuft der Rest des Programmes denn korrekt?

Holger wrote:
>Wenn du AVCC einstellst kommt an AREF nichts raus.
>Wozu auch?

Dann schau Dir das ADC-Blockschaltbild an, wann Aref Spannung führt (FET 
ist eingeschaltet). Dann sollte die Antwort, die ich oben nannte, klar 
sein. (Spannung liegt an Aref an, wenn REFS0 = 1 ist.) Sonst würde der 
empfohlene externe Kondensator an diesem Pin auch wenig Sinn machen ...

von Christoph A. (shadowrunner93)


Lesenswert?

holger schrieb:
>>So müsste es doch klappen wenn ich als Referenzquelle AVCC einstelle
>>oder irre ich mich?
>
> Wenn du AVCC einstellst kommt an AREF nichts raus.
> Wozu auch?

Hmm, ich glaube so ganz habe ich die Funktion von dem AREF nicht 
kapiert.

Ist das jetzt ein Ein- oder Ausgang?

Könntest du mir die Funktion des Pins bitte erklären?

holger schrieb:
>>#define COMM_ANODE_R 1
>
>>  if(COMM_ANODE_R)
>>  {
>>  }
>>  else
>>  {}
>
> Schönen toten Code hast du dir da gebastelt.
> if(COMM_ANODE_R) ist immer wahr. Das else kommt
> nie zum Zug. Bestenfalls optimiert der Compiler
> den else Zweig komplett weg.

^^

Es sei denn ich ändere das define, wenn ich Siebensegm.Anzeigen habe die 
nicht COMM-ANODE sind, verstehst du? xD
Somit muss ich es nicht in allen 3 Funktionen umändern wenn ich den Code 
vearbeiten will.

holger schrieb:
>erg in der main() Loop sollte auch irgendwann
>mal wieder auf 0 gesetzt werden. Sonst addierst du
>da immer wieder Reste deiner Rechnung mit auf.

Danke!
Ich glaube du hast damit voll ins Schwarze getroffen..
Ich hatte schon eine Vermutung dass ich irgendeinen schweren Denkfehler 
eingebaut habe...

Ausprobieren kann ich es allerdings heute nicht mehr, jeder braucht 
seinen Schlaf ;)

Also dann Gute Nacht, ich meld' mich morgen wieder obs geklappt hat!

Mfg

von Christoph A. (shadowrunner93)


Lesenswert?

Verfolgt diesen Thread noch jemand?

Mfg

von Bernd (Gast)


Lesenswert?

Denke du verfolgst diesen Thread. Wie weit bist du nun gekommen ?

von Gast (Gast)


Lesenswert?

> Also dann Gute Nacht, ich meld' mich morgen wieder obs geklappt hat!
 Du bist am Zug.

von Christoph A. (shadowrunner93)


Lesenswert?

Schande über mich, dass mit dem ADC funktioniert immer noch nicht.
Ich habe auch nicht weiter daran herumgewerkelt, ich hatte andere 
Projekte am laufen.

Hier nochmal mein neuer Code:
1
#include <avr/io.h>
2
3
//#define COMM_ANODE_R 0
4
//#define COMM_ANODE_M 0
5
//#define COMM_ANODE_L 0
6
7
void ADC_init(void);
8
uint16_t ADC_read(uint8_t kanal);
9
10
//void sev_segm_l(uint16_t number);
11
//void sev_segm_m(uint16_t number);
12
//void sev_segm_r(uint16_t number);
13
14
unsigned char digits[] = { 0b00111111,    // 0
15
                           0b00000110,    // 1
16
                           0b01011011,    // 2
17
                           0b01001111,    // 3
18
                           0b01100110,    // 4
19
                           0b01101101,    // 5
20
                           0b01111101,    // 6
21
                           0b00000111,    // 7
22
                           0b01111111,    // 8
23
                           0b01101111,    // 9
24
                           0b01000000     // alles andere
25
                         };
26
27
28
29
void main()
30
{
31
  
32
  //Ports als Ausgänge/Eingänge definieren
33
  DDRA=0x00;
34
  DDRB=0xFF; 
35
  DDRC=0xFF; 
36
  DDRD=0xFF; 
37
38
  uint16_t erg;  
39
  
40
  //ADC initalisieren
41
  ADC_init;
42
43
  while(1)
44
  {
45
    erg=ADC_read(0);
46
    //Auch wenn PORTB nur 8 Ausgänge hat
47
    //muss es trotzdem was anderes
48
    //als 0 ausgeben
49
    PORTB=erg;
50
  }  
51
}
52
53
54
55
void ADC_init(void)
56
{
57
  //Diese Funktion aktiviert den ADC
58
59
  ADCSRA |= (1<<ADEN); //ADC aktivieren und
60
61
  ADCSRA |= (1<<ADPS2) | (1<<ADPS1); //Teilungsfaktor auf 8 stellen
62
}
63
64
uint16_t ADC_read(uint8_t kanal)
65
{
66
  ADMUX=kanal; //Kanal des Multiplexers wählen:
67
68
  ADMUX |= (1<<REFS0); //AVCC als Referenz
69
70
  ADCSRA |= (1<<ADSC); //Messung starten
71
72
  while(bit_is_set(ADCSRA,ADSC)); // Auf Ergebnis warten:
73
74
  return ADCW;
75
}
76
77
78
/*
79
80
void sev_segm_l(uint16_t number)
81
{
82
83
  if(COMM_ANODE_L)
84
  {
85
    if( number < 10 )
86
      PORTD = ~digits[number];
87
    else
88
      PORTD = ~digits[10];
89
  }
90
  else
91
  {
92
    if( number < 10 )
93
      PORTD = digits[number];
94
    else
95
      PORTD = digits[10];
96
  }
97
}
98
99
100
void sev_segm_m(uint16_t number)
101
{
102
  if(COMM_ANODE_M)
103
  {
104
    if( number < 10 )
105
      PORTC = ~digits[number];
106
    else
107
      PORTC = ~digits[10];
108
  }
109
  else
110
  {
111
    if( number < 10 )
112
      PORTC = digits[number];
113
    else
114
      PORTC = digits[10];
115
  }
116
}
117
118
void sev_segm_r(uint16_t number)
119
{
120
121
  if(COMM_ANODE_R)
122
  {
123
    if( number < 10 )
124
      PORTB = ~digits[number];
125
    else
126
      PORTB = ~digits[10];
127
  }
128
  else
129
  {
130
    if( number < 10 )
131
      PORTB = digits[number];
132
    else
133
      PORTB = digits[10];
134
  }
135
}
136
137
*/

Egal was ich an den Pin0 am PORTA anlege, alle Ausgänge des PORTB 
bleiben auf Low.

Hier meine Beschaltung;

VCC=5V
GND=GND
AVCC=5V
AGND=GND
PINA0=Analoge Spannung

Doch dieses Mal liegt am AREF-Pin AVCC an. Das heißt die 
Referenzspannung müsste da sein.

Mfg

von Christoph A. (shadowrunner93)


Lesenswert?

Da ihr zu dem Code nichts zu sagen habt, gehe ich davon aus dass er in 
Ordnung ist.

Dann kann es also nur an der Umgebung liegen.

Den ATMega32 habe ich dem linken roten Slot des STK500 drinnen.

Was muss ich bei dem Setzten der FUSEBITS beachten? Gibt es welche die 
den ADC im Weg stehen könnten?

Mfg

von Andreas R. (rebirama)


Lesenswert?

Ich hab mal deinen Code kommentiert:
1
uint16_t ADC_read(uint8_t kanal)
2
{
3
  ADMUX=kanal; //Kanal des Multiplexers wählen und Ref auf Extern!
4
5
  ADMUX |= (1<<REFS0); //AVCC als Referenz
6
7
  ADCSRA |= (1<<ADSC); //Messung starten
8
9
  while(bit_is_set(ADCSRA,ADSC)); // Auf Ergebnis warten:
10
11
  return ADCW;
12
}

Dadurch schaltest du die Referenz bei jeder Messung einmal um. Das macht 
Probleme, da man nach dem Umschalten kurz (125µs oder so) warten muss.

Besser:
1
uint16_t ADC_read(uint8_t kanal)
2
{
3
  ADMUX=kanal |(1<<REFS0); //Kanal des Multiplexers wählen und Ref auf AVCC
4
5
  ADCSRA |= (1<<ADSC); //Messung starten
6
7
  while(bit_is_set(ADCSRA,ADSC)); // Auf Ergebnis warten:
8
9
  return ADCW;
10
}

Und ich bin grad unsicher, aber muss es nicht  "ADC_init();" statt 
"ADC_init;" heißen?

von Christoph A. (shadowrunner93)


Lesenswert?

Andreas R. schrieb:
> Ich hab mal deinen Code kommentiert:
>
>
1
> uint16_t ADC_read(uint8_t kanal)
2
> {
3
>   ADMUX=kanal; //Kanal des Multiplexers wählen und Ref auf Extern!
4
> 
5
>   ADMUX |= (1<<REFS0); //AVCC als Referenz
6
> 
7
>   ADCSRA |= (1<<ADSC); //Messung starten
8
> 
9
>   while(bit_is_set(ADCSRA,ADSC)); // Auf Ergebnis warten:
10
> 
11
>   return ADCW;
12
> }
13
>
>
> Dadurch schaltest du die Referenz bei jeder Messung einmal um. Das macht
> Probleme, da man nach dem Umschalten kurz (125µs oder so) warten muss.


Echt unglaublich wieviele Genies sich hier in den Foren aufhalten. (Das 
ist jetzt kein Sarkasmus^^)

Die Funktion ist jetzt so umgeändert, dass der Kanal automatisch 0 ist.

Ich glaube damit hast du mein Problem gelöst, bin mir noch nicht ganz 
sicher..
Ich melde mich wieder! (diesmal sicher xD)

von Christoph A. (shadowrunner93)


Lesenswert?

Also es sieht so aus als ob es funktionert ;)

Ich glaube die grobe Wandlung von 10bit in 8bit macht dem µC Probleme.
Dafür muss ich noch eine Lösung finden. Aber schonmal vielen Dank!!

Hier ist mein derzeitiger Code:
1
void main()
2
{
3
  
4
  //Ports als Ausgänge/Eingänge definieren
5
  DDRA=0x00;
6
  DDRB=0xFF; 
7
  DDRC=0xFF; 
8
  DDRD=0xFF; 
9
  
10
  uint8_t erg;  
11
  
12
  //ADC initalisieren
13
  ADC_init();
14
15
  while(1)
16
  {
17
    erg=ADC_read();
18
    //Auch wenn PORTB nur 8 Ausgänge hat
19
    //muss es trotzdem was anderes
20
    //als 0 ausgeben
21
    PORTB=erg;
22
  }  
23
}
24
25
26
27
void ADC_init()
28
{
29
  //Diese Funktion aktiviert den ADC
30
31
  ADCSRA |= (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);//ADC aktivieren und
32
33
  ADMUX |= (1<<REFS0);//AVCC als Referenz
34
35
}
36
37
uint16_t ADC_read()
38
{
39
40
  ADCSRA |= (1<<ADSC);//Messung starten
41
42
  while(bit_is_set(ADCSRA,ADSC));// Auf Ergebnis warten:
43
44
  return ADCW;
45
}

von Bernd (Gast)


Lesenswert?

Nur mal nen Vorschlag, weil der Thread schon so lange läuft :-)

ADC Readout gemittelt:
1
uint16_t getAdcValue (void)
2
{
3
    uint8_t AdcCount = 0;
4
    uint32_t AdcSum = 0;
5
6
    for (AdcCount = 0; AdcCount < 128; AdcCount ++)  {
7
        ADCSRA |= (1 << ADSC);            // AD Wandler starten
8
        while (ADCSRA & (1 << ADSC));     // AD Wandlung fertig ?
9
        AdcSum += ADC;                    // Mittelwert über 128 Messungen
10
    }
11
    return (uint16_t) (((((AdcSum >> 7)
12
}
Bei ADC Init, prescaler beachten:
ADC Init
1
    
2
    // enable ADC, ADC clock /128 = 144 kHz
3
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

von Bernd (Gast)


Lesenswert?

Sorry, das mit den 4 MHz hatte ich nicht gesehen also prescale Tipp 
zurück :-)

von Bernd (Gast)


Lesenswert?

Und noch was... warum werden blos immer wieder die beiden ADC Register 
getrennt betrachten... das Ergebnis beider Register steht in ADC.

von Thomas (kosmos)


Angehängte Dateien:

Lesenswert?

das Ergebniss kann aber je nach Einstellung des ADLAR Bits verschieden 
aussehen.

Keine Ahnung ob der Compiler gleich einen 16 bit Wert draus macht.

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.