Forum: Mikrocontroller und Digitale Elektronik ADC auf DAC spiegeln Problem!


von daci (Gast)


Lesenswert?

Hallo uC-Freunde!

Ich habe folgendes Problem,

ich möchte mit einem ADC-Eingang eine Spannung einlesen und den gleichen 
Spannungswert am DAC-Ausgang ausgeben.

Leider funktioniert dies noch nicht richtig.

Ich gebe 5 V an den Eingang und erhalte am Ausgang lediglich 2,62 V. DAC 
und ADC lasse ich über die interne Vref 2,56 V laufen.

Kann mir jemand sagen, was ich in meinem Code falsch mache?!
1
void get_adc_data(void)
2
{
3
  
4
  while(1)
5
  {
6
  ADCSRA |=(1<<ADSC);//ADC_starting_conversation
7
  
8
  //adcl = ADCL;
9
  //adch = ADCH;
10
  //dacl = DACL;
11
  //dach = DACH;
12
13
//adc = adcl+adch;
14
//dac = dacl+dach;
15
  
16
  DACH = ADCH;  //ADC_H soll nach DAC_H kopiert werden
17
  DACL = ADCL;  //ADC_L soll nach DAC_L kopiert werden
18
  
19
  
20
DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation
21
  }
22
  
23
}
Vielen DANK!

Gruss

von STK500-Besitzer (Gast)


Lesenswert?

daci schrieb:
> Ich gebe 5 V an den Eingang und erhalte am Ausgang lediglich 2,62 V. DAC
> und ADC lasse ich über die interne Vref 2,56 V laufen.

Wie soll da auch (wesentlich) mehr als 2,56V rauskommen?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

daci schrieb:
> ich möchte mit einem ADC-Eingang eine Spannung einlesen
Mit welchem?
> und den gleichen Spannungswert am DAC-Ausgang ausgeben.
Mit welchem?

> 5 V an den Eingang ... Vref 2,56 V
Kann das der ADC?

von daci (Gast)


Lesenswert?

Hallo!

Kann ich dann den DAC quasi nur von 0V bis 2,56V aussteueren?

Ist also mein Code richtig?

Gruss

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:

> Ich gebe 5 V an den Eingang und erhalte am Ausgang lediglich 2,62 V. DAC
> und ADC lasse ich über die interne Vref 2,56 V laufen.

Und wie soll das gehen?
Wenn du dem ADC eine 2.56V Referenz gibst, dann kann der schon mal 
prinzipbedingt keine Spannungen größer als 2.56V feststellen. Alles was 
darüber hinausgeht ist für den ADC genau so, als ob du die 
Referenzspannung angelegt hättest.

> Kann mir jemand sagen, was ich in meinem Code falsch mache?!

