ADC bei Atmega 32

#1369990
Lesenswert?

Hallo Leute!

Ich bin neu hier und habe natürlich eine Frage:

Ich versuche zurzeit einen Temperatursensor zu bauen.
Die Temperatur habe ich schon in Form von Spannung (10mV / °Kelvin).

Nun will ich diese in Form von Grad auf 3 Siebensegmentanzeigen 
ausgeben. (falls die Temperatur dreistellig ist).

Dies ist das erste mal dass ich mit einem ADC arbeite, deswegen 
funktioniert es natürlich nicht gleich..

Hier der Code:
1
#include <avr/io.h>
2

3
#define LOOP 5
4
#define COMM_ANODE_R 0
5
#define COMM_ANODE_M 0
6
#define COMM_ANODE_L 0
7

8
void ADC_init(void);
9
void test_readout(void);
10
double measurement(void); 
11
void sev_segm_l(int number);
12
void sev_segm_m(int number);
13
void sev_segm_r(int number);
14

15
void main()
16
{
17

18
  DDRB=0xFF; // als Ausgang
19
  DDRC=0xFF; 
20
  DDRD=0xFF; 
21

22
  double erg;
23

24

25
  ADC_init();  
26
  test_readout();
27
  
28
  while(1)
29
  {
30
    erg=measurement();
31
    
32
    erg*=19,1;    //4,87V / 255 = 19,1 mV
33
    erg*=10;
34

35
    erg/=10;
36
    
37
    sev_segm_l((int)(erg/100));
38
    // die letzte Stelle des Ergebnises
39
    sev_segm_m((int)(erg/10-(erg/100)*10));
40
    // die mittlere Stelle...
41
    sev_segm_r((int)(erg-(erg/10)*10));
42
    // usw.
43
  }
44

45
}
46

47
void ADC_init(void)
48
{
49
  /*  
50

51
  ADEN=0;   aktiviert ADC (Analog-Digital-Converter)
52

53
  ADSC=0;    setzt auf (nicht)freilaufenden Betrieb
54
           Der ADC misst (nicht)durchgehend
55

56
  ADATE=1;   Für freilaufenden Modus
57
  
58
  ADIF=0;    Interrupt-Flag aus
59

60
  ADIE=0;   Kein Interrupt Flag
61

62
  ADPS0...ADPS2 : Teilungsfaktor 
63
          CPU_Clk/Fakt. = >40kHz & <150 kHz
64

65
  */
66

67
  ADCSRA=0b01000101;
68
  
69
  /*
70

71
  REFS0..REFS1 Referenzspannungsmodus
72

73
  ADLAR Darstellung des Ergebnisses
74

75
  MUX Pinnummer des eingehenden A/D Ports
76

77
  */
78

79
  ADMUX=0b00000000;
80
}
81

82
void test_readout(void)
83
{
84
  
85
  double dummy_erg;
86
  
87

88
  ADCSRA |= (1<<ADSC); // "Warmlaufrunde" für ADC
89

90
  while(ADCSRA & (1<<ADSC) )
91
  {
92
    //auf Abschluss der Konvertierung warten  
93
  }
94
  
95
  dummy_erg=ADCW; //ADCW muss einmal gelesen werden 
96
          //sonst wird das Ergebnis der
97
          //nächsten Wandlung nicht übernommen
98
  
99
}
100

101
double measurement(void)
102
{
103

104
  double erg=0;
105
  
106

107
  for(int i=0;i<LOOP;i++)    // zur Genauigkeit wird der
108
  {              // Mittelwert von LOOP-Messungen
109
                // verwendet      
110
    ADCSRA |= (1<<ADSC);  // Eine Wandlung      
111
    
112
    while(  ADCSRA & (1<<ADSC) )
113
    {
114
      ;//auf Abschluss der Konvertierung warten  
115
    }    
116
      
117
    erg += ADCW;
118
  }
119
  
120
  erg  /= LOOP;
121
  
122
  return erg;
123

124
}
125

