Forum: Mikrocontroller und Digitale Elektronik ADC initialisieren - ist das so richtig?


von Achilles (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe eine Frage zum Initialisieren eines ADC-Kanals.

Im Anhang ist zu sehen, wie ich das angeschlossen habe.
Ich verwende ein ATmega8 und als ADC-Eingang kommt PC0 (ADC0) zum 
Einsatz:
1
[...]
2
unsigned char ADCkanal = 0;
3
[...]
4
//initialisieren des Analog-Digital-Converters
5
void adcinit (void)
6
{
7
// AVcc als externe Referenzspannung nutzen, Spannungsmessung an PC0
8
ADMUX = (1<<REFS0) | (0<<REFS1) | ADCkanal;
9
10
// ADC Enable | ADC Start Conversion | Vorteiler 64
11
ADCSRA = (1<<ADEN) | (1<<ADIE) | (0x06);
12
}

Ist diese Routine so richtig?
Der Takt beträgt 3,6864MHz, die WinAVR Version ist 20090313.

Gruß,
Achilles

von Karl H. (kbuchegg)


Lesenswert?


von willi (Gast)


Lesenswert?

>>ADMUX = (1<<REFS0) | (0<<REFS1) | ADCkanal;

wenn ich das so sehe solltest du dir erstmal bewusst machen was in 
dieser zeile eigentlich geschieht........ weil so is das mehr als nur 
absoluter mist

von Achilles (Gast)


Lesenswert?

Danke,

ich habe diese Routine in ähnlicher Form in einem anderen Posting 
bereits verwendet, da es sich um ein größeres Projekt handelt (ist kein 
Cross-Posting -> nur dieses ADC-Thema wollte ich hier getrennt 
behandeln).
Da wurde mir gerade auch berichtet, dass das komisch ist.
Es handelt sich nämlich um einen fertigen Code, den ich zum Üben nehmen 
wollte. War wohl die falsche Wahl...

Das Tutorial werde ich durchgehen müssen und das gesamte Programm wohl 
auch selber schreiben, da der fertige Code voller Fehler zu sein scheint 
und es einem Anfänger nicht gerade leicht fällt, alle Fehler zu finden 
und zu korrigieren.

von Achilles (Gast)


Lesenswert?

Also, jetzt mal der Reihe nach...

Ist überhaupt die Beschaltung richtig?

ADC Prescaler Select bestimmen:
TFmin = 18,432
TFmax = 73,728

Also kommt ein Teilungsfaktor von 32 oder 64 zum Einsatz.
Es soll aber schnell gewandelt werden, deshalb nehme ich einen 
Teilungsfaktor von 32.

Ist das dann folgend richtig?
1
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);

Als ADC-Kanal will ich den ADC0 verwenden, schreibt man das dann wie 
folgt?
1
ADMUX = (1<<PC0);

Nun will ich AVcc als Referenzspannung nehmen, also wie folgend 
geschrieben?
1
ADMUX |= (0<<REFS1) | (1<<REFS0);

Wenn ich das jetzt alles zusammenfüge, sieht die Routine dann so aus?
1
//initialisieren des Analog-Digital-Converters
2
void adcinit (void)
3
  {
4
  ADMUX = (1<<PC0);
5
  ADMUX |= (0<<REFS1) | (1<<REFS0);
6
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
7
  }

So, jetzt auszugsweise das Programm:
1
[...]
2
unsigned char Messung = 0;
3
uint8_t i;
4
uint16_t result;
5
[...]
6
void adcinit (void)
7
  {
8
  ADMUX = (1<<PC0);
9
  ADMUX |= (0<<REFS1) | (1<<REFS0);
10
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
11
  }
12
13
void messen(void)
14
  {
15
  ADCSRA |= (1<<ADSC);
16
  while ( ADCSRA & (1<<ADSC) )
17
  {
18
  ;
19
  }
20
  result = ADCW;
21
 
22
  result = 0; 
23
  for( i=0; i<4; i++ )
24
  {
25
    ADCSRA |= (1<<ADSC);
26
    while ( ADCSRA & (1<<ADSC) )
27
    {
28
    ;
29
    }
30
    result += ADCW;
31
  }
32
  ADCSRA &= ~(1<<ADEN);
33
 
34
  result /= 4;
35
 
36
  return result;
37
}
38
39
void main(void)
40
  {
41
  adcinit();
42
    for(;;)
43
    {
44
     if(Messung==1)
45
     {
46
     messen();
47
     Messung=0;
48
     }
49
    }
50
  }

