Keine ADC-Kanal Adressierung möglich ATMEGA8

OP #2861886
Lesenswert?

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
(Firma: matzetronics) #2861907
Lesenswert?

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.
OP #2861924
Lesenswert?

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.
#2861931
Lesenswert?

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
OP #2861944
Lesenswert?

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
(Firma: matzetronics) #2861946
Lesenswert?

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
ADMUX = (1<<REFS0)|(1<<MUX0);
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
Lesenswert?

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.
OP #2861975
Lesenswert?

@ 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
#2862095
Lesenswert?

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
OP #2862140
Lesenswert?

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
(Firma: matzetronics) #2862538
Lesenswert?

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
// Setze Vorteiler und gib den ADC frei.
2
// Man beachte das vorbesetzen mit =
3
ADCSRA = (1<<ADEN) | (1<<ADPS2);
4
// nu wird der ADMUX besetzt
5
ADMUX = (1<<REFS0)|(1<<MUX0);
6
// und jetzt starten wir. Um ADCSRA nicht zu zermanschen, mit |=
7
ADCSRA |= (1 << ADSC);
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
ADCSRA &= ~(1<<ADFR); // als Beispiel mal das Freerun Bit löschen
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.
OP #2862549
Lesenswert?

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
Angehängte Dateien:
#2862550
Lesenswert?

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
  ADMUX = (1<<REFS0) | 5;
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
#define ADC_COMMON     (1<<RAFS0)   // die allen ADC Messungen gemeinsamen Einstellungen
2
#define ADC_SOLL       0            // Kanal 0 ist der Sollwert
3
#define ADC_COMPARE_1  2            // Der erste Vergleichswert am ADC Kanal 2
4
#define ADC_COPMARE_2  4            // Der zweite Vergleichswert am ADC Kanal 4
5

6
...
7

8

9

10
   ADMUX = ADC_COMMON | ADC_SOLL;     // Sollwert messen
11
   ...
12

13
   ADMUX = ADC_COMMON | ADC_COMPARE_1;   // ersten Vergleichswert
14
   ...
15

16
   ADMUX = ADC_COMMON | ADC_COMPARE_2;   // zweiten Vergleichswert
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.
#2862563
Lesenswert?

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.
#2862565
Lesenswert?

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
OP #2862584
Lesenswert?

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
(Firma: matzetronics) #2862592
Lesenswert?

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
#define LED_PORT PORTB
2
#define LED_DIR DDRB
3
#define LED_GREEN_PIN 5   // eine grüne LED an PB5 , Kathode am Pin, Anode an Plus
4
// Port init 
5
LED_DIR = (1<<LED_GREEN_PIN)
6

7
// grüne LED an
8
LED_PORT &= ~(1<<LED_GREEN_PIN);
9
// grüne LED aus
10
LED_PORT |= (1<<LED_GREEN_PIN);
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.
OP #2862781
Lesenswert?

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
OP #2862845
Lesenswert?

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.
#2862858
Lesenswert?

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
int main()
2
{
3
  lcd_init();
4

5
  while( 1 ) {
6

7
    ADMUX = ...
8
    ADCSRA |= ...
9
    while( ADCSRA ... )
10
      ;
11
    Soll = ADCW;
12

13
    ADMUX = ...
14
    ADCSRA |= ...
15
    while( ADCSRA ... )
16
      ;
17
    Ist1 = ADCW;
18

19
    ADMUX = ...
20
    ADCSRA |= ...
21
    while( ADCSRA ... )
22
      ;
23
    Ist2 = ADCW;
24

25
    zb
26
    sprintf( buffer, "%04d %04d %04d", Soll, Ist1, Ist2);
27
    lcd_gotoxy( 0, 0 );
28
    lcd_puts( buffer );
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.
OP #2862876
Lesenswert?

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
#2862904
Lesenswert?

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
uint16_t GetADC( uint8_t channel )
2
{
3
  ADMUX = ADC_COMMON | channel;
4

5
  ADCSRA |= ...
6
  while( ADCSRA ... )
7
    ;
8

9
  return ADCW;
10
}
11

12
int main()
13
{
14
  lcd_init();
15

16
  while( 1 ) {
17

18
    Soll = GetADC( ADC_SOLL );
19
    Ist1 = GetADC( ADC_COMPARE_1 );
20
    Ist2 = GetADC( ADC_COMPARE_2 );
21

22
    zb
23
    sprintf( buffer, "%04d %04d %04d", Soll, Ist1, Ist2);
24
    lcd_gotoxy( 0, 0 );
25
    lcd_puts( buffer );
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.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren