Forum: Mikrocontroller und Digitale Elektronik ATMega32 ADC0 zeigt immer max Wert.


von Andreas S. (crsle)


Lesenswert?

Hallo,

Ich möchte mittels LM35 die Temp messen und habe den LM35 am ADC0. 
Dieser liefert eine Spannung von ca. 2,31 V.

Leider zeigt das LCD immer 1023 an, egal ob das Kabel dran ist oder 
nicht. DIe Spannung steht eigentlich auf interne Ref, aber selbst wenn 
ich sie extern schalte und dort +5V dran habe passiert nix.
Das komische ist, dass es funktioniert hat, ich das ganze aber eine 
Weile hab ruhen lassen und jetzt geht nix mehr.

Hier die zwei Routinen, die ich probiert habe. Beide werden ausgewertet 
und zeigen 1023 an.

1. Routine

uint16_t ReadChannel(uint8_t mux)
{
  uint8_t i;
  uint16_t result;

  ADMUX = mux;                      // Kanal waehlen
  ADMUX |= (0<<REFS1) | (0<<REFS0); // interne Referenzspannung nutzen

  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);    // Frequenzvorteiler
                               // setzen auf 8 (1) und ADC aktivieren 
(1)

  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man 
liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu 
lassen" */
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung
  while ( ADCSRA & (1<<ADSC) ) {
     ;     // auf Abschluss der Konvertierung warten
  }
  result = ADCW;  // ADCW muss einmal gelesen werden,
                  // sonst wird Ergebnis der nächsten Wandlung
                  // nicht übernommen.

  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden 
Wandlungen */
  result = 0;
  for( i=0; i<4; i++ )
  {
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
    while ( ADCSRA & (1<<ADSC) ) {
      ;   // auf Abschluss der Konvertierung warten
    }
    result += ADCW;        // Wandlungsergebnisse aufaddieren
  }
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)

  result /= 4;                     // Summe durch vier teilen = arithm. 
Mittelwert

  return result;
}


2. Routine
int MESSWERT (int kanal){

    int i=512,j=512;
    long int analogwert=0, analogwert1=0, analogwert2=0 ;

  while(j){

    while(i){
      ADCSRA=0x80;     // ADC eingeschaltet, kein Prescale
      ADMUX=kanal;        // ADC Ref auf Avcc, PC0 gewaehlt, normale 
Formatierung
      ADCSRA |=_BV(ADSC);   // single conversion mode ein
      while (ADCSRA & (1<<ADSC)) {;}  // auf Abschluss der Konvertierung 
warten
      analogwert+=ADCW;
      i--;
    }

    analogwert1 = analogwert/512;
    analogwert2 += analogwert1;
    j--;
  }

  analogwert=(analogwert2/512);

  return (analogwert);
}

von Justus S. (jussa)


Lesenswert?

Andreas Schneider wrote:

> nicht. DIe Spannung steht eigentlich auf interne Ref, aber selbst wenn

dann hast du ein anderes Datenblatt als ich

>   ADMUX |= (0<<REFS1) | (0<<REFS0); // interne Referenzspannung nutzen

was soll diese Zeile deiner Meinung nach machen?

von Paul Baumann (Gast)


Lesenswert?

Ich verstehe nichts von "C" aber m.E. bedeuten die Bits REFS0 und REFS1
auf Null, daß AREF verwendet wird und die interne Referenzspannung
abgeschaltet wird. (Datenblatt von 11/04)

Vielleicht hilft Dir das.
MfG Paul

von spess53 (Gast)


Lesenswert?

Hi

>Ich verstehe nichts von "C" aber m.E. bedeuten die Bits REFS0 und REFS1
>auf Null, daß AREF verwendet wird und die interne Referenzspannung
>abgeschaltet wird. (Datenblatt von 11/04)

Ich auch nicht. Aber, da das eine ODER-Verknüpfung ist gilt das nur, 
wenn vorher schon 0 drin stand. Also sinnlos.

MfG Spess

von Johannes M. (johnny-m)


Lesenswert?

Es gibt auch so was wie for-Schleifen. while ist nicht alles auf der 
Welt. Und es gibt hier im Forum eine ganz tolle Funktion, mit der man 
C-Code schön formatiert darstellen kann, so dass man ihn lesen kann, 
ohne dabei Augenkrebs zu bekommen. Bitte benutze diese Funktion!

Und ja: Eine beliebige Zahl mit 0 verODERt ergibt wieder die beliebige 
Zahl, so dass man die Operation auch weglassen kann. Schau Dir mal den 
Artikel zum Thema Bitmanipulation an.