Hab ich das jetzt erstmal so richtig verstanden und auch umgesetzt?
Wenn Messung = 1 wird, sollte eine ADC-Messung durchgeführt werden?!

von Oliver (Gast)


Lesenswert?

Vorne angefangen:
1
//initialisieren des Analog-Digital-Converters
2
void adcinit (void)
3
  {
4
  ADMUX = (1<<PC0);

macht nicht, was du möchtest. Zur Messung auf Kanal 0 muß ADMUX 3..0 = 
0000 gesetzt werden. PD0 ist eine Konstante zum Zugriff auf den Pin PD0 
im PORT-Register, und hat den Wert 1. Das hat mit ADC0 nichts zu tun 
(auch wenn es der selbe Pin ist). Mit deiner Zeile würdest du auf Kanal 
ADC 1 messen.
1
  ADMUX = 0;
wäre richtig.
1
  ADMUX |= (0<<REFS1) | (1<<REFS0);
ist ok, für interne 2,56V Referenz
1
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
2
  }
ADC enablen, Teilungsfaktor 32. Ok soweit.
1
void messen(void)
2
{
3
...
4
}

entspricht anscheinend dem Tutorial, und sollte daher funktionieren, 
auch wenn weder der dummy-ADC-Zyklus noch das anschließemde Auslesen des 
Registers erforderlich sind. Ausser, daß da unnütz Zeit verbraten wird, 
schadet es aber auch nicht.

Oliver

von Oliver (Gast)


Lesenswert?

