Forum: Mikrocontroller und Digitale Elektronik Poti mit ADC einlesen


von Heike (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
ich habe einen Atmega128 und am Analog-Port habe ich ein Potentiometer 
angeschlossen.

So sieht mein Programmcode aus:
  uint8_t i;
  uint16_t result;
  ADCSRA = (1<<ADEN) | (1<<ADPS0)| (1<<ADPS1) | (1<<ADPS2);
  ADMUX = 3;
  ADMUX |=  (1<<REFS0);
  ADCSRA |= (1<<ADSC);
  while(!(ADCSRA & (1<<ADIF)));
  result = 0;
  for(i=0;i<4;i++)
  {
    ADCSRA |= (1<<ADSC);
    while(!(ADCSRA & (1<<ADIF)));
  result += (ADCL + (ADCH<<8));
  }

  ADCSRA &= ~(1<<ADEN);
  result >>= 2;

Messen tu ich nur unsinn. Muss ich da noch eine andere Beschaltung 
vornehmen?  Als ich mein Poti zu weit ausgesteuert habe, ist glaube ich 
mein Netzteil in die Begrenzung gefahren.

von Rahul, der Trollige (Gast)


Lesenswert?

>result += (ADCL + (ADCH<<8));

mach das lieber so:
result += ADC;

von Rahul, der Trollige (Gast)


Lesenswert?

>while(!(ADCSRA & (1<<ADIF)));

Das machst du auch lieber anders:

while(!(ADCSRA & (1<<ADSC)));

Oder setzt du das ADIF irgendwo zurück? Soweit ich mich erinnere, wird 
das ADIF beim Sprung in die ISR zurückgesetzt.

von Karl H. (kbuchegg)


Lesenswert?

Das ADIF Flag wird nicht automatisch mit Beginn
der Wandlung gelöscht.

Der Compiler bietet dir einen einfach Weg an, wie man
korrekt die beiden 8 Bit Register auslesen kann.
Mit: (ADCL + (ADCH<<8));
kannst du nicht wirklich die Reihenfolge kontrollieren,
in der auf die Register zugegriffen wird.


Machs doch so, wie im Tutorial gezeigt:

   ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
    while ( ADCSRA & (1<<ADSC) ) {
      ;   // auf Abschluss der Konvertierung warten
    }
    result += ADCW;          // Wandlungsergebnisse aufaddieren


> Als ich mein Poti zu weit ausgesteuert habe, ist glaube
> ich mein Netzteil in die Begrenzung gefahren.

Wie hast du denn das Poti verschaltet?
Bei normaler Verschaltung, ist es für das Netzteil
irrelevant, wie das Poti steht. Es zieht aus Sicht
des Netzteils unabhängig vom Potischleifer immer
gleich viel Strom.

von Rahul, der Trollige (Gast)


Lesenswert?

>result += ADCW;
Hab ich doch das W vergessen...

von Karl H. (kbuchegg)


Lesenswert?

Rahul, der Trollige wrote:
>>result += ADCW;
> Hab ich doch das W vergessen...

Musst du ins Tutorial schaun :-)

von Rahul, der Trollige (Gast)


Lesenswert?

>Musst du ins Tutorial schaun :-)
Ich steige auf BASCOM um, da brauch' ich weder Tutorium noch 
Datenblatt...;-)

von gasto (Gast)


Lesenswert?

hallo,
BASCOM iss langweilig da lernste gar nix !!!

gruss

von johnny.m (Gast)


Lesenswert?

@Rahul:
> while(!(ADCSRA & (1<<ADSC)));
Da ist ein "!" zu viel... Ansonsten völlig korrekt. ADSC wird nach 
Beendigung der Wandlung automatisch gelöscht und ist bei der Abfrage 
über Polling die einzig sinnvolle Möglichkeit.

Mit der Begrenzung vom Netzteil kann ich mir auch nur so vorstellen, 
dass das Poti falsch angeschlossen ist.

von Sonic (Gast)


Lesenswert?

Warte nach dem Umschalten des ADMUX min. 40µs bis zur Messung, dann 
klappt's!

von Hannes L. (hannes)


Lesenswert?