>   while(1)
>   {
>   ADCSRA |=(1<<ADSC);//ADC_starting_conversation

Schön.
Denkst du nicht, dass es eine gute Idee wäre, wenn man dann auch darauf 
warten würde, dass der ADC fertig gewandelt hat, ehe man sich das 
Ergebnis abholt?

von daci (Gast)


Lesenswert?

Laut Datenblatt: • 0 - VCC ADC Input Voltage Range (VCC = 5V)

kann der uC 5V am ADC!

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:
> Laut Datenblatt: • 0 - VCC ADC Input Voltage Range (VCC = 5V)
>
> kann der uC 5V am ADC!

Ja, das kann er elektrisch ab.

Aber für das Wandlungsergebnis ist die Referenzspannung ausschlaggebend. 
Sie definiert, was für den ADC 100% (also Voll-'Ausschlag') darstellt. 
Der ADC tut im Grunde nichts anderes als die Eingangsspannung mit dieser 
Referenzspannung zu vergleichen und er liefert dir eine Angabe, wie 
dieses Verhältnis ist. Und mehr als 100% (oder eben 1023) geht nun mal 
nicht.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

daci schrieb:
> Kann ich dann den DAC quasi nur von 0V bis 2,56V aussteueren?
Vermutlich ja, aber dein Fehler passiert schon früher:
du kannst das ADC nur bis Vref aussteuern.

daci schrieb:
> Laut Datenblatt: • 0 - VCC ADC Input Voltage Range (VCC = 5V)
> kann der uC 5V am ADC!
Man kuckt nicht bei "Absolute Maximum Values"!

> kann der uC 5V am ADC!
Er kann aber nur bis zur Vref wandeln, das steht weiter hinten im 
Datenblatt...


daci schrieb:
> Ist also mein Code richtig?
Wie kann man das wissen, wenn diese Fragen noch nicht beantwortet sind:
> daci schrieb:
>> ich möchte mit einem ADC-Eingang eine Spannung einlesen
> Mit welchem?
>> und den gleichen Spannungswert am DAC-Ausgang ausgeben.
> Mit welchem?
Bitte Ankreuzen:
[ ] ist geheim!
[ ] warum willst du das wissen?
[ ] darf ich nicht sagen.
[ ] sieht man doch!

von daci (Gast)


Lesenswert?

@lkmiller

Einlesen: ADC9 PC5
Ausgeben: D2A PC7

Warum ist das so wichtig?!!!

von daci (Gast)


Lesenswert?

Hallo!

Ich habe meine Vref auf 5 V erhöht und schau an, ich kann meine 
eingelesene Spannung 1:1 ausgeben.

Jedoch müsste ich noch softwaremäßig die Wandlung und 
Registerbeschreibung abwarten und dann erst die Spannung an D2A 
ausgeben.

Kann mir jemand ein Bsp. geben, wie ich das in meinen vorhanden Code 
noch einfügen kann?

void init_adc(void)
{
  ADMUX |= 
(0<<REFS1)|(0<<REFS0)|(1<<ADLAR)|(1<<MUX3)|(0<<MUX2)|(0<<MUX1)|(1<<MUX0) 
;  //External_Referencevoltage_used //Right_Adjust_ADC_result //Select 
ADC_9
  ADCSRA |= (1<<ADEN)|(1<<ADATE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); 
//ADC_enable 
//ADC_Auto_trigger_enable//ADC_Prescaler_Division_128////ADIF/ADIE????
  ADCSRB |= 
(0<<ADTS3)|(0<<ADTS2)|(0<<ADTS1)|(0<<ADTS0);//Free_Running_Mode_enable
}

void get_adc_data(void)
{

while(1)
{

  ADCSRA |=(1<<ADSC);//ADC_starting_conversation



  DACH = ADCH;  //ADC_H soll nach DAC_H kopiert werden
  DACL = ADCL;  //ADC_L soll nach DAC_L kopiert werden


  DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation

}

}

Lieg ich richtig, wenn diese Funktion mit folgendem Register bearbeiten 
kann?

• Bit 4– ADIF: ADC Interrupt Flag
Set by hardware as soon as a conversion is complete and the Data 
register are updated with the
conversion result.
Cleared by hardware when executing the corresponding interrupt handling 
vector.
Alternatively, ADIF can be cleared by writing it to logical one.


Danke!

Gruss

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:
> @lkmiller
>
> Einlesen: ADC9 PC5
> Ausgeben: D2A PC7
>
> Warum ist das so wichtig?!!!

Das ist erst mal nicht so wichtig.
Aber: Es gibt auf der Welt mehr als nur einen Typ von ADC. Genauso wie 
es auch mehr als nur einen Typ von DAC gibt.

Bis jetzt kann mun nur raten, dass du von einem ADC sprichst, der in 
einen AVR eingebaut ist. Zumindest suggeriert das der Programmcode.
Ob dieser AVR dann auch einen DAC eingebaut hat oder nicht, kann man 
mangels Wissen um welchen AVR es sich handelt, aber nicht sagen. Die 
meisten (alle?) haben keinen DAC eingebaut, so dass man über das genaue 
Verhalten deises DAC nur raten kann.

Wenn du exakte AUskünfte willst, dann komm auch mit exakten 
Informationen rüber. Wir sitzen nicht hinter dir und sehen dir über die 
Schulter, was du vor dir am Tisch liegen hast. Auf unserer Seite des 
Monitors sieht man da eher schlecht, so dass wir des öfteren auf die 
Kristallkugel angewiesen sind. Und das willst du eigentlich nicht 
wirklich, dass wir raten müssen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Und das willst du eigentlich nicht wirklich, dass wir raten müssen.
Und noch wichtiger: wir (oder zumindest ich) wollen das auch nicht...

Am einfachsten wäre es, immer gleich einen Schaltplan mitzuposten, dann 
weiß jeder, von was da die Rede ist, und wie das Zeug zusammengeschaltet 
ist.

von daci (Gast)


Lesenswert?

Ich verwende eine AT90PWM3B - uC.

Dank eurer Tipps, funktioniert alles soweit ganz gut, bis auf diese ADIF 
Interrupt-Flag um das Wandlungsende zu erfassen.

Meine Idee:

while(1)
{
if(ADIF Flag ist gesetzt)
{
-Kopiere ADC-Register ins DAC-Register
-Rücksetzen des Interrupflag
-ADC start conversation
}
else
{
-ADC start conversation
}

DAC start conversation
}

Gruss

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:

> Lieg ich richtig, wenn diese Funktion mit folgendem Register bearbeiten
> kann?
>
> • Bit 4– ADIF: ADC Int

Lass das Interrupt Flag in Ruhe.

Du setzt das ADSC Flag auf 1 um die Wandlung zu starten und der ADC 
zieht es wieder zurück auf 0 wenn er fertig ist.

Ganz einfach und simpel.
1
  ADCSRA |=(1<<ADSC);//ADC_starting_conversation
2
  while( ADCSRA & (1<<ADSC))
3
    ;
4
  ...

AVR-GCC-Tutorial

von daci (Gast)


Lesenswert?

@kbuchegg

Habe es jetzt so gemacht:

void get_adc_data(void)
{

while(1)
{

  ADCSRA |=(1<<ADSC);//ADC_starting_conversation


  while( ADCSRA & (1<<ADSC))
  {
  DACH = ADCH;  //ADC_H soll nach DAC_H kopiert werden
  DACL = ADCL;  //ADC_L soll nach DAC_L kopiert werden
  DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation
  }


}

}

Passt das, oder soll ich die DAC-starting-conversation aus der 
while-schleife nehmen?

Vielen DANK!

Gruss

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:
> @kbuchegg
>
> Habe es jetzt so gemacht:
>
> void get_adc_data(void)
> {
>
> while(1)
> {
>
>   ADCSRA |=(1<<ADSC);//ADC_starting_conversation
>
>
>   while( ADCSRA & (1<<ADSC))
>   {
>   DACH = ADCH;  //ADC_H soll nach DAC_H kopiert werden
>   DACL = ADCL;  //ADC_L soll nach DAC_L kopiert werden

Aha.
Da steht:

  Solange der ADC #NICHT# fertig ist
  {
     Hole das Ergebnis
     und bestücke den DAC damit
  }

Das wird wohl nicht so wahnsinnig sinnvoll sein. Solange der ADC #NICHT# 
fertig ist, hast du keine sinnvollen Werte zum Arbeiten.


Das ist Punkt 1.
Punkt 2 besteht darin, dass du eine Reihenfolge beim 
Auslesen/Beschreiben von 16 Bit Registern einhalten musst.

von daci (Gast)


Lesenswert?

@kbuchegg:

Du hast mir doch folgenden Tipp gegeben, dass ich es so machen soll!

Du setzt das ADSC Flag auf 1 um die Wandlung zu starten und der ADC
zieht es wieder zurück auf 0 wenn er fertig ist.

Ganz einfach und simpel.
  ADCSRA |=(1<<ADSC);//ADC_starting_conversation
  while( ADCSRA & (1<<ADSC))
    ;
  ...



und ich habe es so übernommen!

void get_adc_data(void)
{

  ADCSRA |=(1<<ADSC);//ADC_starting_conversation


  while( ADCSRA & (1<<ADSC))
  {
  DACH = ADCH;  //ADC_H soll nach DAC_H kopiert werden
  DACL = ADCL;  //ADC_L soll nach DAC_L kopiert werden
  DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation
  }


}

Danke!

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:
> @kbuchegg:
>
> Du hast mir doch folgenden Tipp gegeben, dass ich es so machen soll!
>
> Du setzt das ADSC Flag auf 1 um die Wandlung zu starten und der ADC
> zieht es wieder zurück auf 0 wenn er fertig ist.
>
> Ganz einfach und simpel.
>   ADCSRA |=(1<<ADSC);//ADC_starting_conversation
>   while( ADCSRA & (1<<ADSC))
>     ;
>   ...
>
>
>
> und ich habe es so übernommen!

Nein hast du nicht.
Wenn du es übernommen hättest, dann hättest du geschrieben
1
   ADCSRA |=(1<<ADSC);//ADC_starting_conversation
2
   while( ADCSRA & (1<<ADSC))
3
     ;
4
5
   DACH = ADCH;  //ADC_H soll nach DAC_H kopiert werden
6
   DACL = ADCL;  //ADC_L soll nach DAC_L kopiert werden
7
   DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation

Und mit ein wenig Nachdenken, was das ADSC Flag aussagt, kann man sich 
leicht davon überzeugen, dass da steht

    Starte den ADC
    Warte, bis der ADC fertig ist
    Verarbeite den ADC Wert


Bleibt noch Punkt 2:
Du kannst nicht einfach beliebig die Register lesen/schreiben, wie du 
lustig bist. Du musst eine Reihenfolge High-Byte / Low-Byte einhalten.

Beim Auslesen von 16 Bit Register:
    erst Low Byte lesen
    dann High Byte lesen

Beim Schreiben von 16 Bit Registern
    erst High-Byte schreiben
    dann Low-Byte schreiben


Ich gehe mal davon aus, dass der gcc genauso wie für die meisten anderen 
Dinge ein Pseudo-16 Bit Register für den DAC breitstellt. Dann kümmert 
sich der Compiler bei
1
   ADCSRA |=(1<<ADSC);//ADC_starting_conversation
2
   while( ADCSRA & (1<<ADSC))
3
     ;
4
5
   DACW = ADCW;
6
   DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation

um die richtige Reihenfolge.
Ob du noch irgendwas spezielles für den DAC brauchst oder ob du dem 
ständig Werte zuweisen kannst, entzieht sich meiner Kenntnis.

von daci (Gast)


Lesenswert?

@kbuchegg:

ich habe den code jetzt soweit verstanden.

Leider verstehe ich Pkt.2 nicht, wie soll ich das machen, kannst du mir 
ein Bsp. geben?

DANKE!

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:
> @kbuchegg:
>
> ich habe den code jetzt soweit verstanden.
>
> Leider verstehe ich Pkt.2 nicht, wie soll ich das machen,


Was ist da jetzt schwer drann

    low_byte = ADCL;
    high_byte = ADCH;

    DACH = high_byte;
    DACL = low_byte;

oder eben, wenn es ein Pseudo-16 Bit Register gibt: überlass es dem 
Compiler das richtig zu machen

    DACW = ADCW;

oder

    DAC = ADC;

je nachdem, wie das Pseudo 16 Bit Register vom DAC heißt (DAC oder 
DACW). Beim ADC geht beides: sowohl ADC als auch ADCW sind gültige Namen 
für das Pseudo-16 BIt Register und der COmpiler liest es in der 
korrekten Reihenfolge getrennt nach High-Byte und Low-Byte aus.

von daci (Gast)


Lesenswert?

@kbuchegg:

Vielen Dank für deine Bemühungen!

Es funktioniert aber nur, wenn ich es so löse:

ADCSRA |=(1<<ADSC);//ADC_starting_conversation


  while( ADCSRA & (1<<ADSC))
  {
    DAC = ADCW;
    DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation
  }



und nicht wie vorgeschlagen:

ADCSRA |=(1<<ADSC);//ADC_starting_conversation


  while( ADCSRA & (1<<ADSC))
  ;
    DAC = ADCW;
    DACON |= (1<<DALA)|(1<<DAOE)|(1<<DAEN); //DAC_starting_conversation


DANKE!

von Karl H. (kbuchegg)


Lesenswert?

daci schrieb:
> @kbuchegg:
>
> Vielen Dank für deine Bemühungen!
>
> Es funktioniert aber nur, wenn ich es so löse:

ist trotzdem falsch. (Der ADC Teil). Wenn du mir nicht glaubst, dann 
sieh halt im Datenblatt nach. ADSC wird auf 0 gesetzt, wenn der ADC 
fertig ist. Der Rest folgt daraus.


Dann gibt es eben noch eine Nebenbedingung. Wahrscheinlich irgendwas im 
DAC.

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.