Ähh..
1
void adcinit (void)
2
  {
3
  ADMUX = (1<<PC0);

macht doch, was du möchtest. Da ahbe ich micht vertan. PC0 ist 0, daher 
funktioniert das so. Also ist alles richtig.

Oliver

von Achilles (Gast)


Lesenswert?

Danke Oliver,

ich möchte aber 5V als Referenz nehmen und nicht die internen 2,56V, da 
ich bis 4,5V messen muss.

Dachte mit
1
ADMUX |= (0<<REFS1) | (1<<REFS0);
verwende ich AVcc als Referenzspannung, also 5V.

Wenn ich die interne 2,56V Spannungsreferenz nehmen möchte, muss ich 
doch
1
ADMUX |= (1<<REFS1) | (1<<REFS0);
schreiben, oder ist das ein Denkfehler meinerseits?

Der Rest von der ADC-Routine ist vom Tutorial abgekupfert, ja, richtig 
erkannt! Aber ist das auch richtig implementiert?

von Karl H. (kbuchegg)


Lesenswert?

Achilles schrieb:

> Der Rest von der ADC-Routine ist vom Tutorial abgekupfert, ja, richtig
> erkannt! Aber ist das auch richtig implementiert?

Die Tutorial Routine funktioniert.

Und ehe du dich an der vergreifst und sie umänderst, solltest du sie 
erst mal so lassen wie sie ist.

Du hast bei deiner Umänderei gleich wieder einen Fehler eingebaut. Damit 
der ADC überhaupt arbeitet, muss er enabled sein, sprich das ADEN Bit 
muss gesetzt sein. Am Ende deiner messen() Funktion disablest du den ADC 
und schaltest ihn nie wieder ein.

Im Allgemeinen sind die Tutorialsfunktionen hier ziemlich gut getestet. 
Das hängt auch damit zusammen, dass sie sofort korrigiert werden, sobald 
ein Benutzer einen Fehler darin meldet (was mitlerweilse sehr selten 
vorkommt). Wenn du also die Code-Schnipsel so übernimmst, wie sie dir im 
Tutorial angeboten werden, ist die Wahrscheinlichkeit sehr hoch, dass 
das auch funktioniert. Wenns dann nicht funktioniert, dann liegt das 
daran, dass
* du entweder den Schnipsel falsch eingebaut hast oder
* du den Schnipsel verändert hast und dabei Fehler eingebaut hast

Man ist daher immer gut beraten, Schnipsel erst mal so zu lassen, wie 
sie präsentiert werden. Und erst dann macht man kleine Veränderungen und 
beobachtet ob die Funktionalität stribt. Wenn ja, dann hat man beim 
Verändern einen Fehler eingebaut.

von Oliver (Gast)


Lesenswert?

>Dachte mitADMUX |= (0<<REFS1) | (1<<REFS0);
>ADMUX |= (1<<REFS1) | (1<<REFS0);
>verwende ich AVcc als Referenzspannung, also 5V.

Ok, tust du auch. Ich nutze (und mag) die Schreibweise (0<<REFS1) nicht, 
daher die Verwirrung, aber das ist Ansichtssache.

Oliver

von Achilles (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Am Ende deiner messen() Funktion disablest du den ADC
> und schaltest ihn nie wieder ein.

Super, danke! Im Tutorial steht das so, weil man damit Strom sparen 
kann.
Ich disable erstmal nicht das ADEN-Bit, sondern nehme einfach
1
void messen(void)
2
{
3
[...]
4
  ADCSRA &= ~(1<<ADEN);
5
[...]
6
}
raus. Den Code kann ich hinterher besser schreiben, aber du kennst es 
bestimmt selber, dass Anfänger gerne zu quick&dirty greifen und sich 
freuen, wenn überhaupt das Programm funktioniert!
Hab bitte Verständnis!

@Oliver:
Wie schreibst du das denn anders?

Gruß,
Achilles

von Oliver (Gast)


Lesenswert?

Ich schiebe keine Nullen durch die Gegend...

ADMUX |= (1<<REFS0);

Oliver

von Achilles (Gast)


Lesenswert?

So, ich habe ein neues Problem:
Da ich die Beschaltung wie in meinem ersten Posting benutze und mein 
IU-Wandler derzeit 2,5V ausgibt, sollte mein ADC eigentlich einen Wert 
von ~511 einlesen?! Macht er aber nicht:
1
void adcinit (void)
2
  {
3
  ADMUX = (1<<PC0);
4
  ADMUX |= (0<<REFS1) | (1<<REFS0);
5
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
6
  }
1
void messung (void)
2
{
3
  {
4
  ADCSRA |= (1<<ADSC);
5
  while ( ADCSRA & (1<<ADSC) )
6
  {
7
  ;
8
  }
9
  ADCSRA &= ~(1<<ADSC);
10
  ergebnis = ADCW;
11
  ausgabe();
12
}

Der Wert fängt bei ~340 an und fällt bis auf ~305 ab, wenn mehrere 
Anfragen an die Funktion messung() kurz hintereinander kommen.
Das Beispiel im Tutorial liefert dieselben Werte.

Wer den kompletten Code braucht: 
[Beitrag "Re: ATmega8 und UART => Code bringt Einsteiger zum Verzweifeln"]

Was mache ich denn nun wieder falsch?

von Achilles (Gast)


Lesenswert?

Sorry, eine geschwungene Klammer fehlt:
1
void messung (void)
2
{
3
  {
4
  ADCSRA |= (1<<ADSC);
5
  while ( ADCSRA & (1<<ADSC) )
6
  {
7
  ;
8
  }
9
  ADCSRA &= ~(1<<ADSC);
10
  ergebnis = ADCW;
11
  ausgabe();
12
  }
13
}
Aber daran lag es natürlich nicht!

von Achilles (Gast)


Lesenswert?

Mit
1
ADMUX = 0x00;

anstatt mit
1
ADMUX = (1<<PC0);

hat es funktioniert!

von Michael U. (amiga)


Lesenswert?

Hallo,

Achilles schrieb:
> Mit
>
>
1
> ADMUX = 0x00;
2
>
>
> anstatt mit
>
>
1
> ADMUX = (1<<PC0);
2
>
>
> hat es funktioniert!

was auch völlig richtig ist.
(1<<PC0) schiebt eine 1 PC0 mal, also 0 mal, nach links.
Macht dann 0x01 und damit ADC1.

Da hat sich der Oliver in seiner Korrektur vertan. ;-)

Gruß aus Berlin
Michael

von Achilles (Gast)


Lesenswert?

Tja, oder man schiebt Nullen durch die Gegend:
1
ADMUX = (0<<PC0);
lach

Aber am besten, man schreibt es direkt in hex, also mit 0x00, dann kann 
man sich eigentlich nicht mehr vertun.

Gruß nach Berlin!

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.