PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Fragen zum A/D wandeln



Murphywareinoptimist
16.03.2006, 18:12
So ich habe mich nun von der Bitverarbeitung zur Analogverarbeitun gekämpft und habe da einige Fragen.


Was ist bei der Dimensionierung zu beachten? Mal abgesehen davon, dass die Referenzspannung nicht überschritten werden darf?

Beschaltet werden muss bei Interner Referenzspannung nur AVCC (VCC) und AGND ohne Bauteile.

Bei Externer Referenzspannung AGND AVCC (VCC) und AREF ohne Bauteile.
AREF muss zwischen VREF (2,56V) und VCC liegen.

Habe gelesen man kann nur einen der vier ADC´s nutzen?? Ist das so richtig (ATMega8)

Da ich mich mit C noch nicht beschäftigt habe würde ich auch gerne noch den Code besprechen...



#include <avr/io.h>

/* Analog/Digital Wandler initialisieren */
void adc_init(void);

unsigned int buffer;

int main(void)
{

/* A/D - Wandler initialisieren */
adc_init();

while (1)
{
/* Wandlung starten */
ADCSRA |= (1<<ADSC); // Setzt das ADSC Bit im ADCSRA Register Also eine einfache Wandlung steht an.

/* Warten bis die AD-Wandlung abgeschloßen ist */
while ( !(ADCSRA & (1<<ADIF)) ) //Das ADCSRA wird mit dem ADIF Bit und verknüpft
//Rückgabewert 1 Wenn die Wandlung abgeschlossen ist. Duch die Negation wir die While Schleife möglich.
;

/* AD-Wert auslesen */
buffer = (ADCH<<8) | ADCL; // Das ist mir überhaupt noch nicht klar???

}

}

/* Analog/Digital Wandler initialisieren */
void adc_init(void)
{

/* externe Referenzspannung und AD-Wandlerkanal 0 ( ADC0 ) auswählen */
ADMUX = 0;

/* AD-Wandler einschalten und Prescaler = 64 einstellen ( enstpricht 115 khz Wandlertakt ) */
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1); // Is klar ADEN triggert eine Messung... aber wie genau
//wirkt sich der Wandlertakt aus?? Je langsamer je genauer???

/* Spezialfunktionen ausschalten */
SFIOR = 0; // Warum müssen alle Pull Up Widerstände ausgeschaltet werden?

}



Das eigentliche Prg ist aus dem Netz gesaugt. Ausprobiert habe ich es aber noch nicht. Code den ich so nicht verstehe bringt mir leider nichts.

Gruß Olli

askazo
16.03.2006, 19:07
Was ist bei der Dimensionierung zu beachten? Mal abgesehen davon, dass die Referenzspannung nicht überschritten werden darf?
Außer, dass Du auch keine negativen Eingangsspannungen haben darfst nichts weiter.


Beschaltet werden muss bei Interner Referenzspannung nur AVCC (VCC) und AGND ohne Bauteile.

Bei Externer Referenzspannung AGND AVCC (VCC) und AREF ohne Bauteile.
Um das ganze unempfindlicher gegen Rauschen zu machen, kannst Du an AVCC und AREF jeweils noch Kondensatoren gegen GND schalten. (Ich habe da immer je einen 10µF Elko und einen 100nF Keramikkondensator dran). Ist aber nicht zwingend notwendig.


AREF muss zwischen VREF (2,56V) und VCC liegen.
AREF muss zwischen 2,0V und VCC liegen.


Habe gelesen man kann nur einen der vier ADC´s nutzen?? Ist das so richtig (ATMega8)Der Mega8 hat 8 ADC-Eingangspins (beim DIP-Gehäuse nur 6) aber de fakto nur einen ADC. Über einen Multiplexer kann man einstellen, welcher Kanal eingelesen werden soll. Das heißt, man kann zwar mehrere verschiedene Signale erfassen, aber nicht genau zum selben Zeitpunkt.


Das Code-Erklären muss ich auf morgen früh verschieben, oder jemand anderes macht's. Muss jetzt weg.

Gruß,
askazo

askazo
17.03.2006, 08:27
Ok, kommen wir zum Code.
Ich fange mal mit der adc_init() an:


