Forum: Mikrocontroller und Digitale Elektronik Atmega32 AD_Wandler


von Bach001 (Gast)


Lesenswert?

Hallo Leute,

ich habe ein Problem mit dem internen AD_Wandler des Atmega32. Und zwar 
liegt an dem Wandler an Pin1 (ADC0) ein Abstandsensor, welcher 
Spannungen zw. 0,4 V und 2,56 V liefert. Abhängig von diesen Spannungen 
möchte ich ein Servo üner pwm mit dem Timer/Counter 2 steuern. Da die 
maximal gelieferte Spannung des Sensors 2,56 V ist, dachte ich mir, das 
ist optimal um die interne Referenzspannung des Wandlers zu verwenden.

Das Problem ist bloß, das das pwm-signal nicht auf die Werte des 
Ad-Wandlers reagiert, sie werden einfach ignoriert. Ich weiß nicht woran 
das liegen könnte, ich habe das Datenblatt mehrmals durchgelesen und 
auch hier im Forum gibt es ja genug Quelltext-beispiele.
ich hoffe hier kann mir jemand unter die Arme greifen, vielleicht es es 
ja nur ein dummer Anfängerfehler.

Hier mein Quelltext:
1
#include <avr/io.h>
2
3
// Initialisierung des AD-Wandlers
4
void adc_initial(void) {
5
6
//interne Referenzspannung, AD-Wandler- kanal 0 (ADC0)    
7
  ADMUX |= (1<<REFS1) | (1<<REFS0) ;
8
9
//ADC Enable, Prescaler=64 d.h. 125 KHz Wandlertakt
10
  ADCSRA |= ADCSRA |= (1<<ADEN) | (1<<ADATE)  | (1<<ADPS2) | (1<<ADPS1);
11
//Free Running Mode
12
  SFIOR  |= (0<<ADTS2) |(0<<ADTS1) | (0<<ADTS0); 
13
14
//Initialisierung des Timer 2 für PWM
15
void timer2_initial(void) {
16
17
    //Mode 1, Signale invertieren, Prescaler 64 für PWM-Frequenz = 245 Hz  
18
  TCCR2 = 0x73;
19
  DDRD |= (1<<PD7);    //OC2 (Pin 21) als PWM-Ausgang setzen
20
21
int main(void) {
22
23
  while(1) {
24
25
  timer2_initial ();  //Timer 2 initialisieren
26
27
  adc_initial();     //AD-Wandler initialisieren
28
  int DWert = 0;
29
  ADCSRA |= (1<<ADSC);      //AD-Wandlung starten
30
  while ( !(ADCSRA & (1<<ADIF)) );     //Warten bis Wandlung beendet ist
31
  DWert = ADC;
32
33
  if (DWert > 320)                     //320 entspricht 0,8 V
34
      OCR2 = 0xC4;
35
  else 
36
       OCR2 = 0x71;          
37
  }
38
}

Vielleicht sollte ich noch was zur Hardware sagen: zwischen AVCC des 
Wandlers und Masse habe ich einen 100nF-Kondensator angebracht, ebenso 
zw. AREFF des Wandlers und der Masse. Außerdem befindet sich ze. Vcc 
(Versorgungsspannung des µC) und Avcc (Versorgungsspannung des Wandlers) 
noch eine 22 µH -Spule. Im Datenblatt steht zwar was von 10 µH aber mir 
wurde gesagt ich sollte es mit den 22 µH versuchen.

Vielen Dank schon für jede Hilfe, wenn och etwas unklar sein sollte 
beantworte ich gerne alle Fragen

Grüße,
Bach001

von Christopher G. (cbg)


Lesenswert?

>  while ( !(ADCSRA & (1<<ADIF)) );     //Warten bis Wandlung beendet ist
Ist falsch.
1
while(ADCSRA & (1<<ADSC));
ist was du suchst.

LG

von Bach001 (Gast)


Lesenswert?

Danke für die schnelle Antwort, aber trotzdem funktioniert es noch nicht 
ganz wie ich will.
das pwm-signal reagiert immernoch nicht wirklich auf die vom Wandler 
gelieferten Werte. Also der Wandler soll ohne unterbrechung ständig die 
Werte des sensors wandeln und sobalt die grenze, also die 320 
unterschritten wird, soll sich das pwm-signal ändern. Das tut es bis 
jetzt nch nicht....
aber noch mal danke für die Hilfe..

von Christopher G. (cbg)


Lesenswert?

Ich seh grad, dass du den Free Running Mode nutzt. Das vorhin ist mir 
nur sofort ins Auge gesprungen.
Beim Free Running Mode muss man nur einmal starten mit
1
ADCSRA |= (1<<ADSC);
. Weiß jetzt nicht genau, ob in dem Modus das Bit jemals gelöscht wird. 
Müsstest im Datenblatt nachsehen.
Ich würde jedoch den ADC Interrupt aktivieren und das Setzen vom Compare 
Wert fürn Timer2 in der ADC ISR machen. Auto Trigger brauchst dann gar 
nicht aktivieren, kannst einfach in den Idle oder ADC Noise Reduction 
Sleep Modus gehen, da wird automatisch eine Wandlung gestartet.

von Tim (Gast)


Lesenswert?

Kann es sein das dein Compiler das hier
1
while ( !(ADCSRA & (1<<ADIF)) );
wegoptimiert hat?

Teste doch mal:
1
while ( !(ADCSRA & (1<<ADIF)) ){
2
  asm volatile ("nop");
3
};

von Bach001 (Gast)


Lesenswert?

Hallo,

@Tim: also wegoptimiert ist wohl nichts, habe deinen Vorschlag getestet, 
aber ilft immernch nicht.

@Christopher G.:
1
ADCSRA |= (1<<ADSC);

habe ich auch ausprobiert, geht auch nicht. Allerdings wird das bit im 
free running mode nie gelöscht, heißt das ich muss deshalb noch was 
beachten?
dein zweiter vorschlag hört sih interessant an, allerdings ist mir noch 
nicht alles ganz klar: in welchen register stelle ich den einen von den 
beiden modes ein? im Register SFIOR wüsste ich nicht, was ich da 
auswählen sollte.

zu den Interrupts steht im Datenblatt bei ADCSRA: "Bit 3 : when this bit 
is written to one and the I-Bit in SREG is set, the ADC conversion 
Complete Interrupt is activated"
 Da schalte ich den interrupt doch ein, oder? aber was ist mit "I-Bit in 
SREG" gemeint?

danke für alle antworten,
gruß Bach001

von Bach001 (Gast)


Lesenswert?

hmm...ich versteh das nicht..hab nch ein bisschen experimentiert, und 
dabei rausgefunden, dass:
1
 adc_initial();     //AD-Wandler initialisieren
2
  int DWert = 0;
3
  ADCSRA |= (1<<ADSC);      //AD-Wandlung starten
4
5
  DWert = ADC;
6
7
  if (DWert > 320)                     //320 entspricht 0,8 V
8
      OCR2 = 0xC4;
9
  else 
10
       OCR2 = 0x71

hier wird immer die else-Anweisung gestartet, unabhängig davon was DWert 
liefert. Wnn ich (DWert > 320) zu (DWert < 320) mache, wird allerdings 
die if-Anweisung aufgerufen. D.h. es wird immer die Anweisung < 320 
aufgerufen.
Und das ohne den Wert des AD-Wandlers zu beachten.
könnte es sein, dass irgendwie ne while-schleife falsch gesetzt habe, 
dass der AD-wert nicht aktualisiert wird ? oder das bei der Deklaration 
der Variablen DWert etwas falsch gelaufen ist? Oder habe ich beim 
Free-running mode etwas nicht beachten?

Bin langsam am verzweifeln...danke für alle Antworten...
Gruß

von Thilo M. (Gast)


Lesenswert?

Probiere mal:
1
unsigned int ADC_temp = 0;
2
ADCSRA |= (1<<ADSC);       // 'single conversion' einleiten
3
while(!(ADCSRA & 0x10));   // auf ende der Wandlung warten, ADIF flag '1'
4
ADC_temp = ADCL;           // ADCL Register lesen
5
ADC_temp += (ADCH << 8);   // ADCH Register lesen
Das muss funktionieren, vorausgesetzt dein Prescaler vom ADC stimmt.
ADMUX und ADCSRA richtig eingestellt?

von Bach001 (Gast)


Lesenswert?

und in ADC_temp steht, dann der endwert, den ich dann in den if-abfragen 
verwenden kann?
ich werds gleich ausprobieren...
danke auch

von Thilo M. (Gast)


Lesenswert?

>und in ADC_temp steht, dann der endwert
Ja.

Je nachdem wo du dann abfragst solltest du vllt. ADC_temp volatile und 
als globale Variable definieren.

von Bach001 (Gast)


Lesenswert?

habe es jetzt ausprobiert, aber das ergebnis ist wie oben...hat sich 
nichts verbessert, aber auch nicht falscher geworden :-)

mal ne andere (doofe)frage: also ich benutze den internen takt, 8 Mhz. 
Kann ich da überhaupt im register ADCSRA den prescaler einstellen? weil 
im datenblatt steht: "ADPS2:0 these bits determine the division factor 
between the XTAL frequenzy and the input clock to the ADC" . aber an 
XTAL hab ich ja garnichts hängen...

Und die Hardware stimmt? also nichts vergessen und die 
spule/Kondensatoren richtig dimensioniert?

Grüße und vielen dank für die mühe

von Christopher G. (cbg)


Lesenswert?

Zieh dir bitte das Datenblatt vom AtMega32 und lies dir die 
entsprechenden Kapitel durch. Das Datenblatt ist sehr informativ, wenn 
man es liest.
Dir fehlt eindeutig das Grundwissen.

von spess53 (Gast)


Lesenswert?

Hi

>aber an XTAL hab ich ja garnichts hängen...

Mit der Aussage ist der Controllertakt gemeint.
Im Übrigen solltest du die ADC-Initialisierung aus der Schleife in der 
main herausnehmen. Das macht man nur ein mal.

MfG Spess

von Thilo M. (Gast)


Lesenswert?

Die Einstellung müsste für 8Mhz so funktionieren:
1
ADMUX |= (1<<REFS1)|(1<<REFS0); // interne Referenz, Multiplexer auf ADC0
2
ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADIF);
3
          // ADC enable
