Forum: Mikrocontroller und Digitale Elektronik ADC Kanalwechsel Problem - Procyon-Avrlib


von Stephan (Gast)


Lesenswert?

Hallo!

Die Funktionen für den ADC sind aus der Procyon-Library. Wenn ich
folgende Schleife ausführe, kommt als Ergebnis nur für den ersten
Messwert ein einigermassen korrektes Ergebnis. Die anderen Messwerte
verwenden anscheinend immer einen Teil der vorhergehenden Wandlung.

Verwende ich für jeden Messwert eine eigene Schleife ist das Ergebnis
weitaus besser, eine Dummykonvertierung vor dem eigentlichen Aufruf der
kty_measure() routine führt dagegen zu keiner Verbesserung.

Hat jemand eine Idee was ich da falsch mache? Ich komme da nicht weiter
:-(

Die Messwerte sind als structs definiert und mit malloc initialisiert
worden.


Hier der Code (die wichtigsten Teile):

  a2dInit();
  a2dSetPrescaler(ADC_PRESCALE_DIV32);


for(lcount = 0; lcount < 65; lcount++){
   kty_measure(TempMotor);
   kty_measure(TempController);
   kty_measure(TempFahrakku);
}

void kty_measure(measureT * m)
{
  uint16_t tmp;

  if (m->count) {
    tmp = a2dConvert10bit(m->ioPin);
    m->tval += tmp;
    m->count--;
  } else {
    m->tval = m->tval / 101;
    m->tval = m->tval - 150;
    m->value = m->tval;
    m->count = 64;
    m->tval = 82;

  }
}

Gruss,
  Stephan

von Bernhard S. (bernhard)


Lesenswert?

Hallo Stephan,

>Hat jemand eine Idee was ich da falsch mache?

Dein Programm-Code verstehe ich leider nicht,
aber ich vermute, Dein Mess-Prinzip hat einen gedanklichen Fehler.

Du machst folgendes:

Kanal umschalten
Messen
Kanal umschalten
Messen
usw.

besser wäre:

Kanal umschalten
Messen und Ergebnis verwerfen
Messen
Kanal umschalten
Messen und Ergebnis verwerfen
Messen
usw.



Bernhard

von Stephan (Gast)


Lesenswert?

Hallo Bernhard,

du meinst sicher folgendes:

 for(lcount = 0; lcount < 65; lcount++){
     a2dConvert10bit(TempMotor->ioPin);
   kty_measure(TempMotor);
     a2dConvert10bit(TempController->ioPin);
   kty_measure(TempController);
     a2dConvert10bit(TempFahrakku->ioPin);
   kty_measure(TempFahrakku);
 }

Das habe ich bereits probiert da ich den Hinweis schon irgendwo im
Forum gefunden hatte. Leider tritt dadurch keine (sichtbare)
Verbesserung ein :-(

Gruss,
  Stephan

von Bernhard S. (bernhard)


Lesenswert?

> du meinst sicher folgendes:

Da kann ich leider nicht mitdiskutieren



>Leider tritt dadurch keine (sichtbare) Verbesserung ein

Und wenn Du längere Pausen zwischen den Messungen einlegst?

Aber wenn Du nur einen einzigen Kanal misst, dann stimmte das Ergebnis?

von Stephan (Gast)


Lesenswert?

Ich denke, ich habe das Problem gefunden.

Die Eingänge für ADC1/2 (TempController/TempFahrakku) sind derzeit
offen. Ich hatte erwartet, dass beim Kanalwechsel das gleiche Ergebnis
wie bei einer Wandlung auf nur einem Kanal erscheind. DIES IST
ANSCHEINEND NICHT DER FALL grummel. Steht das im Datenblatt vom
ATmega128? Ich habs dann wohl beim überfliegen übersehen.

Wenn ich einen der nicht benutzten Messwerte auf ADC_CH_AGND lege,
stimmt das Ergebnis.

Danke Bernhard! Bin dadurch auf die Idee gekommen einen Kanal mal auf
GND zu legen.

HURRA :-)

Gruss,
   Stephan

von Bernhard S. (bernhard)


Lesenswert?

>Danke Bernhard! Bin dadurch auf die Idee gekommen einen Kanal mal auf
>GND zu legen.

Mein Bankverbindung gebe ich Dir später ;)


>Die Eingänge für ADC1/2 (TempController/TempFahrakku) sind derzeit
>offen.

Offene Eingänge sollte man vermeiden, habe auch schon diese
zeitraubende Erfahrung machen müssen.

Es entstehen undefinierte Spannungswerte an diesen Pins und diese sind
sogar noch abhängig von den Pegeln der anderen Pins anderer Ports.


Bernhard

von Michael (Gast)


Lesenswert?

Hallo,

ich weiß der Threat ist schon etwas älter ....
aber ich habe das selbe Problem nur ich verstehe die Lösung nicht denke 
ich...

Ich habe alle Pins (außer den den ich messen will) an GND angelötet.
Seltsamerweise bekomme ich aber an allen Pins den selben Messwert...

Was ist gemeint mit:

