Forum: Compiler & IDEs ADC beim Atmega8


von Alex (Gast)


Lesenswert?

Warum steht in der Variable 'i' immer eine Null. Ich habe testweise
2,5V an den Eingang gelegt.
AREF liegt auf 5V.

#include<avr/io.h>

int main (void) {

  char i = 0;

  outp ((1<<MUX0), ADMUX);
  //Aktivierung des Pins PC1 (ADC1), an ihm soll die zu messende
Spannung liegen

  outp ((1<<ADEN) | (1<<ADPS2) | (1<<ADPS1), ADCSRA);
  //Aktivierung des ADC, festlegen eines Prescalers von 64 -->
4Mhz/64=62,5kHz

  outp ((1<<ADSC), ADCSRA);
  //Starten einer einzelnen Konvertierung

  for (i=0; i<100; i++) {
    i++;
  }
  //warte ein bischen

  x = ADCL + ADCH*256;
  //ADCH muss binär um 4 Stellen nach links geschoben werden
  //es muss immer zuerst ADCL ausgelesen werden
}

von Alex (Gast)


Lesenswert?

Statt char i steht natürlich unsigned int x in meinem Programm.

Alex

von Joerg Wunsch (Gast)


Lesenswert?

Erstens wird die Warteschleife wahrscheinlich wegoptimiert, zweitens
ist 100 ohnehin eine gewagte Annahme.  Du solltest stattdessen auf das
entsprechende Bit im Statusregister pollen.

Übrigens:

x = ADC;

geht auch. ;-)

(Der Kommentar zu ADCH ist ohnehin flasch, wenn schon, muß es um 8 bit
geschoben werden, aber eigentlich nicht wirklich geschoben...)

von Datenblatt-Hinweiser (Gast)


Lesenswert?

Wie hoch/niedrig wird denn der ADC getaktet?

Hier stehts genau drinnen:
http://www.mikrocontroller.net/datasheet.php?atmega8

Auf dieser Seite befindet sich bereits ein Code der für 4MHz läuft,
auch nicht angeschaut...

von Alex (Gast)


Lesenswert?

Besten Dank für die Tips, eigentlich habe ich das Programm fast nur mit
Hilfe des Datenblatts zum Atmega8 erstellt, das C-Programm muss ich
wohl übersehen haben ;-)

Alex

von Alex (Gast)


Lesenswert?

@Datenblatt-Hinweiser
Also in meinem datasheet ist kein C-Code Beispiel, zumindest sehe ich
keins in der ADC-Sektion.
Mein Code liefert allerdings trotz geringer Modifikationen immer noch
nicht das richtige Ergebnis ;-(

#include<avr/io.h>
#include<lcd.h>
#include<stdlib.h>

int main (void) {

  int x = 0;
  char buffer [5];
  lcd_init(LCD_DISP_ON);

  outp ((1<<MUX0), ADMUX);
  //Aktivierung des Pins PC1 (ADC1), an ihm soll die zu messende
Spannung liegen

  outp ((1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADFR), ADCSRA);
  //Aktivierung des ADC, festlegen eines Prescalers von 64 -->
4Mhz/64=62,5kHz
  //Starten einer einzelnen Konvertierung

  for (;;) {

    outp ((1<<ADSC), ADCSRA);

    while (ADCSRA & (1<<ADSC)) {}
    //warte, bis ADSC gelöscht wird --> Ende der Konvertierung

    x = ADC;
    //ADCH muss binär um 4 Stellen nach links geschoben werden
    //es muss immer zuerst ADCL ausgelesen werden

    //lcd_clrscr ();
    itoa(x, buffer, 10);
    lcd_puts(buffer);
    delay (10000);
  }
}

von Alex (Gast)


Lesenswert?

Habe mal noch
outp (0x00, DDRC);
outp (0x00, PINC);
eingefügt, das Ergebnis bleibt aber immer noch 0.

Alex

von Joerg Wunsch (Gast)


Lesenswert?

Nu ja, im Gegensatz zum ehrenwerten Datenblatthinweiser kannst Du Dir
ja zumindest einen Vornamen leisten...

Klar, was sollen Deine zusätzlichen Ausgaben auch bringen?  DDRC ist
auf 0 vorbelegt nach Reset (alles Eingang), und das PIN-Register zu
beschreiben, ist alles andere als sinnvoll...

Btw., inp() hast Du ja schon weggeworfen, vergiß auch outp() (oder
outb()), schreib einfach:

ADCSRA = ...;

Wenn Du bei AVR-GCC bleiben willst, kannst Du außerdem _BV() zur
Vereinfachung nehmen:

ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADFR);