126
void sev_segm_l(int number)
127
{
128
  if(COMM_ANODE_L)
129
  {
130
    switch(number)
131
    {
132
      case 0:
133

134
      //wobei PORT1 = a usw.  
135
      PORTB=~(0b00111111);
136
      break;
137

138
      case 1:
139

140
      PORTB=~(0b00000110);
141
      break;
142

143
      case 2:
144

145
      PORTB=~(0b01011011);
146
      break;
147

148
      case 3:
149

150
      PORTB=~(0b01001111);
151
      break;
152

153
      case 4:
154

155
      PORTB=~(0b01100110);
156
      break;
157

158
      case 5:
159

160
      PORTB=~(0b01101101);
161
      break;
162

163
      case 6:
164

165
      PORTB=~(0b01111101);
166
      break;
167

168
      case 7:
169

170
      PORTB=~(0b00000111);
171
      break;
172

173
      case 8:
174

175
      PORTB=~(0b01111111);
176
      break;
177

178
      case 9:
179

180
      PORTB=~(0b01101111);
181
      break;
182

183
      default:
184

185
      PORTB=0b01000000;
186
      break;
187
    }
188
  }
189

190
  else
191
  {
192
    switch(number)
193
    {
194
      case 0:
195

196
      //wobei PORT1 = a usw.  
197
      PORTB=0b00111111;
198
      break;
199

200
      case 1:
201

202
      PORTB=0b00000110;
203
      break;
204

205
      case 2:
206

207
      PORTB=0b01011011;
208
      break;
209

210
      case 3:
211

212
      PORTB=0b01001111;
213
      break;
214

215
      case 4:
216

217
      PORTB=0b01100110;
218
      break;
219

220
      case 5:
221

222
      PORTB=0b01101101;
223
      break;
224

225
      case 6:
226

227
      PORTB=0b01111101;
228
      break;
229

230
      case 7:
231

232
      PORTB=0b00000111;
233
      break;
234

235
      case 8:
236

237
      PORTB=0b01111111;
238
      break;
239

240
      case 9:
241

242
      PORTB=0b01101111;
243
      break;
244

245
      default:
246

247
      PORTB=0b01000000;
248
      break;
249
    }
250
  }
251
}
252

253
void sev_segm_m(int number)
254
{
255
  if(COMM_ANODE_M)
256
  {
257
    switch(number)
258
    {
259
      case 0:
260

261
      //wobei PORT1 = a usw.  
262
      PORTC=~(0b00111111);
263
      break;
264

265
      case 1:
266

267
      PORTC=~(0b00000110);
268
      break;
269

270
      case 2:
271

272
      PORTC=~(0b01011011);
273
      break;
274
USW...


Die Siebensegmentanzeigen geben nur Nullen aus.

Die analoge Spannung habe ich auf den PORT A0 angelegt. Ist das bei 
diesem Code richtig?

Mfg
Christoph
#1370035
Lesenswert?

Also in diner ADC_init funktion schaltest du den ADC nicht ein sondern 
startest ihn blos (du setzt enable auf 0 aber start auf 1). es müsste 
eigentlich genau umgekehrt sein:

ADCSRA=0b10000101;

und nicht ADCSRA=0b01000101;


probier das mal ... mir fallen in dem code zwar noch andre sachen auf 
aber die sollten zumindest die konvertierung nicht verhindern.
#1370050
Lesenswert?

Gerald D. schrieb:
> Also in diner ADC_init funktion schaltest du den ADC nicht ein sondern
> startest ihn blos (du setzt enable auf 0 aber start auf 1). es müsste
> eigentlich genau umgekehrt sein:
>
> ADCSRA=0b10000101;
>
> und nicht ADCSRA=0b01000101;
>
>
> probier das mal ... mir fallen in dem code zwar noch andre sachen auf
> aber die sollten zumindest die konvertierung nicht verhindern.

Und ändere deine Syntax

ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);

ist doch leichter zu lesen als

ADCSRA=0b10000101
#1370059
Lesenswert?

beispielsweise verwendest du den datentyp double für das ergebnis. ich 
meine es ist ein atmega32 ... mit limitiertem speicher und befehlssatz. 
eine double operation benötigt unmengen an rechenleistung ... wennste 
stattdessen einen int16 hernimmst und die werte schön aufskalierst 
kommst auch auf das selbe ergebnis ohne rießen platzbedarf und 
performanceverlust.

weiters ...

erg*=19,1;

wieso kompiliert dieser ausdruck ? sind commazahlen nicht eigentlich mit 
punkt festgelegt?

oder die zeilen

erg*=10;
erg/=10;

nacheinander wirken ein wenig naja überflüssig, weis nicht was du damit 
erreichen willst, da es sich ja um einen double handelt.

aber das sind nur eben kleinigkeiten die hier nicht ins gewicht fallen 
aber bei projekten die mehr logik erfordern dir den speicher aussaugen 
könnten :)
#1370060
Lesenswert?

Karl heinz Buchegger schrieb:

>
> Und ändere deine Syntax
>
> ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);
>
> ist doch leichter zu lesen als
>
> ADCSRA=0b10000101