Rahul, der Trollige wrote:
>>Musst du ins Tutorial schaun :-)
> Ich steige auf BASCOM um, da brauch' ich weder Tutorium noch
> Datenblatt...;-)

Soll ich Dir das glauben??? ;-)

...

von Heike (Gast)


Lesenswert?

Danke für die Antworten. Mein Netzteil ist in die Begrenzung gegangen, 
als ich am Poti gedreht habe. Fehlt da ein Vorwiderstand?

von Rahul, der Trollige (Gast)


Lesenswert?

Wie hast du denn das Poti angeschlossen?

von 42 (Gast)


Lesenswert?

> Wie hast du denn das Poti angeschlossen?

Vermutlich falsch.

MFG

von Sonic (Gast)


Lesenswert?

@ johnny.m:
>> while(!(ADCSRA & (1<<ADSC)));
ist doch korrekt? Oder ist:
while(!(ADCSRA & 0x10));
etwas Anderes? Letzeres läuft bei mir in 'zig Applikationen korrekt!

von johnny.m (Gast)


Lesenswert?

@Sonic:
Das ADSC ist während der Wandlung gesetzt und wird am Ende der Wandlung 
zurückgesetzt. Es ist also Unsinn, in der while-Schleife auf "nicht 
gesetzt" zu überprüfen. Also muss die Negation weg, sonst ist die 
Bedingung direkt nach dem Start des ADC schon nicht mehr erfüllt.

BTW: Hattest Du nicht immer diesen Tip mit den 40 µs warten vor jeder 
Wandlung? Könnte es evtl daran liegen, dass Du eben diesen Fehler in 
allen Deinen Programmen drin hast und immer versuchst, das Ergebnis zu 
lesen, bevor es überhaupt da ist? Nur so als Hinweis... das kann so 
nämlich nicht, wei Du behauptest, korrekt laufen!

von johnny.m (Gast)


Lesenswert?

Upps, das "wei" in der letzten Zeile soll natürlich ein "wie" sein...

von Sonic (Gast)


Lesenswert?

Also 1. löschst du das ADIF indem du eine 1 (ADSC) in's ADCSRA 
schreibst, und 2. starte ich die AD-Wandlung NACH der Pause. Das Ganze 
habe ich nicht selbst erfunden, das ist aus irgendeinem Tutorial, weiß 
auf die Schnelle leider nicht von welchem.

von johnny.m (Gast)


Lesenswert?

@Sonic:
Noch mal ganz präzise für Dich: Das ADSC-Bit (ADC Start Conversion) wird 
gesetzt, um eine Wandlung zu starten. Während die Wandlung läuft, bleibt 
dieses Bit gesetzt. Wenn man es in einer ansonsten leeren while-Schleife 
auf "nicht gesetzt" abfragt, also mit "while(!(ADCSRA & (1 << ADSC)));", 
dann wird diese Schleife gar nicht erst betreten, da das ADSC gesetzt 
ist und die Bedingung für den Schleifeneintritt nicht erfüllt ist! Das 
ADSC wird am Ende der Wandlung von der Hardware zurückgesetzt, und erst 
dann darf das Ergebnis ausgelesen werden.

Wenn Du eine Wandlung mit "ADCSRA |= 1 << ADSC;" startest und nach der 
von Dir so eisern verteidigten while-Schleife, die nie betreten wird, 
das Ergebnis ausliest, dann hat dieses Ergebnis, wenn es überhaupt etwas 
Sinnvolles ergibt, nichts mit der aktuellen Wandlung zu tun sondern 
stammt im günstigsten Fall von der vorhergehenden Wandlung. Also LASS 
DAS "!" IN DER SCHLEIFENBEDINGUNG WEG, ES IST SCHLICHT FALSCH!!! Die 
Schleife muss lauten
1
while(ADCSRA & (1 << ADSC)); //Warte, solange ADSC gesetzt
und nichts anderes, sonst kann alles Mögliche rauskommen, nur meist 
nicht das Erwünschte!

von johnny.m (Gast)


Lesenswert?

Übrigens: Das ADIF wird bei jeder Read-Modify-Write-Zugriff auf ADCSRA 
gelöscht, und das ist auch i.d.R. gar nicht verkehrt, denn es macht 
meist wenig Sinn, im ADCSRA etwas zu ändern, und dabei einen anstehenden 
Interrupt "stehenzulassen".