ADMUX = 0; // Hast Du ja völlig richtig erkannt.

ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1); //Die Bits ADEN, ADPS2 ind ADPS1 o, ADCSRA-Register werden gesetzt.
// ADEN ist nicht zum Triggern der Messung da, sondern schaltet den ADC erst mal generell ein.
// ADPS1 und 2 setzten den Wandlertakt für den Free-Running-Mode. Die Eingestellte Frequenz sagt also aus, in welchen Zeitabständen der ADC seinen Eingang abfragt und sein Ergebnis aktualisiert. Je schneller der Takt, desto genauer wird Dein Signal abgetastet. Allerdings schlägt das dann auch auf die Performance des µP, da er logischerweise weniger Zeit für andere Sachen hat. In diesem Codebeispiel ist der Free-Running-Mode allerdings ausgeschaltet, die eingestellte Frequenz hat also keine Auswirkung. Jede Messung wird manuell gestartet.

SFIOR = 0; //Ist meiner Ansicht nach überflüssig. Die PullUps und auch die anderen Einstellungen im SFIOR haben keine Auswirkung auf den ADC.

So, nun zum Hauptprogramm.


adc_init(); //Ist ja schon klar.

ADCSRA |= (1<<ADSC); //Startet eine einfache Wandlung.

while (!(ADCSRA & (1<<ADIF)) ) //Fragt das ADIF Bit ab. Solange ADIF nicht 1 ist,
; //wird die leere while-Schleife ausgeführt, also gewartet.

buffer = (ADCH<<8) | ADCL; //hier wird das Wandlungsergebnis in die Variable buffer geschrieben. Da das Ergebnis aus 2 8-Bit Registern besteht (HighByte ADCH und LowByte ADCL) wird zunächst das HighByte um 8 Stellen nach links geschoben und danach mit dem LowByte verknüpft. So steht auf den Bits 0-7 der Inhalt von ADCL und auf 8-15 der Inhalt von ADCH. Somit hat man das ursprünglich zweigeteilte Ergebnis zu einer verwertbaren Zahl zusammengefügt.

So, das war's schon. Ich hoffe, ich konnte Dir weiterhelfen.

askazo

SprinterSB
17.03.2006, 08:44
buffer = (ADCH<<8) | ADCL;
Das ist ein Kunstfehler. Die Reihenfolge, in der das ausgewertet wird, ist
AFAIK nicht spezifiziert in C, so daß ADCL vor ADCH gelesen werden kann. Der Zugriff muss aber in einer bestimmten Reihenfolge getan werden. Das erreicht man, indem man auf ADC als 16-Bit-Register zugreift, und nicht, indem man es von Hand in zwei 8-Bit-Register zerbröselt.

Klarere, einfacher zu lesen und auf jeden Fall korrekt ist also
buffer = ADC;. Schneller und codesparender ist's auch noch...

askazo
17.03.2006, 09:14
Naja, man zerbröselt es ja nicht, es sind ja von Natur aus eigentlich 2 Register...
Aber Du hast schon absolut Recht, hatte gar nicht mehr dran gedacht, dass man da eine Reihenfolge einhalten muss. Und mit dem Word-Zugriff ist's natürlich auch wesentlich einfacher und verständlicher.

askazo

tschensen
17.03.2006, 09:30
Das ist nen wichtiger Punkt, mit den Registern einlesen.
Hab ich gerade bei mir festgestellt. Mit buffer = ADC läuft das Programm einwandfrei. Anders nicht!
Hatte es auch zuerst anders ausprobiert. (Hatte den Befehl aus nem Tutorial)

Arexx-Henk
17.03.2006, 11:22
Hallo,


Laut Datenblatt sollte mann zuerst ADCL auslesen und danach ADCH.

Denn nachdem ADCH ausgelesen ist wird die ADCH/ADCL register kombination freigegeben um von die AD-Wandler wieder actualisiert zu werden.

Wenn mann zuerst ADCH ausliest, und damit die Register freigibt, konnte es sein dass inzwischen die AD-Wandler neue Daten in ADCL gespeichert hat!
Wenn mann dann ADCL ausliest ist dass die Wert van einem andere Messung!

Ich hab mich die compilierten code mahl angeschaut (GCC.EXE).

--------------------

die Code:

buffer = (ADCH<<8 ) | ADCL;

wird umgesetzt nach:

in r24,37-0x20
in r24,36-0x20

--------------------


die Code:

buffer = ADC;

wird umgesetzt nach:

in r24,36-0x20
in r25,(36)+1-0x20

--------------------

Die '36' ist ADCL und die '37' is ADCH.