4
          // Prescaler auf /64 => 125kHz Takt für ADC
5
          // Flag löschen

von Bach001 (Gast)


Lesenswert?

So, es funktioniert jetzt....sage lieber nicht woran es lag...
Vielen Dank an alle für die Antworten, haben mir sehr geholfen, den 
Ad-Wandler wirklich zu verstehen...super....
Grüße Bach001

von Christopher G. (cbg)


Lesenswert?

Bach001 schrieb:
> So, es funktioniert jetzt....sage lieber nicht woran es lag...
Das ist der Falsche Ansatz. Wenn jetzt einer den Thread findet, weil er 
einen ähnlichen Fehler hat, so steht er jetzt vor geschlossenen Türen.
Selbst wenn es nichts mit dem geposteten Code zu tun hat, weil der 
Fehler sich wo anders eingeschlichen hat, poste es einfach. So kann man 
schaun, ob man nicht denselben Fehler gemacht hat.

Selbst wenn der Fehler noch so blöd ist, keiner ist Perfekt.
Was ich schon an Fehlern gesucht - und auch gefunden - hab...

von Bach001 (Gast)


Lesenswert?

Ok, Christopher, hast wohl recht...
also sage ich den fehler: Die verbindung vom sensor zum µC- Pin, also 
ADC0, war schlecht gelötet, so dass nichts am AD-Wandler ankam,also 
hatte er auch nicht zu tun, und das pwm konnte natürlich auch nicht 
funktionieren.
Also hier men tipp: Auch mal ab und zu direkt am µC-pin nachmessen, ob 
überhaupt nen signal ankommt.
Mit den oben genannten Hinweisen funktioniert der Code also.
Nochmal danke an alle, dadurch habe ich auch das datenblatt nochmals 
intensiever und mit mehr Verständnis gelesen als vorher...
viele Grüße

von Thilo M. (Gast)


Lesenswert?

Manchmal ist es auch hilfreich, die Werte auf UART auszugeben 
(debuggen).
Oder bei Binärgeschichten eine LED an einem Ausgang zur Überwachung 
eines Bits zu benutzen.

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
Noch kein Account? Hier anmelden.