von Sonic (Gast)


Lesenswert?

OK, nochmal:

int ADC_conversion(unsigned char mux)        // Spannung messen (64 
Messswerte-Mittelwert)
{
  ADMUX = mux;      // Multiplexer einstellen
  _delay_us(40);      // Pause wegen MUX-Einschwingzeit
  unsigned int ADC_temp;
    unsigned int MW = 0;

    for(unsigned char i=0;i<=63;i++)  // 64 Messungen für bessere 
Genauigkeit
    {
      ADCSRA |= (1<<ADSC);       // 'single conversion' einleiten
      while(!(ADCSRA & 0x10));    // auf ende der Wandlung warten, ADIF 
flag '1'
      ADC_temp = ADCL;             // ADCL Register lesen
      ADC_temp += (ADCH << 8);   // ADCH Register lesen
      MW += ADC_temp;       // Ergebnisse der 64 Messungen addieren
    }

     MW = MW >> 6;      // Mittelwerte berechnen (/64)


  return (MW);      // spannungswert zurückgeben
}

kannste mal im AVR-Studio debuggen.

von Sonic (Gast)


Lesenswert?

Auszug aus dem mega32-Datenblatt:
>When a conversion is complete, the result is written to the ADC Data >Registers, 
and ADIF is set.

von Sonic (Gast)


Lesenswert?

Oder ausführlich:

• Bit 4 – ADIF: ADC Interrupt Flag
This bit is set when an ADC conversion completes and the Data Registers 
are updated.
The ADC Conversion Complete Interrupt is executed if the ADIE bit and 
the I-bit in
SREG are set. ADIF is cleared by hardware when executing the 
corresponding interrupt
handling vector. Alternatively, ADIF is cleared by writing a logical one 
to the flag.
Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt 
can be disabled.
This also applies if the SBI and CBI instructions are used.

von johnny.m (Gast)


Lesenswert?

Zum Geier noch mal: ADSC != ADIF!!! Das sind zwei völlig verschiedene 
Bits! Den ganzen Kram (und vor allem die Datenblattzitate, lesen kann 
ich selber!) hättest Du Dir echt sparen können. Wenn Du das Polling mit 
dem ADIF machst, dann ist das "!" korrekt. Aber erstens ist es Unsinnig, 
mit dem ADIF zu pollen, weil es anschließend noch von Hand gelöscht 
werden muss, und zweitens ist bei der (eigentlich einzig sinnvollen) 
Polling-Variante, nämlich über das ADSC, das "!" falsch!!!

Das was Du oben geschrieben hast (while(!(ADCSRA & (1<<ADSC)));) ist nur 
dann korrekt, wenn man entweder das "!" weglässt oder (nicht 
empfehlenswert) das ADSC durch ADIF ersetzt. Jetzt klarer?

von Walter (Gast)


Lesenswert?

@Sonic

"while(!(ADCSRA & (1<<ADSC)));
ist doch korrekt? Oder ist:
while(!(ADCSRA & 0x10));
etwas Anderes? Letzeres läuft bei mir in 'zig Applikationen korrekt!"

ja, das ist was anderes

"ADIF is cleared by hardware when executing the
corresponding interrupt
handling vector. Alternatively, ADIF is cleared by writing a logical one
to the flag."

das solltest du in deinem Code auch so machen

von johnny.m (Gast)


Lesenswert?

Edit:
> ...weil es anschließend noch von Hand gelöscht werden muss,...
...wenn man sich nicht darauf verlässt, dass es tatsächlich beim 
nächsten Setzen von ADSC eh gelöscht wird...

Aber wie gesagt: Nicht durcheinanderbringen. Das Codebeispiel oben 
dürfte funktionieren, aber da steht es ja auch ganz anders, als weiter 
oben die ganze Zeit behauptet. Und wenn Du jetzt noch das 
"while(!(ADCSRA & 0x10))", was ja anscheinend dazu geführt hat, dass Du 
alles durcheinandergeschmissen hast, durch ein "while(!(ADCSRA & (1 << 
ADIF)))" ersetzt, dann wird Dir das auch nicht wieder passieren... Da 
weiß man nämlich sofort, was da gemacht wird.