Okay, mach ich ;)

So, folgendes:

Die erste Siebensegmentanzeige zeigt 0 an.
Die zweite - (also den default-Wert bei der switch-case Anweisung)
Die dritte : PortC gibt bei Pin 4 einen Wechselspannung mit dem RMS-Wert 
von 0,6V aus. Der Rest ist wie bei der ersten Siebensegmentanzeige.

Nur zur Info: Die angelegte Wechselspannung beträgt ungefähr 2,9V.

Liefert das STK500 bei AVCC und AGND die nötige Spannung automatisch?

Mfg
Christoph
#1370061
Lesenswert?

Christoph Anlauf schrieb:
> Okay, probier ich!
>
> Schonmal Danke für die Hilfe ;)
>
> Welche Dinge fallen dir denn noch auf?

Oh. Da gibt es noch jede Menge.
Zb. Schon mal was von Arrays gehört?
1
unsigned char digits[] = { 0b00111111,    // 0
2
                           0b00000110,    // 1
3
                           0b01011011,    // 2
4
                           0b01001111,    // 3
5
                           0b01100110,    // 4
6
                           0b01101101,    // 5
7
                           0b01111101,    // 6
8
                           0b00000111,    // 7
9
                           0b01111111,    // 8
10
                           0b01101111,    // 9
11
                           0b01000000     // alles andere
12
                         };
13

14
void sev_segm_l(unsigned char number)
15
{
16

17
  if(COMM_ANODE_L)
18
  {
19
    if( number < 10 )
20
      PORTB = ~digits[number];
21
    else
22
      PORTB = ~digits[10];
23
  }
24
  else
25
  {
26
    if( number < 10 )
27
      PORTB = digits[number];
28
    else
29
      PORTB = digits[10];
30
  }
31
}
32

33
void sev_segm_m(unsigned char number)
34
{
35

36
  if(COMM_ANODE_M)
37
  {
38
    if( number < 10 )
39
      PORTA = ~digits[number];
40
    else
41
      PORTA = ~digits[10];
42
  }
43
  else
44
  {
45
    if( number < 10 )
46
      PORTA = digits[number];
47
    else
48
      PORTA = digits[10];
49
  }
50
}
51

52
....


Ist doch viel kürzer :-)

Die richtigen Datentypen benutzen. int ist kein Allheilmittel für alles.
Gast #1370075
Lesenswert?

>Soweit ich weis wird beim stk500 avcc auf die versorgungsspannung also
>5V gesetzt. außer man setzt den jumper um und stellt was eigenes ein.

Sowas kann man auch im AVRStudio nachgucken.
Die Spannung ist in einem bestimmten Bereich frei einstellbar; 
grundsätzlich aber kleiner als VCC.
#1370077
Lesenswert?

Zuerst eine Ausbesserung meinerseits :

 "Die angelegte Wechselspannung beträgt ungefähr 2,9V."

Hierbei meinte ich analoge Spannung^^.

 Gerald D. schrieb:

>
>
> erg*=10;
> erg/=10;
>
>

Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang 
gezeigt ;)

 Karl heinz Buchegger schrieb:


>Ist doch viel kürzer :-)
>
>Die richtigen Datentypen benutzen. int ist kein Allheilmittel für alles.

Natürlich kenne ich Arrays!
Ja, das ist ja jetzt nur der Prototyp Code. ;)

Mfg
Christoph
#1370086
Lesenswert?