Hmm, Du hast Dich für den free-running mode entschieden.  Anyway:

* Bit 6

   ADSC: ADC Start Conversion In Single Conversion mode, write this
   bit to one to start each conversion. In Free Running mode, write
   this bit to one to start the first conversion.

Den Kommentar dafür hast Du noch drin, aber das zugehörige

ADCSRA |= _BV(ADSC);

fehlt.  Erst danach kannst Du auf das Ergebnis warten.  Aber
eigentlich brauchst Du ja wohl den free-running mode gar nicht,
sondern kannst auch gleich jede Konvertierung einzeln starten.
Free-running ist sinnvoller zusammen mit einer Interruptroutine, die
dann eine globale Variable setzt.

von Alexander Starke (Gast)


Lesenswert?

@Joerg
Vielen Dank für die konstruktiven Tipps, allerdings geht das Proggi
noch immer nicht. Was bewirkt eigentlich:
ADCSRA |= _BV(ADSC);
Wird dadurch das Bit in ADSC gelöscht?
Habe jetzt auch mal den Analog-Pin gewechselt, man weiss ja nie ;-)
Es liegen nachweislich 2,6V an, aber die Ausgabe bleibt bei 0.
Langsam bekomme ich Selbstzweifel, soviel kann man dabei doch
eigentlich garnicht verkehrt machen...

#include<avr/io.h>

int main (void) {

  int x = 0;
  ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADSC);
  //Aktivierung des ADC,  Prescalers von 32 --> 4Mhz/32=125kHz
  //Starten einer einzelnen Konvertierung
  //Start einer Wandlung

  for (;;) {
    ADMUX = _BV(MUX0) | _BV(MUX2);
    //Aktivierung des Pins PC5 (ADC5)
    ADCSRA = _BV(ADSC);
    //Start einer Konvertierung
    while (ADCSRA & (1<<ADSC)) {}
    //warte, bis ADSC gelöscht wird
    x = ADC;
    if (x==0) {
      DDRB = 0xFF;
      PORTB = 0x00;  //--> Dioden am Port leuchten
    }
  }
}

von Joerg Wunsch (Gast)


Lesenswert?

Oh, mit Nachnamen. :-)  Danke!  (Sorry, ich finde das einfach
persönlicher so...)

> Was bewirkt eigentlich:
> ADCSRA |= _BV(ADSC);

Nun, klassisches C, bitweise Operatoren.  Ausgeschrieben wäre es

ADCSRA = ADCSRA | (1 << ADSC);

> Wird dadurch das Bit in ADSC gelöscht?

Nein, gesetzt.  Genau das sagt ja das Datenblatt auch: Bit setzen, um
die Konvertierung zu starten.

> soviel kann man dabei doch eigentlich garnicht verkehrt machen...

Na doch, man kann's immer noch verkehrt hinschreiben. ;-)

    ADCSRA = _BV(ADSC);

    //Start einer Konvertierung


Sollte wohl |= heißen.

Außerdem:

  ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADSC);


Dort setzt Du bereits ADSC.  Das ist so nicht sinnvoll, da Du den
ADMUX ja erst danach umschaltest, d. h. Du schiebst erst eine
Konvertierung an und schaltest danach den MUX um... effektiv wird die
Umschaltung dann verzögert bis zur nächsten Konvertierung.

Ich bin mir gerade auch nicht ganz sicher, ob man zwischen der Auswahl
des MUX und dem Start der Konvertierung eventuell ,,einen Moment''
Zeit vergehen lassen muß, z. B. einen NOP:

asm volatile("nop");

Bei normaler Pin-IO ist es jedenfalls so, daß die Ausgabe erst mit
leichter Verzögerung wirksam wird.

von Alexander Starke (Gast)


Lesenswert?

Danke nochmal für die zusätzliche Hilfe, ich teste es morgen mal aus.
Eventuelle Erfolgserlebnisse poste ich natürlich sofort ins Forum.

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.