von Sonic (Gast)


Lesenswert?

Ok, nochmal: Das ADIF wird durch setzen von ADSC gelöscht.
Es wird nach erfolgter Conversion gesetzt und der INT wird ausgelöst.
Wo ist das Problem? Das iste eine gängige Methode per single-convertion 
zu Messen.
>wenn man entweder das "!" weglässt oder (nicht empfehlenswert) das ADSC durch 
ADIF ersetzt.
Wie ich sehe hast du mein Codebeispiel nicht ausprobiert.

>hättest Du Dir echt sparen können.
Bin halt fleißig! ;-)

Ich glaube, es soll jeder mit seiner Methode glücklich werden, ich bin's 
mit meiner jedenfalls!

von johnny.m (Gast)


Lesenswert?

Vielleicht drücke ich mich unklar aus: Wenn Du mal gründlicher ins 
Datenblatt geschaut hättest (anstatt hier rumzuzitieren, wass nix 
bringt), dann hättest Du die Frage von 19:47 sofort selber beantworten 
können. Da steht nämlich, welches Bit an welcher Stelle im ADCSRA steht, 
und dann hättest Du auch gesehen, dass "0x10" nicht gleich "1 << ADSC" 
ist, sondern gleich "1 << ADIF"! Da muss dann erst ein anderer 
nachsehen...

von johnny.m (Gast)


Lesenswert?

> Wie ich sehe hast du mein Codebeispiel nicht ausprobiert.
Ach ja, ausprobiert habe ich es nicht, weil ich den ADC praktisch nie im 
Polling-Verfahren abfrage. Aber ich habe es mir angesehen und sehe (wie 
oben schon mal gesagt) keinen Grund, warum es nicht funktionieren 
sollte. Allerdings sehe ich beim nochmaligen Durchgehen auch die Stelle, 
an der wir angefangen haben, aneinander vorbeizureden, nämlich genau das 
von mir erwähnte Posting von 19:47... Wie gesagt, bevor man so was 
schreibt, sollte man schon eben ins Datenblatt schauen, dann erübrigt 
sich sowas meist ganz schnell. Und Datenblätter lesen kannste ja...;-)

von Sonic (Gast)


Lesenswert?

Muss ich mich denn selber zitieren?
>Ok, nochmal: Das ADIF wird durch setzen von ADSC gelöscht.
Ich löse durch setzen des ADSC ein Messung aus (dazu ist das bit 
schließlich da!) und warte bis die Messung beendet ist, angezeigt durch 
Wechsel von null auf eins des ADIF.
Liest du eigentlich meine Beiträge oder Codebeipiele durch?

von Sonic (Gast)


Lesenswert?

Jo, mit 19:47 haste recht. Muss trotzdem mal forschen wo ich den Code 
herhab', war wohl 'ne Appnote von Atmel.

von johnny.m (Gast)


Lesenswert?

Der Code ist ja auch OK. Und ich habe auch nie behauptet, dass das ADIF 
nicht gelöscht wird, wenn ADSC gesetzt wird. Aber es gibt eben zwei 
Möglichkeiten der Abfrage, und die sind hier ziemlich 
durcheinandergewürfelt worden. Um das ganze zum Abschluss zu bringen:
1
ADCSRA |= 1 << ADSC;
2
while(ADCSRA & (1 << ADSC));
3
//...
und
1
ADCSRA |= 1 << ADSC;
2
while(!(ADCSRA & (1 << ADIF)));
3
//...
sind von der Funktion her äquivalent. Was nicht geht ist
1
ADCSRA |= 1 << ADSC;
2
while(!(ADCSRA & (1 << ADSC)));
3
//...
Ich hoffe, jetzt sind alle Klarheiten restlos beseitigt... Und vergiss 
nicht, in Deinem Code das "0x10" durch ein "1 << ADIF" zu ersetzen. Dann 
kommen solche Fragen gar nicht erst mehr auf.

Good night...

von Sonic (Gast)


Lesenswert?

>in Deinem Code das "0x10" durch ein "1 << ADIF" zu ersetzen.
sorry, bin von Natur aus faul!

Ebenfalls good n8!

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.