Wenn ich einen der nicht benutzten Messwerte auf ADC_CH_AGND lege,
stimmt das Ergebnis.


Was genau hast du da im Code gemacht?

Gruß
Michael

von Bernhard S. (bernhard)


Lesenswert?

@Michael


> Was genau hast du da im Code gemacht?



Kanal umschalten
Messen und Ergebnis "verwerfen"
Messen
Kanal umschalten
Messen und Ergebnis "verwerfen"
Messen
usw.


Bernhard

von Michael (Gast)


Lesenswert?

Was meinst du mit ergebniss verwerfen?

von Bernhard S. (bernhard)


Lesenswert?

>Was meinst du mit ergebniss verwerfen?

Es wird gemessen, das Messergebnis wird aber ignoriert (Dummy-Messung),

erst die nächste Messung bringt das richtige Ergebnis

von Michael (Gast)


Lesenswert?

Hallo,

hab das mal ausprobiert..
birngt keine Änderung mitsich.....
Hast du noch eine Idee?

Gruß
Michael

von Bernhard S. (bernhard)


Lesenswert?

...zeig mal den Code, sonst orakeln wir nur ;)

von Michael (Gast)


Lesenswert?

Hi,

code könnte helfen stimmt :-)

also ich hab zwei Versionen.
Die eine auf die es mir ankommt mißt nur an einem Pin.
Die andere hab ich aus dem Example der Lib und die misst alle 
AD-Eingänge.
Ich wille einen LM35 auslesen. Der ist aber im Moment noch nicht da. 
Deshalb habe ich ein Potentiometer an AD0 angeschlossen. Ich hab auch 
mit einem Messgerät nachgemessen. Es ist also alles richtig 
angeschlossen.


Die Hauptschleife sieht so aus:

while(1)
  {







      rprintf("Gemessener ADC-Wert : %d \r\n",getLM35ADCValue());


      //gemessene Spannung
      short voltage = (short)getLM35MeasuredVoltage();
      rprintf("Gemessene Spannung  : %d \r\n", voltage);

      //Gemessene Temperatur
      short temperature = (short)getLM35MeasuredTemperature();
      rprintf("Gemessene Temperatur: %d \r\n",temperature);



  }


**********************************************
**Die Methoden die ich benutzte kommen hier:**
**********************************************

void initLM35(){

  // turn on and initialize A/D converter
  a2dInit();


  // configure a2d port pin (PORTA) as input
  unsigned char  mask = ~(0x01 << LM35_ADC_CHANNEL);
  DDRA = DDRA & mask;

  // pull-up resistor off
  PORTA = PORTA & mask;


    //AD-Cahannel wählen
    a2dSetChannel (LM35_ADC_CHANNEL);


  //set prescaler
  a2dSetPrescaler(ADC_PRESCALE_DIV128);

  //set adc reference
  a2dSetReference(ADC_REFERENCE_AVCC);


}


short getLM35ADCValue(){

  short adcValue = a2dConvert10bit(LM35_ADC_CHANNEL);
  return(adcValue);


}


float getLM35MeasuredVoltage(){

  short adcValue = getLM35ADCValue();

  float measuredVoltage = (adcValue * REFERENCE_VOLTAGE)/1024;

  return(measuredVoltage);

}


float getLM35MeasuredTemperature(){

  float LM35MeasuredVoltage = getLM35MeasuredVoltage();

  float measuredTemperature = LM35MeasuredVoltage * 
DIRECTLY_PROPORTIONAL_FACTOR;

  return(measuredTemperature);


}



void a2dInit(void)
{
  sbi(ADCSR, ADEN);        // enable ADC (turn on ADC power)
  cbi(ADCSR, ADFR);        // default to single sample convert mode
  a2dSetPrescaler(ADC_PRESCALE);  // set default prescaler
  a2dSetReference(ADC_REFERENCE);  // set default reference
  cbi(ADMUX, ADLAR);        // set to right-adjusted result

  sbi(ADCSR, ADIE);        // enable ADC interrupts

  a2dCompleteFlag = FALSE;    // clear conversion complete flag
  sei();              // turn on interrupts (if not already on)
}


// configure A2D converter clock division (prescaling)
void a2dSetPrescaler(unsigned char prescale)
{
  outb(ADCSR, ((inb(ADCSR) & ~ADC_PRESCALE_MASK) | prescale));
}

// configure A2D converter voltage reference
void a2dSetReference(unsigned char ref)
{
  outb(ADMUX, ((inb(ADMUX) & ~ADC_REFERENCE_MASK) | (ref<<6)));
}

// sets the a2d input channel
void a2dSetChannel(unsigned char ch)
{
  outb(ADMUX, (inb(ADMUX) & ~ADC_MUX_MASK) | (ch & ADC_MUX_MASK));  // 
set channel
}


