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.
>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.
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.
Rahul, der Trollige wrote: >>result += ADCW; > Hab ich doch das W vergessen... Musst du ins Tutorial schaun :-)
>Musst du ins Tutorial schaun :-)
Ich steige auf BASCOM um, da brauch' ich weder Tutorium noch
Datenblatt...;-)
@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.
Warte nach dem Umschalten des ADMUX min. 40µs bis zur Messung, dann klappt's!
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??? ;-) ...
Danke für die Antworten. Mein Netzteil ist in die Begrenzung gegangen, als ich am Poti gedreht habe. Fehlt da ein Vorwiderstand?
@ 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!
@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!
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.
@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!
Ü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".
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.
Auszug aus dem mega32-Datenblatt:
>When a conversion is complete, the result is written to the ADC Data >Registers,
and ADIF is set.
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.
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?
@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
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.
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!
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...
> 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...;-)
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?
Jo, mit 19:47 haste recht. Muss trotzdem mal forschen wo ich den Code herhab', war wohl 'ne Appnote von Atmel.
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...
>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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.