Da kann mann sehen dass die code '(ADCH<<8 ) | ADCL' ein potentialen Fehler
verursachen kann denn ADCH wird zuerst gelesen und damit die Register freigegeben.


Die code 'buffer = ADC' liest zuerst ADCL und danach ADCH.

==============================
Ich benutzte immer:
buffer = ADCL;
buffer|=(ADCH<<8 );

Ich hatte die code 'buffer = ADC' noch niemahls benutzt,

Danke, schon wieder etwas gelernt!
==============================


Gruss

Henk

SprinterSB
17.03.2006, 21:09
Warum sich mühen, wenn avr-gcc die Arbeit macht...?

Gleiches gilt auch für andere 16-Bit-Register wie TCNT1, etc, sowohl für's Schreiben als auch für's Lesen.

Murphywareinoptimist
18.03.2006, 13:17
Hallo!

Danke für die Antworten. Wie es so ist haben die Antworten noch mehr Fragen aufgeworfen.
Hier erst einmal das modifiziert Programm:


#include <avr/io.h>


void adc_init(void);

unsigned int buffer;

int main(void)
{

adc_init();

while (1)
{
ADCSRA |= (1<<ADSC);
while ( !(ADCSRA & (1<<ADIF)) )
;buffer = ADC;

}

}

void adc_init(void)
{

ADMUX = 0;
ADCSRA = (1<<ADEN);

}


ADCSRA |= (1<<ADSC);

Hätte es hier nicht auch ein einfaches oder getan? Wenn nicht warum nicht?


ADMUX = 0;
Hier wird der Kanal bestimmt der gewandelt werden soll... wer lesen kann ist klar im Vorteil. Habe die DIL Variante ... habe nur PC4 & 5 ignoriert.

Wenn ich die interne Referenzspannung nutzen möchte gebe ich hier also folgendes an?

ADMUX = C0;

Das Ergebnis ist ja 10 Bit breit. Also im HSB stehen nur zwei Bit mit relevanten Daten?

Wenn ich ein 8 Bit genaues Ergebnis haben möchte (was ja die weitere Verarbeitung und Ausgabe sehr vereinfach) muss ich was tun?

O-Ton:

If the result is left adjusted and no more than 8-bit precision is required, it is sufficient to
read ADCH. Otherwise, ADCL must be read first, then ADCH, to ensure that the content
of the Data Registers belongs to the same conversion. Once ADCL is read, ADC access
to Data Registers is blocked. This means that if ADCL has been read, and a conversion
completes before ADCH is read, neither register is updated and the result from the conversion
is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is
re-enabled.

Wie kann das aber sein? Im ADCH steht doch nur ein Teil der auf 10 Bit bezogenen Messung oder??


Ich habe nur die ATMEL AppNote, Assembler Befehlscode und Beschreibung Atmega8.
Ich habe auch ein Buch über C. Was mir noch fehlt ist was über die speziellen Sachen für avr-gcc. Gibt es da was? Ich benutze zum Programmieren nur Programmers Notepad2 und gehe über eine normale ISP-Schnittstelle auf ein Experimentierboard.(Steckboard)


Gruß Olli

askazo
18.03.2006, 18:47
ADCSRA |= (1<<ADSC);

Hätte es hier nicht auch ein einfaches oder getan? Wenn nicht warum nicht?
Es ist ja ein 'einfaches' oder. Der Befehl ist eine Kurzform von
ADCSRA = ADCSRA | (1<<ADCS);

Wenn Du ein Bit setzen willst, ohne den Rest des Registers zu verändern, ist das die einfachste Möglichkeit.


ADMUX = 0;
Hier wird der Kanal bestimmt der gewandelt werden soll... wer lesen kann ist klar im Vorteil. Habe die DIL Variante ... habe nur PC4 & 5 ignoriert.

Wenn ich die interne Referenzspannung nutzen möchte gebe ich hier also folgendes an?

ADMUX = C0;
Richtig!

Das Ergebnis ist ja 10 Bit breit. Also im HSB stehen nur zwei Bit mit relevanten Daten?
In der Standart-Konfguration (ADLAR=0) auch richtig!


Wenn ich ein 8 Bit genaues Ergebnis haben möchte (was ja die weitere Verarbeitung und Ausgabe sehr vereinfach) muss ich was tun?

O-Ton:

If the result is left adjusted and no more than 8-bit precision is required, it is sufficient to
read ADCH. Otherwise, ADCL must be read first, then ADCH, to ensure that the content
of the Data Registers belongs to the same conversion. Once ADCL is read, ADC access
to Data Registers is blocked. This means that if ADCL has been read, and a conversion
completes before ADCH is read, neither register is updated and the result from the conversion
is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is
re-enabled.

Wie kann das aber sein? Im ADCH steht doch nur ein Teil der auf 10 Bit bezogenen Messung oder??
Wenn Du das ADLAR-Bit im ADMUX-Register setzt, wird das Ergebnis Links ausgerichtet. Das heißt, das im ADCH-Register die oberen 8 Bit des Ergebnisses stehen. Die unteren 2 Bit stehen dann im ADCL-Register auf Bit 6 und 7.
Wenn Du also nur 8 Bit benutzen möchtest, ist es am sinnvollsten, die Linksausrichtung einzuschalten und nur ADCH auszuwerten.


Ich habe nur die ATMEL AppNote, Assembler Befehlscode und Beschreibung Atmega8.
Ich habe auch ein Buch über C. Was mir noch fehlt ist was über die speziellen Sachen für avr-gcc. Gibt es da was? Ich benutze zum Programmieren nur Programmers Notepad2 und gehe über eine normale ISP-Schnittstelle auf ein Experimentierboard.(Steckboard)
Im WinAVR ist ein Manual als pdf enthalten, allerdings auf englisch.
Ich glaube, es gibt auch eine deutsche Übersetzung. Wo man die bekommt, kann ich Dir aus dem Stehgreif nicht sagen.

Gruß,
askazo

Murphywareinoptimist
19.03.2006, 19:05
Hi!

Danke für die Antwort ... dann ist mir das jetzt klar. Die Wandlung ist immer noch eine 10 Bit Wandlung. Nur die letzten Stellen werden einfach weggelassen.

Gruß Olli

Frostie
17.04.2006, 12:54
Hallo,

ich hätte jetzt noch eine Frage dazu.

Was soll dann in der var buffer stehen??

Ich wollte den Analogen wert in einen Digitalen umwandeln.

:

/* Wandlung starten */
ADCSRA |= (1<<ADSC); // Setzt das ADSC Bit im ADCSRA Register Also eine einfache Wandlung steht an.

/* Warten bis die AD-Wandlung abgeschloßen ist */
while ( !(ADCSRA & (1<<ADIF)) ) //Das ADCSRA wird mit dem ADIF Bit und verknüpft
//Rückgabewert 1 Wenn die Wandlung abgeschlossen ist. Duch die Negation wir die While Schleife möglich.
;

/* AD-Wert auslesen */
buffer = ADCL; Steht hier der Wert drin??? also ein wert zw. 1 und 512??


Danke

tschensen
17.04.2006, 22:22
Hoi,
also wenn ich mal von meinem AD-Wandler ausgehe, dann handelt es sich um einen 10bit wandler. D.h der Bereich zwischen 0V und Vref wird dementsprechnd in 1024 Bereiche aufgeteilt und somit in Werte von 0..1023 gewandelt.
Da die Register im Mikrocontoller aber nur eine Größe von 8 Bit haben wird der Wert auf 2 Register aufgeteilt. Und zwar in die Register ADCL und ADCH.



buffer = ADCL; Steht hier der Wert drin??? also ein wert zw. 1 und 512??


Mit diesem Befehl liest du also nur das eine Register aus und lässt die 2 höherwertigen Bits des ADCH Registers unbeachtet!!! Du erhäst somit einen 8bit Wert also zwischen 0 und 255.

SprinterSB
17.04.2006, 22:50
uint16_t buffer = ADC;

That's it. Wenn du nur ADCL liest klemmt der ADC, weil immer ein Register gelacht wird und der Wert sonst nicht mehr aktualisiert wird.

Frostie
20.04.2006, 07:57
Stimmt,

mit

buffer = ADC

gehts!!! \:D/

So, jetzt aber was anderes, ich habe ja mehrere von den Sensoren...

Mit

ADMUX = 0;

Weise ich ja dem Multiplexer den Port 0 zu, oder?

Wenn ich jetzt noch Port 1 und 2 zusätzlich haben will, muß ich dann die Ports hintereinander abfrage, soll heißen , in jedem Durchlauf Sensor 1 initen, abfragen, auswerten, dann Sensor 2 initen, abf... usw???

Danke für eure Hilfe

Gruß
Frostie