// Perform a 10-bit conversion
// starts conversion, waits until conversion is done, and returns result
unsigned short a2dConvert10bit(unsigned char ch)
{
  a2dCompleteFlag = FALSE;        // clear conversion complete flag
  outb(ADMUX, (inb(ADMUX) & ~ADC_MUX_MASK) | (ch & ADC_MUX_MASK));  // 
set channel
  sbi(ADCSR, ADIF);            // clear hardware "conversion complete" 
flag
  sbi(ADCSR, ADSC);            // start conversion
  //while(!a2dCompleteFlag);        // wait until conversion complete
  //while( bit_is_clear(ADCSR, ADIF) );    // wait until conversion 
complete
  while( bit_is_set(ADCSR, ADSC) );    // wait until conversion complete

  // CAUTION: MUST READ ADCL BEFORE ADCH!!!
  return (inb(ADCL) | (inb(ADCH)<<8));  // read ADC (full 10 bits);
}



Ich hoffe das ist nicht zu unübersichtlich .....

schonmal vielen Dank
Michael

von Michael (Gast)


Lesenswert?

Hallo,

ich geb ja zu es ist jede Menge code....
aber im Prinzip rufe ich nur Methoden aus der ARLib auf.
Dabei setze ich einen festen Kanal in die Methoden ein weil
der LM35 immer am selben Pin sein wird.

Ich glaube ich mache einen Fehler bei der Benutzung der AVRLib.

1) Ich muß doch eigentlich nur die Init Methode aufufen.
2)Dann setze ich die Referenzspannung
3) und den Prescaler.
4) dann setze ich noch den channel
5)Danach kann ich messen

1) a2dInit();
2)a2dSetReference(ADC_REFERENCE_AVCC);
3)a2dSetPrescaler(ADC_PRESCALE_DIV128);
4)a2dSetChannel (0);
5)a2dConvert10bit(LM35_ADC_CHANNEL);


Wo ist mein Fehler?!

Gruß
Michael

von Michael (Gast)


Lesenswert?

Hups hab mich vertippt...
meine mit ARLib natürlich AVRLib

von Michael (Gast)


Lesenswert?

LM35_ADC_CHANNEL = 0

von Michael (Gast)


Lesenswert?

Arbeitet eigentlich außer mir nochjemand mit der AVRLib von der seite 
http://hubbard.engr.scu.edu/avr/avrlib/docs/html/index.html ??

von Bernhard S. (bernhard)


Lesenswert?

... verzichte testweise mal auf "sei"


cbi(ADCSR, ADFR);        // default to single sample convert mode

ändern in:

sbi(ADCSR, ADFR)

von Michael (Gast)


Lesenswert?

Hallo,

das hat leider nichts gebracht...
ich messe jetzt ganz seltsame werte ...
an GND ist jetzt nichtmehr 0 sondern was zwischen 30 und 40 ....

Noch jemand ideen?

Gruß
Michael

von Bernhard S. (bernhard)


Lesenswert?

Du hast die "Dummy" Messung nicht gemacht, wenn ich's richtig gelesen 
habe ?

von Michael (Gast)


Lesenswert?

Hi,

das hatte ich schon probiert....
Ich hab gemessen ....  den Wert in einer Variablen gespeichert ... 
(dummy)
und dann nochmal gemessen und den Wert in einer anderen Variablen 
gespeichert.. Der Wert hat dann einfach nicht gestimmt ....
Weitere Tipps? Also wie gesgat das meißte aus meinem Code ist aus der 
AVR-Lib.
Hat damit mal jemand erfolgreich A/D gewandelt?

Gruß
Michael

von Michael (Gast)


Lesenswert?

Hallo,

ich habe eben mal den Code aus dem Tutorial verwendet um zu messen. Es 
geht immernoch nicht....
Das sollte doch heißen dass ich einen Fehler bei der Beschaltung mache 
oder?

Gruß
Michael

von Bernhard S. (bernhard)


Lesenswert?

...wie hast Du ihn beschaltet?

Wenn Du nur einen einzigen Kanal misst, klapp's dann wenigstens?

von Michael (Gast)


Lesenswert?

Hi,

ich habe einen Poti angeschlossen ....

 ATMEGA128                Poti
    GND  -------      links

    AD0  -------      mitte

    VCC  -------      rechts

AREF = AVCC = VCC
AGND = GND


Es klappt auch nicht wenn ich nur einen Kanal messe ...
Ich benutze jetzt eins zu eins den Code aus den Code aus dem 
AVR-GCC-Tutorial ....

Ich erhalte immer die selben Werte ...
Egal ob ich an einem Kanal messe ..
Oder an allen....
Ich messe immer ADCW ungefähr 450  (+/- 20)

Gruß
Michael

von Michael (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich hab das gesammte Projekt mal in ein Zip gesteckt...
Evtl hat ja jemand den Nerv es sich mal anzuschauen...
Die main steckt in a2dtest ...

Gruß
Michael

von Michael (Gast)


Lesenswert?

Hat evtl. jemand schon fertigen code zum A/D wandeln mit dem ATMEGA128 
...?
wäre schön wenn man den posten könnte ...
dann könnte ich vergleichen wo der Unterschied ist!?

von Michael (Gast)


Lesenswert?

Hat wirklich keiner eine fertige routine in C?
Ich hab in der Codesammlung leider auch nichts entsprechendes gefunden.
Sowas muß doch jemand haben oder?

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.