Ein Funktionsname in Großbuchstaben ist auch keine gute Idee. Gewöhne 
Dir besser gleich eindeutige Schreibweisen an: Makros in 
GROSSBUCHSTABEN, Variablen und Funktionen in kleinbuchstaben. Dann weiß 
man immer sofort, was was ist.

von Andreas S. (crsle)


Lesenswert?

Ehrlich gesagt keine Ahnung was die Zeile macht, ich hab den Code aus 
einem Beispiel übernommen und hatte ja wie gesagt auch funktioniert.


den Formatier-flag kannte ich nicht aber hier nochmals formatiert.

1. Routine
1
uint16_t ReadChannel(uint8_t mux)
2
{
3
  uint8_t i;
4
  uint16_t result;
5
6
  ADMUX = mux;                      // Kanal waehlen
7
  ADMUX |= (0<<REFS1) | (0<<REFS0); // interne Referenzspannung nutzen
8
9
  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);    // Frequenzvorteiler
10
                               // setzen auf 8 (1) und ADC aktivieren
11
(1)
12
13
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man
14
liest
15
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu
16
lassen" */
17
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung
18
  while ( ADCSRA & (1<<ADSC) ) {
19
     ;     // auf Abschluss der Konvertierung warten
20
  }
21
  result = ADCW;  // ADCW muss einmal gelesen werden,
22
                  // sonst wird Ergebnis der nächsten Wandlung
23
                  // nicht übernommen.
24
25
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden
26
Wandlungen */
27
  result = 0;
28
  for( i=0; i<4; i++ )
29
  {
30
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
31
    while ( ADCSRA & (1<<ADSC) ) {
32
      ;   // auf Abschluss der Konvertierung warten
33
    }
34
    result += ADCW;        // Wandlungsergebnisse aufaddieren
35
  }
36
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
37
38
  result /= 4;                     // Summe durch vier teilen = arithm.
39
Mittelwert
40
41
  return result;
42
}

2. Routine
1
int MESSWERT (int kanal){
2
3
    int i=512,j=512;
4
    long int analogwert=0, analogwert1=0, analogwert2=0 ;
5
6
  while(j){
7
8
    while(i){
9
      ADCSRA=0x80;     // ADC eingeschaltet, kein Prescale
10
      ADMUX=kanal;        // ADC Ref auf Avcc, PC0 gewaehlt, normale
11
Formatierung
12
      ADCSRA |=_BV(ADSC);   // single conversion mode ein
13
      while (ADCSRA & (1<<ADSC)) {;}  // auf Abschluss der Konvertierung
14
warten
15
      analogwert+=ADCW;
16
      i--;
17
    }
18
19
    analogwert1 = analogwert/512;
20
    analogwert2 += analogwert1;
21
    j--;
22
  }
23
24
  analogwert=(analogwert2/512);
25
26
  return (analogwert);
27
}

von Johannes M. (johnny-m)


Lesenswert?

Andreas Schneider wrote:
> Ehrlich gesagt keine Ahnung was die Zeile macht, ich hab den Code aus
> einem Beispiel übernommen und hatte ja wie gesagt auch funktioniert.
Glückssache... Und auch toll: Einfach Code irgendwo her kopieren, ohne 
ihn auch nur ansatzweise zu verstehen...

> den Formatier-flag kannte ich nicht aber hier nochmals formatiert.
und mit den selben "Fehlern" drin...

von Johannes M. (johnny-m)


Lesenswert?

Andreas Schneider wrote:
>   return (analogwert);
BTW: return ist keine Funktion. Die Klammern gehören da nicht hin 
(auch wenn es nicht direkt falsch ist...)

von Johannes M. (johnny-m)


Lesenswert?

Abgesehen davon gehe ich davon aus, dass der eigentliche Fehler in dem 
Teil des Programms liegt, den Du nicht zeigst. Es fehlt z.B. die 
Port-Initialisierung, und mein Gefühl sagt mir, dass da vielleicht ein 
Pull-Up aktiviert sein könnte...

von Jochen (Gast)


Lesenswert?

Hallo,

das hat bei mir funktioniert mit der gleiche Routine, aber in der 
Routine steckt ein Fehler.
Du addierst 5 X den Meßwert und dann result  /= 4 da muß /= 5 stehen.

Gruß
Jochen

von Johannes M. (johnny-m)


Lesenswert?

