www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Atmega32 AD_Wandler


Autor: Bach001 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>

// Initialisierung des AD-Wandlers
void adc_initial(void) {

//interne Referenzspannung, AD-Wandler- kanal 0 (ADC0)    
  ADMUX |= (1<<REFS1) | (1<<REFS0) ;

//ADC Enable, Prescaler=64 d.h. 125 KHz Wandlertakt
  ADCSRA |= ADCSRA |= (1<<ADEN) | (1<<ADATE)  | (1<<ADPS2) | (1<<ADPS1);
//Free Running Mode
  SFIOR  |= (0<<ADTS2) |(0<<ADTS1) | (0<<ADTS0); 

//Initialisierung des Timer 2 für PWM
void timer2_initial(void) {

    //Mode 1, Signale invertieren, Prescaler 64 für PWM-Frequenz = 245 Hz  
  TCCR2 = 0x73;
  DDRD |= (1<<PD7);    //OC2 (Pin 21) als PWM-Ausgang setzen

int main(void) {

  while(1) {

  timer2_initial ();  //Timer 2 initialisieren

  adc_initial();     //AD-Wandler initialisieren
  int DWert = 0;
  ADCSRA |= (1<<ADSC);      //AD-Wandlung starten
  while ( !(ADCSRA & (1<<ADIF)) );     //Warten bis Wandlung beendet ist
  DWert = ADC;

  if (DWert > 320)                     //320 entspricht 0,8 V
      OCR2 = 0xC4;
  else 
       OCR2 = 0x71;          
  }
}

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

Autor: Christopher G. (cbg)
Datum:

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

LG

Autor: Bach001 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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..

Autor: Christopher G. (cbg)
Datum:

Bewertung
0 lesenswert
nicht 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
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.

Autor: Tim (Gast)
Datum:

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

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

Autor: Bach001 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

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

@Christopher G.:
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

Autor: Bach001 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hmm...ich versteh das nicht..hab nch ein bisschen experimentiert, und 
dabei rausgefunden, dass:
 adc_initial();     //AD-Wandler initialisieren
  int DWert = 0;
  ADCSRA |= (1<<ADSC);      //AD-Wandlung starten

  DWert = ADC;

  if (DWert > 320)                     //320 entspricht 0,8 V
      OCR2 = 0xC4;
  else 
       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ß

Autor: Thilo M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Probiere mal:
unsigned int ADC_temp = 0;
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      
Das muss funktionieren, vorausgesetzt dein Prescaler vom ADC stimmt.
ADMUX und ADCSRA richtig eingestellt?

Autor: Bach001 (Gast)
Datum:

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

Autor: Thilo M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bach001 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christopher G. (cbg)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Thilo M. (Gast)
Datum:

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

Autor: Bach001 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christopher G. (cbg)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Bach001 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Thilo M. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.