mikrocontroller.net

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


Autor: Achilles (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
[...]
unsigned char ADCkanal = 0;
[...]
//initialisieren des Analog-Digital-Converters
void adcinit (void)
{
// AVcc als externe Referenzspannung nutzen, Spannungsmessung an PC0
ADMUX = (1<<REFS0) | (0<<REFS1) | ADCkanal;

// ADC Enable | ADC Start Conversion | Vorteiler 64
ADCSRA = (1<<ADEN) | (1<<ADIE) | (0x06);
}

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

Gruß,
Achilles

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: willi (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);

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

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

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

So, jetzt auszugsweise das Programm:
[...]
unsigned char Messung = 0;
uint8_t i;
uint16_t result;
[...]
void adcinit (void)
  {
  ADMUX = (1<<PC0);
  ADMUX |= (0<<REFS1) | (1<<REFS0);
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
  }

void messen(void)
  {
  ADCSRA |= (1<<ADSC);
  while ( ADCSRA & (1<<ADSC) )
  {
  ;
  }
  result = ADCW;
 
  result = 0; 
  for( i=0; i<4; i++ )
  {
    ADCSRA |= (1<<ADSC);
    while ( ADCSRA & (1<<ADSC) )
    {
    ;
    }
    result += ADCW;
  }
  ADCSRA &= ~(1<<ADEN);
 
  result /= 4;
 
  return result;
}

void main(void)
  {
  adcinit();
    for(;;)
    {
     if(Messung==1)
     {
     messen();
     Messung=0;
     }
    }
  }

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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vorne angefangen:
//initialisieren des Analog-Digital-Converters
void adcinit (void)
  {
  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.
  ADMUX = 0;
wäre richtig.
  ADMUX |= (0<<REFS1) | (1<<REFS0);
ist ok, für interne 2,56V Referenz
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
  }
ADC enablen, Teilungsfaktor 32. Ok soweit.
void messen(void)
{
...
}

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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ähh..
void adcinit (void)
  {
  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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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
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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
void messen(void)
{
[...]
  ADCSRA &= ~(1<<ADEN);
[...]
}
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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich schiebe keine Nullen durch die Gegend...

ADMUX |= (1<<REFS0);

Oliver

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
void adcinit (void)
  {
  ADMUX = (1<<PC0);
  ADMUX |= (0<<REFS1) | (1<<REFS0);
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
  }
void messung (void)
{
  {
  ADCSRA |= (1<<ADSC);
  while ( ADCSRA & (1<<ADSC) )
  {
  ;
  }
  ADCSRA &= ~(1<<ADSC);
  ergebnis = ADCW;
  ausgabe();
}

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?

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, eine geschwungene Klammer fehlt:
void messung (void)
{
  {
  ADCSRA |= (1<<ADSC);
  while ( ADCSRA & (1<<ADSC) )
  {
  ;
  }
  ADCSRA &= ~(1<<ADSC);
  ergebnis = ADCW;
  ausgabe();
  }
}
Aber daran lag es natürlich nicht!

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit
ADMUX = 0x00;

anstatt mit
ADMUX = (1<<PC0);

hat es funktioniert!

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Achilles schrieb:
> Mit
>
>
> ADMUX = 0x00;
> 
>
> anstatt mit
>
>
> ADMUX = (1<<PC0);
> 
>
> 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

Autor: Achilles (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tja, oder man schiebt Nullen durch die Gegend:
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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.