Jochen wrote:
> Du addierst 5 X den Meßwert und dann result  /= 4 da muß /= 5 stehen.
Wo addiert er fünfmal den Messwert? Da steht
>  for( i=0; i<4; i++ )
Die Schleife wird viermal durchlaufen.

von Johannes M. (johnny-m)


Lesenswert?

Übrigens: In dem zweiten "Beispiel" werden 512*512 = ca. 262000 
Wandlungen pro Durchlauf durchgeführt. Das dauert bei 15 kSPS schon mehr 
als 17 Sekunden, bis das erste Ergebnis da ist...

von Andreas S. (crsle)


Lesenswert?

@Johannes M. ; Wow du schreibst soviel aber sagst so wenig zur Sache. 
Ich habe eigentlich nicht nach einer Lehrstunde in C-Syntax gefragt, 
sondern eine Lösung des Problems erhofft. Trotzdem vielen Dank, deine 
Tipps sind echt hilfreich.

Das erste Beispiel ist übrigens das GCC Tutorial, deshalb gehe ich davon 
aus, dass es genau so sein muss. Ich werde den Fehler mal im Rest 
suchen.

von Andreas S. (crsle)


Lesenswert?

Okay das Problem lag tatsächlich woanders. Offensichtlich hatte ich den 
falschen Port geschaltet. grmpf :-) Nun geht die erste Variante 
zumindest soweit, dass sie bei 0V auch fast 0 und bei +5 V 1023 anzeigt. 
Der interne Ref ist ja auf 2,xx gestellt, so dass auch bei anschluß des 
LM35 1023 angezeigt wird, da dieser ca. 2,9 V bringt. Klingt irgendwie 
logisch :-) Werde also morgen mal versuchen den internen auf VCC zu 
stellen.

von Justus S. (jussa)


Lesenswert?

Andreas Schneider wrote:


> Das erste Beispiel ist übrigens das GCC Tutorial, deshalb gehe ich davon
> aus, dass es genau so sein muss.

nein ist es nicht...

bei dir
1
ADMUX |= (0<<REFS1) | (0<<REFS0);

im Tutorial
1
ADMUX |= (1<<REFS1) | (1<<REFS0);

Deine Variante funktioniert wenn nur zufällig, weil REFS1 und REFS0 
beide als default Null sind, aber deine Code würde sie nicht wieder auf 
0 stellen, wenn sie vorher mal auf 1 gestanden hätten

von Andreas S. (crsle)


Lesenswert?

Ja stimmt, leider kann man hier nix editieren. Ich benutze die Variante 
mit 1

von Johannes M. (johnny-m)


Lesenswert?

Andreas Schneider wrote:
> Ja stimmt, leider kann man hier nix editieren.
Doch, kann man. Aber nicht bis in alle Ewigkeit. Ne Viertelstunde nach 
Absenden geht es glaub ich...

von Johannes M. (johnny-m)


Lesenswert?

Andreas Schneider wrote:
> @Johannes M. ; Wow du schreibst soviel aber sagst so wenig zur Sache.
> Ich habe eigentlich nicht nach einer Lehrstunde in C-Syntax gefragt,
> sondern eine Lösung des Problems erhofft.
Naj, wenn schon, dann richtig. Wenn ich mir den Code schon anschaue, 
dann kriegste auch alles, was mir auffällt, um die Ohren gehauen...;-)

von Andreas S. (crsle)


Lesenswert?

Johannes M. wrote:
> Naj, wenn schon, dann richtig. Wenn ich mir den Code schon anschaue,
> dann kriegste auch alles, was mir auffällt, um die Ohren gehauen...;-)

Okay hast ja recht :-)

Funktioniert nun übrigens endlich. Problem war die Kombination aus 
Port-Fehler und Ref-Spannung falsch gesetzt. Nachdem diese auf VCC ist, 
gehts so wie es sollte.
1
ADMUX = (0<<REFS1) | (1<<REFS0); // interne Ref auf VCC

von Johannes M. (johnny-m)


Lesenswert?

Andreas Schneider wrote:
1
> ADMUX = (0<<REFS1) | (1<<REFS0); // interne Ref auf VCC
OK, das geht zwar, und es gibt auch Leute, die sagen, dass sie das so 
übersichtlicher finden, wenn alle Bits da stehen, aber: Die Operation 
"(0<<REFS1)" macht rein gar nichts, und sinnlose Operationen sollte man 
besser ganz weglassen. Eine Null kann man schieben, so lange man will, 
es bleibt eine Null. Und 0 | IRGENDWAS ist einfach nur IRGENDWAS...

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.