ADC initialisieren - ist das so richtig?

Gast #1258631
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
Angehängte Dateien:
Gast #1258664
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.
Gast #1258732
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?!
Gast #1258756
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
Gast #1258789
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?
#1258815
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.
Gast #1258820
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
Gast #1258847
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
Gast #1277379
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?
Gast #1277382
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!
#1283302
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
Gast #1284723
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!

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