ADC bei Atmega32

OP #1427671
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
Angehängte Dateien:
OP #1427763
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
Gast #1427773
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
Gast #1427820
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.
OP #1427831
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
OP #1427833
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
Gast #1427850
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.
Gast #1427864
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 ...
OP #1427865
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
OP #1450701
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
#1453369
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?
OP #1453552
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)
OP #1453586
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
}
Gast #1453671
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);

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren