Hallo, ich öchte mit dem ATTINY84A zwei ADC Werte einlesen. Leider musste ich feststellen, dass die ADC Werte immer gleich sind, obwohl an den Pins definitiv unterschiedliche Spannungen anliegen (1V und 3V, VREF=VCC=5V). Ich habe nun schon probiert, nur den MUX umzuschalten, den ADC per ADEN abzuschalten - Mux umschalten und wieder einzuschalten und bin dabei mit den Wartezeiten zum "warmwerden" des ADC bis auf 10ms hochgegangen. Das Ergebnis ist das gleiche. ADC Werte stimmen überein. Das habe ich damit überprüft, dass ich die If-Schleife zur "RECLAIBRATION OF BRIGHTNESS" abfragen lasse, ob die Werte gleich sind. In diesem Fall wird ein Testcode ausgeführt, den ich hier rausgewofen habe. Eine LED blinkt. Und sie blinkte sehr zuverlässig. Egal ob sich die gemessenen ADC Input Werte ändern oder nicht. Was mache ich noch grundlegend falsch, um vernünftig auf 2 ADC Kanälen messen zu können?
Sag mal was soll den das laufend:
1 | ADCSRA &= ~(1<<ADEN); |
entweder man will mit dem ADC Arbeiten, dann muss man ihn auch einschalten ! Des weiteren muss man sich nach dem Datenblatt richten und nach aktivieren der ADC-Einheit den ersten Messwert verwerfen.
Uwe S. schrieb: > Sag mal was soll den das laufend: > ADCSRA &= ~(1<<ADEN); > entweder man will mit dem ADC Arbeiten, dann muss man ihn auch > einschalten ! > > Des weiteren muss man sich nach dem Datenblatt richten und nach > aktivieren der ADC-Einheit den ersten Messwert verwerfen. Wie oben beschrieben: Ich habe unter anderem, wie Sie sehen, probiert den ADC nach einer Wandlung und vor dem Kanalwechsel auszuschalten. Den Kanal zu wechslen und wieder einzuschalten. Die gemessenen Werte werden in den jeweiligen Variablen abgelegt und die erste Messung sollte nach einer Wartezeit von 10ms im Betriebsmodus free running längst verworfen sein :) Oder etwa nicht?
Das hier ist wohl nicht ganz richtig.
1 | ADMUX &= ~(1<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0); //CLEAR ADC |
sollte wohl eher das hier sein
1 | ADMUX &= ~( (1<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (1<<MUX0) ); //CLEAR ADC |
Wahnsinn. Dabei könnte das mit den Tutorial-Routinen alles so einfach sein
1 | int main() |
2 | {
|
3 | |
4 | ADC_Init(); |
5 | |
6 | while( 1 ) |
7 | {
|
8 | uint16_t wert1 = ADC_Read( 0 ); |
9 | uint16_t wert2 = ADC_Read( 1 ); |
10 | |
11 | ... mach was mit wert1 und wert2 |
12 | }
|
13 | }
|
fertig.
:
Bearbeitet durch User
Zusätzlich zu den anderen Antworten/Anmerkungen:
1 | ADCSRA |= (1<<ADEN) | (1<<ADSC); //Enables ADC and starts conversation |
2 | _delay_ms(10); |
3 | |
4 | poti_value = (ADCL | (ADCH << 8)); //Write ADC result into poti variable |
"_delay_ms" ist meiner Ansicht nach keine gute Idee. Entweder ADC Interrupt oder ADSC in ADCSRA abfragen, siehe Datenblatt ("ADSC will read as one as long as a conversion is in progress. When the conversion is complete, it returns to zero. Writing zero to this bit has no effect."). Man muss, wie in den anderen Antworten, dass ADEN nicht ein/ausschalten, dass stört eher. Wenn Du den MUX setzt und danach die Konvertierung mit ADSC startest und das Ergebnis über Interrupt oder ADSC ausliest, dann sollte das tun.
:
Bearbeitet durch User
Primex schrieb: > Die gemessenen Werte werden in den jeweiligen Variablen abgelegt und die > erste Messung sollte nach einer Wartezeit von 10ms im Betriebsmodus free > running längst verworfen sein :) Free Running und mehrere Kanäle verwenden? Da muss man schon ziemlich gut sein, um das in den Griff zu kriegen.
Achim K. schrieb: > Zusätzlich zu den anderen Antworten/Anmerkungen: > > [code] > ADCSRA |= (1<<ADEN) | (1<<ADSC); //Enables ADC and starts conversation > _delay_ms(10); > > poti_value = (ADCL | (ADCH << 8)); //Write ADC result into poti variable > [/code> > > "_delay_ms" ist meiner Ansicht nach keine gute Idee. Vor allen Dingen ist es sinnlos. Der Zweck vom Free Running Modus besteht ja gerade darin, dass man nicht warten muss, sondern sich das Ergebnis abholt, wenn man gerade im Programm an der bewussten Stelle ist. Aber manche glaubens halt einfach nicht und wollen auch nicht hören. Immer schön mit dem Kopf durch die Wand.
:
Bearbeitet durch User
Achim K. schrieb: > "_delay_ms" ist meiner Ansicht nach keine gute Idee. Entweder ADC > Interrupt oder ADSC in ADCSRA abfragen, siehe Datenblatt ("ADSC will > read as one as long as a conversion is in progress. When the conversion > is complete, Im Datenblatt des Tiny ist in einem Timing DIagram abgebildet, dass im Free running mode der ADSC sich nicht ändert (bleibt auf High)
Karl Heinz schrieb: > Aber manche glaubens halt einfach nicht und wollen auch nicht hören. > Immer schön mit dem Kopf durch die Wand. Locker bleiben :) Ich bin für jeden gut gemeinten Ratschalg dankbar und durch Fehler lernt man am besten :) Die Wartezeit soll in diesem Fall eigentlich nur sicherstellen, dass der ADC nach dem wieder einschalten vernünftig "warmgelaufen" ist. War ursprünglich mal bei xxus, habe ich dann aber nach und nach hochgezogen, bevor ich mich hier ans Forum wandt.
Karl Heinz schrieb: > Vor allen Dingen ist es sinnlos. > Der Zweck vom Free Running Modus besteht ja gerade darin, dass man nicht > warten muss, sondern sich das Ergebnis abholt, wenn man gerade im > Programm an der bewussten Stelle ist. > Ich muss gestehen, dass bei soviel Code ich nicht jedes Bit anschauen :-). Das mit dem "Free Running" habe ich schlicht übersehen. @Primex: Man muss nicht immer alles aus der Hardware holen. Manchmal reicht ein "Bruchteil" und dafür robust. Mach kein "Free Running" sondern stosse die Konvertierungen einzeln an. Also MUX setzten, ADSC setzen, warten bis ADSC 0 ist, ADC Wert auslesen, Mux auf anderen Kanal usw. Selbst mit dieser Bremse gehe ich davon aus, dass es insgesamt schnell genug ist. Oder wie oft soll Dein Programm etwas nachregeln? Alle Sekunde? Oder 10000x pro Sekunde?
Primex schrieb: > Die Wartezeit soll in diesem Fall eigentlich nur sicherstellen, dass der > ADC nach dem wieder einschalten vernünftig "warmgelaufen" ist. Quatsch. Die Wartezeit brauchst du aus mehreren Gründen. Unter anderem deshalb, weil du nicht weißt, mit welcher MUX Einstellung die gerade laufende Wandlung eigentlich operiert. Sprich nach dem unsynchronisierten Umstellen der MUX Bits müsstest du 2 Wandlungen abwarten, bis du sicher gehen kannst, dass das Ergebnis auch vom eingestellten Kanal kommt. Free-Running und _delay_ms erinnert mich irgendwie an die Bundeswehr. Da muss auch der Papierkorb so schnell wie möglich ausgeleert werden (am besten in 2 Zehntel Sekunden), nur damit man dann 3 Stunden lang Däumchen dreht. Völlig sinnlos.
:
Bearbeitet durch User
Achim K. schrieb: > Oder wie oft soll Dein Programm etwas nachregeln? Alle Sekunde? Oder > 10000x pro Sekunde? Das Programm regelt die Helligkeit einer LED. Ich füge zum Schluss eh ein Delay von 25ms ein um der Änderung der Helligkeit etwas Zeit zu geben sich zu realisieren :) Ich gehe derzeit also davon aus, dass der Single Conversion Mode hierfür deutlich besser geeignet ist!? Muss ich dabei die erste Messung nicht verwerfen und kann direkt die Kanäle wechseln, auslesen, abwarten auf ADSC=0 und weitermachen? Sehe ich das richtig?
Achim K. schrieb: > bis ADSC 0 ist, ADC Wert auslesen, Mux auf anderen Kanal usw. Selbst mit > dieser Bremse gehe ich davon aus, dass es insgesamt schnell genug ist. Wobei in seinem Fall, die 'Bremse' schneller abgearbeitet wird, als der Käse, den er jetzt hat. Tatsächlich ist in seinem Fall der Free-Running Modus jetzt zur Bremse geworden.
:
Bearbeitet durch User
Primex schrieb: > Achim K. schrieb: >> "_delay_ms" ist meiner Ansicht nach keine gute Idee. Entweder ADC >> Interrupt oder ADSC in ADCSRA abfragen, siehe Datenblatt ("ADSC will >> read as one as long as a conversion is in progress. When the conversion >> is complete, > > Im Datenblatt des Tiny ist in einem Timing DIagram abgebildet, dass im > Free running mode der ADSC sich nicht ändert (bleibt auf High) Das mit dem "Free Running" hatte ich leider übersehen :-). Wenn man das wirklich braucht, dann muss man den Interrupt nehmen. Ich baue meine Programme meist ohne "Free Running" so, dass der Interrupt die Werte ausliest in eine globale Datenstruktur schreibt, den MUX anpasst und neu startet. Der nächste Interrupt macht dann wieder das selbe mit dem nächsten Kanal. Man kann dann noch über eine Maske signalisieren, dass neue Werte da sind. Ist aber oft nicht nötig. Je nachdem kann man aber auch ebenfalls ohne "Free Running" einfach auf das ADSC Bit warten. Kommt drauf an, was das Programm in der Wartezeit sinnvoll tun kann/muss.
Primex schrieb: > Ich gehe derzeit also davon aus, dass der Single Conversion Mode hierfür > deutlich besser geeignet ist!? Wie in mehr als 95% aller Fälle. > Muss ich dabei die erste Messung nicht verwerfen und kann direkt die > Kanäle wechseln, auslesen, abwarten auf ADSC=0 und weitermachen? > Sehe ich das richtig? AVR-GCC-Tutorial Such nach ADC. Im Tutorial ist ein Link auf einen weiterführenden Artikel. Dort finden sich entsprechende Funktionen, die du nur noch verwenden musst.
Primex schrieb: > Achim K. schrieb: >> Oder wie oft soll Dein Programm etwas nachregeln? Alle Sekunde? Oder >> 10000x pro Sekunde? > > Das Programm regelt die Helligkeit einer LED. Ich füge zum Schluss eh > ein Delay von 25ms ein um der Änderung der Helligkeit etwas Zeit zu > geben sich zu realisieren :) > > Ich gehe derzeit also davon aus, dass der Single Conversion Mode hierfür > deutlich besser geeignet ist!? > Muss ich dabei die erste Messung nicht verwerfen und kann direkt die > Kanäle wechseln, auslesen, abwarten auf ADSC=0 und weitermachen? > Sehe ich das richtig? Du siehst es (so weit ich es sehen kann :-) ) richtig. Meiner Erfahrung nach muss man nur den 1. Werte nach ADEN verwerfen. Danach MUX := Kanal, ADSC := 1, warten auf ADSC == 0 und ADC auslesen. Oder Interrupt Lösung, wenn man die Wartezeit nutzen will/muss.
Achim K. schrieb: > Meiner Erfahrung nach muss man nur den 1. Werte nach ADEN verwerfen. > Danach MUX := Kanal, ADSC := 1, warten auf ADSC == 0 und ADC auslesen. > Oder Interrupt Lösung, wenn man die Wartezeit nutzen will/muss. Interrupt kann man machen, muss man aber oft nicht. Anstatt
1 | while( 1 ) |
2 | {
|
3 | MUX setzen |
4 | ADSC setzen |
5 | while( ADSC gleich 1) |
6 | ;
|
7 | ADC auslesen |
8 | |
9 | mach was mit dem gelesenen |
10 | }
|
kann man das ja auch modifizieren
1 | MUX setzen |
2 | ADSC setzen |
3 | |
4 | while( 1 ) |
5 | {
|
6 | while( ADSC gleich 1 ) |
7 | ;
|
8 | ADC auslesen |
9 | |
10 | MUX setzen |
11 | ADSC setzen |
12 | |
13 | mach was mit dem gelesenen |
14 | }
|
auch hier arbeitet der ADC parallel zum Codeteil 'mach was mit dem gelesenen' und man spart sich den ganzen Interrupt Aufsatz. Wenn man allerdings in der Hauptschleife sowieso ein _delay_ms(25) hat, dann ist das alles völlig egal. Denn im Vergleich zu diesen 25ms sind die paar µs, die der ADC braucht, lediglich Peanuts. Macht man halt anstelle von 25ms nur 24ms, dann hat man die ADC Zeit locker wieder herinnen.
:
Bearbeitet durch User
Habe nun einige gute Ideen durch euch erhalten. Ich bedanke mich ganz herzlich bei allen Ideengebern und werde mich nochmal melden, ob es nun geklappt hat und wie ich es umgesetzt habe :) Danke!
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.