Guten Tag, leider habe ich kein passendes, bereits offenes, Thema hierzu gefunden. Zu meinem Problem. Für meine Abschlussarbeit der Technikerschule entwickel ich ein Projekt bei dem ich mehrere ADC-Kanäle auswerten will. Verwendet wird ein ATMEGA8 im PDIP Gehäuse. An AREF ist ein Kondensator nach Masse geschaltet (laut Datenblatt). An PORT D ist ein LCD Display angeschlossen. Funktion der entsprechenden Zeilen: -es wird ein Sollwert (0-5V) ermittelt -es werden nacheinander zwei weitere Werte ermittelt Danach folgt die Verarbeitung und die Ausgabe an das LCD-Display. Nun ist das Problem, dass, egal welchen Kanal ich im ADMUX Register eingebe, nur der ADC Kanal 0 arbeitet. Codeausschnitt: [c] //Sollwerterfassung ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1)|(1<<ADSC); ADMUX=0x11; ADMUX=(0<<REFS1)|1<<REFS0); while (ADCSRA & (1<<ADSC)); Sollwert = ADCW; [c] Also ob ich im ADMUX eine 11 oder eine 01 usw. schreibe hat keine Auswirkung auf das Programm.Das heißt es wird immer Kanal 0, also ADMUX = 0x00, erfasst und an das LCD Display ausgegeben. Ich habe schon diverse unterschiedliche Schreibweisen der Adressen sowie zwei ATMEGA 8 mit dem Programm getestet. Leider ohne Erfolg. Hat hier jemand schon einmal ein ähnliches Problem gehabt / gelöst? Im Augenblick komme ich nicht weiter. Danke und viele Grüße Stephan
Stephan Winckler schrieb: > ADMUX=0x11; > ADMUX=(0<<REFS1)|1<<REFS0); Du schreibst also erstmal 0x11 ins ADMUX. Dann überschreibst du das mit der 2ten Zeile. Welche Technikerschule ist das denn? Etwa Beuth?
Gast
#2861896
Hallo, "(0<<REFS1)" was willst Du damit erreichen? MfG egonotto
Gast
#2861898
Stephan Winckler schrieb: > ADMUX=(0<<REFS1)|1<<REFS0); Anzahl der Klammern ist nicht ausgewogen
> ADMUX=0x11; > ADMUX=(0<<REFS1)|1<<REFS0); Völliger Humbug
Gast
#2861901
Stephan Winckler schrieb: > ADMUX=(0<<REFS1)|1<<REFS0); Da fehlt doch eine Klammer. Das Programm darf doch gar nicht übersetzt werden. MfG egonotto
Ich würde gerne mal wissen, was für einen Abschluss man dann hat - und warum bin ich so früh geboren, das ich nicht auch mit so einem Sontagsvormittags-Picknick Projekt meinen Abschluss machen durfte. Das ist ungerecht - allerdings wird mir angst und bang, wenn ich darüber nachdenke, was für Controllerprogramme dann in meinem nächsten Auto laufen.
Hey Matthias mal ganz ruhig! Ich habe von der Programmierung wenig bis keine Ahnung und lerne das auch nicht in der Schule! Das ganze ist ein Projekt mit Praxisanteil und ausführlicher Dokumentation, eine Gsamtentwicklung neben der Abendschule. Das Programmieren ist nur ein winziger, unbedeutender Teil, der aber trotzdem funktionieren muss! Nun mal zu den hilfreichen (auch deiner Matthias) Antworten. Erstmal danke. Die Klammer habe ich drin, die ist nur auf dem Weg ins Forum verschütt gegangen. @ Matthias: Um das Überschreiben zu umgehen, muss der Befehl also so aussehen? ADMUX |= 0x11 | (1<<REFS0); Oder raff ich da etwas nicht? @egonotto: Ich weiß, dass dieser Befehl nicht sein muss, habe es aber aus einem Beispiel zur Problemlösung mit übernommen.
egonotto schrieb: > Stephan Winckler schrieb: >> ADMUX=(0<<REFS1)|1<<REFS0); > > > Da fehlt doch eine Klammer. Das Programm darf doch gar nicht übersetzt > werden. Heutzutage haust du deinen Krempel (aka Krempel von anderen den du per Copy&Paste geklaut hast) erstmal ins Forum, dann übersetzt du ihn. Sonst kannst du ja nicht wissen ob er korrekt ist. Und der Compiler gibt keine so schönen Lösungsvorschläge wie das Forum. gruß cyblord
Gast
#2861933
LOL 1. Forumssuche bemühen 2. Datenblatt lesen und versuchen zu verstehen 3. Verstandene Funktion des µC umsetzen 4. Ergebis testen, wenn nix funzt gehe zu 2. 5. Abliefern
Gast
#2861940
Tipp: Bit löschen Register&=~(1<<bitnr);
Und nochmal. Danke für die Hilfe! Ich habe in der Suche leider nichts Ähnliches gefunden. @ cyblord: Wodurch, kann ein Fehler der Art auftreten, das die Adressierung des Kanals nicht übernommen wird? Hat Matthias Recht mit seiner Aussage, dass das doppelte ADMUX die Adresse überschreibt und damit eine Änderung unwirksam macht? @loller: Wie suche ich denn, deiner Meinung nach, nach einem Adressierungproblem des ADC's in der Suche? MfG Stephan
Stephan Winckler schrieb: > ADMUX |= 0x11 | (1<<REFS0); > > Oder raff ich da etwas nicht? Nein. Erstens ist Bit 4 ein reserviertes Bit im Mega8 und soll gar nicht angefasst werden (Seite 205 im Datenblatt). Und zweitens solltest du es, wenn schon denn schon, so schreiben:
1 | |
Zugegeben, (1<<MUX0) schiebt gar nichts nach links, aber so ist es einfach, auch mal MUX1 oder MUX2 anzusprechen, ohne im Datenblatt rumzuwühlen.
Gast
#2861956
Stephan Winckler schrieb: > @loller: > > Wie suche ich denn, deiner Meinung nach, nach einem Adressierungproblem > des ADC's in der Suche? Wenn Du Dir die Mühe gemacht hättest VORHER nachzusehen, hättest Du via Suche nach AVR ADC GCC Tutorial z.B. das gefunden: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe Noch schneller wäre gewesen gleich direkt im GCC-Tutorial nachzusehen.
@ Matthias: Den Befehl (1<<MUX0) kannte ich in der Form noch nicht. Ich habe mit einem Lernbuch die am Anfang genannte Adressierung gelernt. Jetzt machen auch die Tabellen im Datenblatt mehr Sinn :-) @ loller: Das Tutorial habe ich natürlich Hilfe gezogen. allerdings hat es mir, aufgrund der oben geschriebenen "Lernanfänge", nicht sonderlich geholfen, da mir die Adressierung wie von Matthias beschrieben, unbekannt war. Danke erstmal an alle. Ich werde mich morgen früh da mal drauf stürzen und die andere (Volt einzig richtige) Adressierung ausprobieren. MfG
Stephan Winckler schrieb: > Ich habe mit einem Lernbuch die am Anfang genannte Adressierung > gelernt. Nur als abschreckendes Beispiel - nenn dieses Buch mal bitte, damit wir einen Shitstorm auslösen können, hehehe.
Das Buch heißt AVR. Autor : F. Schäffer. Verlag : Elektor. Die Einstellungen des ADC des ATMEGA wird ca. ab Seite 158 sehr detailliert beschrieben. Die Kanalauswahl scheint mir jetzt aber ein wenig unglücklich. MfG Stephan
Das Problem ist doch, dass es dabei nicht um die Funktion des Registers geht, sondern grundsätzlich um Bitoperationen. Was der Register in Endeffetk bewirkt ist doch dann eine ganz andere Geschichte. Darum brigt doch hier ein Kapitel über den ADC gar nichts, wenn die Grundlagen nicht sitzen. gruß cyblord
Gast
#2862118
unsigned int convertanalog(unsigned cha channel)
{
ADMUX=(1<<REFS1)|(1<<REFS0)|(channel & 0x0f);
.....
bla bla
.....
}
Aufruf:
adc0val=convertanalog(0);
Gast
#2862121
Die Biteinstellung für ADMUX ist mit ADMUX = (1<<REFS0) usw. ADMUX = 0x00 beschrieben. Da es sich um ein Buch von 0 Wissen an handelt, meine ich, sollte es so doch möglich sein. Welcher Wert in das Register muss steht im Datenblatt. Also habe ich den Wert für den entsprechenden Kanal abgelesen und eingegeben. Bis zu dem Punkt ist mir das Buch sehr verständlich und hilfreich gewesen. Hoffentlich komme ich noch dahinter, was ich da falsch verstehe. :-) @ Unwissender: Diese Form habe ich auch schon gesehen. Welche Vorteile bringt sie mit? Danke für eure Hilfe. Ich werde mich morgen weiter mit dem Thema beschäftigen. MfG
Stephan Winckler schrieb: > Hoffentlich komme ich noch dahinter, was ich da falsch > verstehe. :-) Ach komm, so kompliziert ist das doch nicht. i = 2; i = 4; Was ist nach diesen beiden Zeilen der Inhalt von i?
:-) Eigentlich müsste es 4 sein.
Stephan Winckler schrieb: > Eigentlich müsste es 4 sein. Ganz genau. Und nun schau dir noch mal die ADMUX-Zeilen an: ADMUX=0x11; ADMUX=(0<<REFS1)|1<<REFS0); Was ist nun der Inhalt von ADMUX nach diesen beiden Zeilen? Und was bedeutet das für die Kanalauswahl? (dass 0x11 sowieso der falsche Wert ist, lassen wir mal außer Betracht)
Guten Morgen, Das heisst dann steht 1<<REFS1 USW da drin und das 0x11,auch wenn es der falsche Wert ist, wird überschrieben.
Mich wundert (nicht wirklich), das in einem Elektor Buch reservierte Bits beschrieben werden, normalerweise tut man das nicht. Beim ADMUX ist ein Schreiben in einem Rutsch, also per ADMUX = blabla ganz sinnvoll. Interessant wird es halt beim Starten des ADC über ADCSRA. Da ist verodern mit dem Originalwert des Registers interessant, vor allem zum löschen des IRQ Flags und zum Starten des ADC:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
Die Technik der Veroderns und VerANDens ist beim AVR gang und gäbe. Du kannst so in C gezielt einzelne Bits löschen und setzen, im Prinzip das, was die Assemblerbefehle CBI und SBI bzw. SBR und CBR auch machen. Zum Löschen eines Bits nimmst du die Komplementärfunktion in C.
1 | |
2 | |
Die Wellenlinie ~ ist die Komplementärfunktion und das &= ist dann das VerANDen mit dem Ziel. Diese Technik und die ausführliche Schreibweise führen dazu, das du dein Program auch nach Jahren noch lesen kannst, ohne im Datenblatt nachzulesen. Ausserdem musst du beim Wechsel auf einen anderen Mega nicht immer wieder schauen, ob die Bits ihre Position geändert haben.
Moin nochmal, danke Matthias für die Erklärungen. Eine Sache erschließt sich mir allerdings noch nicht. Wenn ich mit bspw. (1<<MUX0) den ADC-Kanal 0 auswähle, müsste ich ja mit (1<<MUX1) usw die nächsten Kanäle ansteuern können. In der datei im Anhang habe ich einmal die MUX-Tabelle aus dem ATMEGA8 Datenblatt angehängt. Dort ist MUX3..0 beschrieben, aber 6 ADC Kanäle. Wie spreche ich denn dann die letzten beiden an? MfG
Wobei ich hier bei den MUX Bits ausnahmsweise nicht auf die MUX Bitbezeichnungen gehen würde, sondern das ganze so ansetzen würde Am Beispiel zur 'Aktivierung' des Kanals 5
1 | |
der Vorteil dieser Schreibweise ist ziemlich offensichtlich. Im Code steht für jeden lesbar, dass hier der Kanal 5 aktiviert wird. Die MUX Bits sind genau so angeordnet, dass ihre binäre Repräsentierung exakt der Kanalzahl entspricht. Und in diesem Fall ist mir diese bessere Lesbarkeit das dann auch wert ausgenutzt zu werden. Hat man in einem Programm mehere ADC Kanäle im Einsatz, dann eben zb so
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
(*) bzw. dann eben die entsprechenden Varianten, wenn die ADC Funktionalität in einer eigenen Funktion steckt. Da hat man dann das beste aus 2 Welten. Auf der einen Seite eine leichte Anpassbarkeit und auf der anderen Seite einen lesbaren, selbsterklärenden Code. (*) wobei es dann natürlich sinnlos ist, im Kommentar die Kanalnummer noch einmal zu nennen. Steht ja eh schon im Makro und wurde hier im Kommentar nur deshalb angeführt, damit man die Verständllichkeit des Makros sieht.
Um nocheinmal auf das Buch zu sprechen zu kommen. Da steht als Beispiel ADMUX = 0x01; drin. Das vierte Bit wird nicht angesprochen. Allerdings wird so beschrieben, dass so die Kanalauswahl funktioniert. Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen?
Stephan Winckler schrieb: > Um nocheinmal auf das Buch zu sprechen zu kommen. Da steht als Beispiel > > ADMUX = 0x01; drin. Das vierte Bit wird nicht angesprochen. Allerdings > wird so beschrieben, dass so die Kanalauswahl funktioniert. > Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen? Du musst dir eines angewöhnen. Deine absolute, uneingeschränkte, über jeden Zweifel erhabene, für dich während der AVR-Programmierung einzig zuständige 'Bibel' ist das Datenblatt des Prozessors. Dort stehen die Antworten für alle deine Fragen, die sich um die Funktionsweise deines Prozessors und seiner Steuerregister drehen. Das kriegst du bei ATmel auf der Homepage. Und ja. Du brauchst das Datenblatt. Ohne kannst du nicht vernünftig programmieren. Kein Mensch kennt alle Bits auswendig. Was denkst du, wo wir nachsehen, wenn du fragst, was Bit 4 im ADMUX Register macht? Wenn wir im Datenblatt nachsehen, dann kannst du das genausogut auch gleich selber machen. Deine Bibel ist immer das Datenblatt und nicht irgendein windiges Elektor Buch. Und diese Zuweisung von Hex-Zahlen an ein Register, die vergisst du am besten gleich wieder. Das sollte man ausdrucken und dem Autor solange um die Ohren dreschen, bis nur noch Konfetti übrig bleiben.
Stephan Winckler schrieb: > Wenn ich mit bspw. (1<<MUX0) den ADC-Kanal 0 > auswähle, Falsch. Bei Kanal 0 sind sind alle Bits (MUX0...3) = 0 Kanal 1: (1<<MUX0) Kanal 2: (1<<MUX1) Kanal 3: (1<<MUX1) | (1<<MUX0) Kanal 4: (1<<MUX2) ... > Dort ist MUX3..0 beschrieben, aber 6 ADC Kanäle. Der ATmega8 hat nur 6 Kanäle, nur das Register ist gleich wie bei den Typen mit 8 Kanälen. Gruß Dietrich
Stephan Winckler schrieb: > Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen? Schau mal im Datenblatt: da steht "-", was bedeutet soviel wie "nicht benutzt" bzw. "keine Funktion". Beim Lesen liefert es "0". Gruß Dietrich
Jetzt hab ichs! Die Auswahl der Kanäle erfolgt über über das Setzen der Bits im MUX Register. und über die 4 MUX´s kann ich alle 8 (wenn vorhanden) Kanäle anwählen. Kanal 5 : (1<<MUX2) | (1<<MUX0) Jetzt kanns weiter gehen. @ Karl Heinz: Das Datenblatt habe und verwende ich bereits soweit es meine Möglichkeiten zulassen. @ Dietrich und Karl heinz: Eure Hinweise, auch die Schreibweise, sind großartig und haben mir auch bei zukünftigen Problemen sehr geholfen! Danke. MfG
Stephan Winckler schrieb: > Um nocheinmal auf das Buch zu sprechen zu kommen. Da steht als Beispiel > > ADMUX = 0x01; drin. In diesem Moment, und das ist das Problem, wird zwar der Eingangskanal schon auf 1 gesetzt, aber auch REFS0 und REFS1 auf 0. Das ist nicht das, was du möchtest. Karl Heinz hat das sehr gut gelöst, indem er die gemeinsame Einstellung (nämlich REFS0 auf 1) in den #define ADC_COMMON gepackt hat, und dann den gewünschten Kanal mit dieser Einstellung verodert und in das ADMUX schreibt. Dietrich L. schrieb: >> Für welche Funktion ist das vierte Bit denn im ATMEGA8 denn vorgesehen? > > Schau mal im Datenblatt: da steht "-", was bedeutet soviel wie "nicht > benutzt" bzw. "keine Funktion". Beim Lesen liefert es "0". Das ist typisch für so eine Prozessorfamilie, die Jungs behalten sich vor, bei Nachfolgemodellen dieses Bit zu benutzen. Spassigerweise benutzen die direkten Nachfolger Mega48/88 etc. dieses Bit immer noch nicht. Also schön auf 0 lassen :-P Nochmal zur Bitmanipulation. Besonders sinnvoll ist das natürlich für I/O Ports, um einen Pin high oder low zu setzen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Wenn du jemals auf die Idee kommen solltest, das die LED an einem anderen Pin/Port besser aufgehoben ist, änderst du nur die #defines und fertig. Auch ein Prozessorwechsel oder eine Platinenänderung schockt dich dann nicht mehr.
So nun habe ich die variante von Karl Heinz in das mein Programm
integriert.
Mit meinen Einstellungen sieht das wie folgt aus:
#define ADC_Einstellung (1<<REFS0) // interne Referenzspannung
für alle Messungen
#define ADC_Sollwert 2 // Wert von Sollwert an Kanal 2
#define ADC_Sensor_1 1 // Wert von Sensor 1 an Kanal 1
#define ADC_Sensor_2 0 // Wert von Sensor 2 an Kanal 0
und im Text:
[c] ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); // ADC
Voreinstellungen
vornehmen
ADMUX = ADC_Einstellung | ADC_Sollwert; // REF-Spannung wählen,
Sollwertkanal einstellen
ADCSRA |= (1<<ADSC); //Einzelumwandlung
verODERt
while (ADCSRA & (1<<ADSC));
Sollwert = ADCW; [c]
Die Ausgabe des Sollwertes funktioniert tadelos.
Das gleiche Vorgehen verwende ich für die anderen Werte. Nun ist das
Problem, dass mir für die drei Kanäle die gleichen Werte zurück gegeben
werden.
Ich habe nun probiert mit
ADCW = 0;
nach jeder Wandlung den Wert zu überschreiben. Aber ich erhalte trotzdem
immer den Wert des Sollwertes. Auch wenn ich diesen verändere, ändert
sich der Wert für Sensor 1 und 2 mit.
Kann mir jemand veraten, wo mein Denkfehler liegt?
MfG
Stephan Winckler schrieb: > Das gleiche Vorgehen verwende ich für die anderen Werte. Nun ist das > Problem, dass mir für die drei Kanäle die gleichen Werte zurück gegeben > werden. Beschreib nicht deinen Code, zeig ihn einfach. So umfangreich wird der schon nicht sein. Es gibt noch hunderte andere Möglichkeiten, was da alles passiert sein kann.
Gast
#2862800
Stephan Winckler schrieb: > Kann mir jemand veraten, wo mein Denkfehler liegt? ADCH und ADCL kann nur gelesen werden. Erkennt man im Datenblatt daran, daß bei Read/write immer nur ein R bei den entsprechenden Registern steht.
Ok, dass ich das Ausgaberegister nicht beschreiben kann, habe ich verstanden. @ Karl Heinz: Den Code darf ich, wegen der Prüfung, die damit in Zusammenhang steht nicht veröffentlichen. Hast du eine Mailadresse oder gibt es PN in diesem Forum? Ich habe des Weiteren mal nur die einzelnen Umwandlungen im Code gelassen. Wenn ich die Werte einzelnd abfrage stimmen Sie. Allerdings sobald es zwei oder drei sind, dann wird der Wert immer noch in die anderen Messungen übernommen.
Stephan W. schrieb: > Ok, dass ich das Ausgaberegister nicht beschreiben kann, habe ich > verstanden. > > @ Karl Heinz: > > Den Code darf ich, wegen der Prüfung, die damit in Zusammenhang steht > nicht veröffentlichen. Hast du eine Mailadresse oder gibt es PN in > diesem Forum? Du meinst du darfst
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
nicht veröffentlichen? Na dann. Man kann IMMER eine Aufgabe soweit abspecken, dass sie sich nur auf einen Aspekt konzentriert und man trotzdem das rauskriegt, was man wissen will.
Ok da hast du natürlich Recht. Es klang danach, dass ich den ganzen Code einstellen soll und das geht leider nicht. Die Abschnitte sehen wie folgt aus: Die defines: #define ADC_Einstellung (1<<REFS0) #define ADC_Sollwert 2 #define ADC_Sensor_1 1 #define ADC_Sensor_2 0 Für den Sollwert: ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); ADMUX = ADC_Einstellung | ADC_Sollwert; ADCSRA |=(1<<ADSC); while (ADCSRA & (1<<ADSC)); Sollwert = ADCW; Für Sensorwert 1: ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); ADMUX = ADC_Einstellung | ADC_Sensor_1; ADCSRA |= (1<<ADSC); while (ADCSRA & (1<<ADSC)); Sensor_1 = ADCW; Für Sensorwert 2: ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); ADMUX = ADC_Einstellung | ADC_Sensor_2; ADCSRA |= (1<<ADSC); Sensor_2 = ADCW; Ich habe den Fehler gefunden! Ich habe für Sensor 2 nicht gewartet, bis die Umwandlunge fertig war! while (ADCSRA & (1<<ADSC)); hat bei dem zweitem Sensor gefehlt. Dann hat der prozessor also den Wert, der sich in ADCW befand genommen, da der aktuelle noch nicht volag! MfG
Stephan W. schrieb: > Ich habe den Fehler gefunden! Ich habe für Sensor 2 nicht gewartet, bis > die Umwandlunge fertig war! Und .. du hast rausgefunden, warum es vernünftig ist, sich für Teilaufgaben, überhaupt wenn es immer die gleiche Teilaufgabe ist, sich Funktionen zu machen
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
Nicht nur wird das Programm kürzer. Es wird auch übersichtlicher und wenn die Funktion für den einen Kanal funktioniert, funktioniert sie auch für die anderen.
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.