Gerald D. schrieb:
>>>
>>>
>>> erg*=10;
>>> erg/=10;
>>>
>>>
>>
>> Das ist Absicht, wegen der Übersicht. So wird mein Gedankenvorgang
>> gezeigt ;)
>
> hihi ;) ... hast in deinen gedanken an eine andere schreibweise für noop
> instruktionen gedacht ? ;)

Na klar doch ;)

Auch wenn ihr keine Noobs seit, habt ihr den Code dadurch wahrscheinlich 
trotzdem schneller kapiert^^. (Wenn ihr über den Zusammenhang überhaupt 
eine Gedanken verliert)
Gast #1370095
Lesenswert?

>Die Taktfrequenz ist bei 4 Mhz.
>Somit stimmt der Teiler hoffentlich ;)
Wie hast du das eingestellt?
Das STK500 kann maximal 3,686MHz von sich aus erzeugen.

Der AREF-Jumper auf dem STK500 ist gesetzt.
Damit sollte zumindest die interne Referenz angeschlossen sein.
Wie hoch die Spannung ist, kannst du im AVRStudio auslesen und 
einstellen.
#1370100
Lesenswert?

(1) erg=measurement();
(2) erg*=19,1;    //4,87V / 255 = 19,1 mV
(3) erg*=10;
(4) erg/=10;


Ahlso:

nach dem schritt 1 sollte erg einen wert von 0-1024 haben (0-2^10) da 
der adc eine 10 Bit genauigkeit hat.

bei schritt 2 multiplizierst du dann erg mit 19.1 ... aber nimmst 
gleiczeitig an, nur eine adc auflösung von 8 bit zu haben da du durch 
255 dividierst. das wäre mal der erste wiederspruch

schritte 3 und 4 wären dann mal überflüssig

danach gibst du erg auf der 7 segmentanzeige aus. wo du hergehst und 
völlig wirr herummultiplizierst und dividierst. also ich kann aus der 
ausgabe auch nicht wirklich was schließen.
#1370102
Lesenswert?

STK500-Besitzer schrieb:
>>Die Taktfrequenz ist bei 4 Mhz.
>>Somit stimmt der Teiler hoffentlich ;)
> Wie hast du das eingestellt?
> Das STK500 kann maximal 3,686MHz von sich aus erzeugen.
>
> Der AREF-Jumper auf dem STK500 ist gesetzt.
> Damit sollte zumindest die interne Referenz angeschlossen sein.
> Wie hoch die Spannung ist, kannst du im AVRStudio auslesen und
> einstellen.

Es heißt im STK500 zwar 4 Mhz int. Clk, aber sind tatsächlich nur 3,6 
Mhz.
#1370109
Lesenswert?

Gerald D. schrieb:
> (1) erg=measurement();
> (2) erg*=19,1;    //4,87V / 255 = 19,1 mV
> (3) erg*=10;
> (4) erg/=10;
>
>
> Ahlso:
>
> nach dem schritt 1 sollte erg einen wert von 0-1024 haben (0-2^10) da
> der adc eine 10 Bit genauigkeit hat.
>
> bei schritt 2 multiplizierst du dann erg mit 19.1 ... aber nimmst
> gleiczeitig an, nur eine adc auflösung von 8 bit zu haben da du durch
> 255 dividierst. das wäre mal der erste wiederspruch
>
> schritte 3 und 4 wären dann mal überflüssig
>
> danach gibst du erg auf der 7 segmentanzeige aus. wo du hergehst und
> völlig wirr herummultiplizierst und dividierst. also ich kann aus der
> ausgabe auch nicht wirklich was schließen.

So stimmt es aber, keine Ahnung was ich mir vorher dabei gedacht habe..

erg*=4,7;    //4,87V / 1023 = 4,7 mV
erg/=10;    //Ergebnis in °Kelvin
erg-=273.15*10          //Ergebnis in °Celsius

Nachtrag:
Dass mit der Rechnerei im Funktionsaufruf war nur ein Versuch folgendes 
hinzubekommen:

Ich möchte einfach nur eine 3-stellige Zahl auf 3 Anzeigen aufteilen.

Also z.b die Zahl ist 450.

1.Anzeige: 4
2. 5
3. 0

Aber nichts sagen, ich bin gerade am Überlegen ;-)

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