mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Poti mit ADC einlesen


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

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

Autor: Rahul, der Trollige (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>result += (ADCL + (ADCH<<8));

mach das lieber so:
result += ADC;

Autor: Rahul, der Trollige (Gast)
Datum:

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

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

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

Autor: Rahul, der Trollige (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>result += ADCW;
Hab ich doch das W vergessen...

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

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

Musst du ins Tutorial schaun :-)

Autor: Rahul, der Trollige (Gast)
Datum:

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

Autor: gasto (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,
BASCOM iss langweilig da lernste gar nix !!!

gruss

Autor: johnny.m (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: Hannes Lux (hannes)
Datum:

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

...

Autor: Heike (Gast)
Datum:

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

Autor: Rahul, der Trollige (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie hast du denn das Poti angeschlossen?

Autor: 42 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wie hast du denn das Poti angeschlossen?

Vermutlich falsch.

MFG

Autor: Sonic (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
while(ADCSRA & (1 << ADSC)); //Warte, solange ADSC gesetzt
und nichts anderes, sonst kann alles Mögliche rauskommen, nur meist 
nicht das Erwünschte!

Autor: johnny.m (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

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

Autor: Walter (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: Sonic (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
ADCSRA |= 1 << ADSC;
while(ADCSRA & (1 << ADSC));
//...
und
ADCSRA |= 1 << ADSC;
while(!(ADCSRA & (1 << ADIF)));
//...
sind von der Funktion her äquivalent. Was nicht geht ist
ADCSRA |= 1 << ADSC;
while(!(ADCSRA & (1 << ADSC)));
//...
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...

Autor: Sonic (Gast)
Datum:

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

Ebenfalls good n8!

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.