Forum: Mikrocontroller und Digitale Elektronik ADC an atmega128


von Florian Schuller (Gast)


Lesenswert?

Ich habe einen Atmega128 und möchte den internen ADC verwenden. Ich habe 
diese initialisiermethode aus dem Tutorial:
1
void ADC_Init(void) {
2
 
3
  uint16_t result;
4
 
5
//  ADMUX = (0<<REFS1) | (1<<REFS0);      // AVcc als Referenz benutzen
6
  ADMUX = (1<<REFS1) | (1<<REFS0);      // interne Referenzspannung nutzen
7
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
8
  // schon auf 0, also single conversion
9
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
10
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
11
 
12
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
13
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
14
 
15
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
16
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der Konvertierung warten
17
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
18
     Wandlung nicht übernommen. */
19
  result = ADCW;
20
}
Doch irgendwie bleibt er in dieser funktion stehen. Wenn ich nach aufruf 
dieser Funktion z.B. Leds einschalte passiert nichts.

Hoffentlich kann mir jemand helfen.
LG Florian Schuller

von till (Gast)


Lesenswert?

Kenn mich mit dem Atmega128 zwar nicht so gut aus... aber was ist ADCW? 
Wie viel Bit hat den der ADC? Müsste es nicht die Register ADCH und ADCL 
geben in denen das ADC Ergebnis steht? Du hast ja die Variable result, 
du erwartest also mehr als 8 bit...

Ist alles richtig verdrahtet?

Du könntest mal versuchen die LED in dieser Funktion zwischen bestimmten 
Aufrufen zu schalten um die genaue Stelle zu finden wo der Fehler 
auftritt. Ich vermute er bleibt in der while Schleife hängen...

von Karl H. (kbuchegg)


Lesenswert?

Florian Schuller schrieb:

> Doch irgendwie bleibt er in dieser funktion stehen. Wenn ich nach aufruf
> dieser Funktion z.B. Leds einschalte passiert nichts.

Diese Funktion initialisiert (schaltet ein) ja auch nur den ADC.
Zum auslesen des Wertes gibt es die ADC_Read Funktion aus dem gleichen 
Tutorial.
Im Tutorial ist auch ein komplettes main() Programm, welches zeigt wie 
man die Funktionen benutzt. Hast du das nicht gesehen?

von Karl H. (kbuchegg)


Lesenswert?

till schrieb:
> Kenn mich mit dem Atmega128 zwar nicht so gut aus... aber was ist ADCW?

ein 16 Bit Pseudoregister.

> Wie viel Bit hat den der ADC?

10, so wie alle AVR in dieser Kategorie

> Müsste es nicht die Register ADCH und ADCL
> geben in denen das ADC Ergebnis steht?

Gibt es. Brauchst du aber nicht. Genau dazu gibt es nämlich das 
Psuedoregister ADCW, damit man sich nicht mit so Kleinkram wie dem 
auselesen von 2 Registern und dem Zusammenstoppeln des Ergebnisses 
rumschlagen muss. Das kann der Compiler nämlich auch für einen machen.

von Karl H. (kbuchegg)


Lesenswert?

> Atmega128

Die M103 Fuse hast du gelöscht?

von Florian Schuller (Gast)


Lesenswert?

Ich verwende alle Funktionen aus dem Forum auch das main, doch wenn ich 
im main das initialisieren aufrufe, wird der Code danach nicht mehr 
ausgeführt.
1
   #include <avr/io.h>
2
void ADC_Init(void) {
3
 
4
  uint16_t result;
5
 
6
//  ADMUX = (0<<REFS1) | (1<<REFS0);      // AVcc als Referenz benutzen
7
  ADMUX = (1<<REFS1) | (1<<REFS0);      // interne Referenzspannung nutzen
8
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
9
  // schon auf 0, also single conversion
10
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
11
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
12
 
13
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
14
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
15
 
16
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
17
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der Konvertierung warten
18
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
19
     Wandlung nicht übernommen. */
20
  result = ADCW;
21
}
22
 
23
/* ADC Einzelmessung */
24
uint16_t ADC_Read( uint8_t channel )
25
{
26
  // Kanal waehlen, ohne andere Bits zu beeinflußen
27
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
28
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
29
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
30
  return ADCW;                    // ADC auslesen und zurückgeben
31
}
32
 
33
/* ADC Mehrfachmessung mit Mittelwertbbildung */
34
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t average )
35
{
36
  uint32_t result = 0;
37
 
38
  for (uint8_t i = 0; i < average; ++i )
39
    result += ADC_Read( channel );
40
 
41
  return (uint16_t)( result / average );
42
}
43
 
44
int main()
45
{
46
  uint16_t adcval;
47
  ADC_Init();
48
  DDRD = 0xFF;
49
  PORTD = 0x00;
50
 
51
  while( 1 ) {
52
    adcval = ADC_Read(0);  // Kanal 0
53
    // mach was mit adcval
54
 
55
    adcval = ADC_Read_Avg(2, 4);  // Kanal 2, Mittelwert aus 4 Messungen
56
    // mach was mit adcval
57
  }
58
}
Das "DDRD = 0xFF;und PORTD = 0x00; wird nicht ausgeführt. Ich 
glaube,dass er in der Schleife im ini. stehen bleibt, doch warumm?

von Florian Schuller (Gast)


Lesenswert?

Es funktioniert :-) ich habe die M103 Fuse gelöscht, doch ich bekomme 
auf ADC auf masse 0 als wert. Wenn der ADC auf irgendeinem anderen 
potential ist bekomme ich 1023. warumm?

von André R. (andr_r23)


Lesenswert?

Liegt wohl an der Referenzspannung. Welchen Wert hat die Interne 
Referenzspannung von deinem uC

von florian Schuller (Gast)


Lesenswert?

Habe die interne referenzspannung auf 5 v gesetzt. Wiviel sollte sie 
sein ?

von Karl H. (kbuchegg)


Lesenswert?

Hngt davon ab, was du messen willst.
AVcc (also deine Versorungsspannung von 5V) ist schon mal ein guter 
Wert. Kannst ja am ARef Pin mal die Spannung nachmessen (wie ist der 
ARef Pin beschaltet?)

Wie erzeugst du die Spannung am ADC Eingangs-Pin, wie stellst du die 
1023 fest?

von Florian Schuller (Gast)


Lesenswert?

>Hngt davon ab, was du messen willst

Ich will zwischen 0 und 5V messen, deshalb ist Aref auch 5V. Habe auch 
nachgemessen und es stimmt.

>Wie erzeugst du die Spannung am ADC Eingangs-Pin
Ich habe das SDK500 und lege damit die Aref fest.

>wie stellst du die 1023 fest?

Ich überprüfe wo das Messergebnis liegt(zuerst mit <,> und dann mit ==) 
und lasse dann eine LED leuchten.

von Karl H. (kbuchegg)


Lesenswert?

Florian Schuller schrieb:

> Ich will zwischen 0 und 5V messen, deshalb ist Aref auch 5V. Habe auch
> nachgemessen und es stimmt.
>
>>Wie erzeugst du die Spannung am ADC Eingangs-Pin
> Ich habe das SDK500 und lege damit die Aref fest.

Du hast am ARef Pin hoffentlich keine externe Spannung angeschlossen. 
Der Mega erzeugt sich die 5V selber und legt sie auf den Pin. Das ist 
ein Ausgang und kein Eingang.

Aber das meinte ich nicht. Wo kommt deine zu messende Spannung her? 
Netzteil, Poti, ....

>
>>wie stellst du die 1023 fest?
>
> Ich überprüfe wo das Messergebnis liegt(zuerst mit <,> und dann mit ==)
> und lasse dann eine LED leuchten.

zeigen.
Wann immer Code im Spiel ist, gibt es die Möglichkeit für Fehler. Das 
können auch ganz dumme Fehler sein. (Und ja, das passiert mir genauso, 
dass ich hier Code poste ohne ihn zu testen und ganz dumme Fehler mache. 
Und ich habe ein paar Millionen Codezeilen mehr als du programmiert)

von Florian Schuller (Gast)


Lesenswert?

>Du hast am ARef Pin hoffentlich keine externe Spannung angeschlossen.

Nein habe ich nicht

>Wo kommt deine zu messende Spannung her?

Über einen Widerstand auf VCC.


>> Ich überprüfe wo das Messergebnis liegt(zuerst mit <,> und dann mit ==)
>> und lasse dann eine LED leuchten.


>zeigen.
1
   #include <avr/io.h>
2
void ADC_Init(void) {
3
 
4
  uint16_t result;
5
 
6
  ADMUX = (0<<REFS1) | (1<<REFS0);      // AVcc als Referenz benutzen
7
  //ADMUX = (1<<REFS1) | (1<<REFS0);      // interne Referenzspannung nutzen
8
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
9
  // schon auf 0, also single conversion
10
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
11
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
12
 
13
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
14
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
15
 
16
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
17
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der Konvertierung warten
18
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
19
     Wandlung nicht übernommen. */
20
  result = ADCW;
21
}
22
 
23
/* ADC Einzelmessung */
24
uint16_t ADC_Read( uint8_t channel )
25
{
26
  // Kanal waehlen, ohne andere Bits zu beeinflußen
27
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
28
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
29
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
30
  return ADCW;                    // ADC auslesen und zurückgeben
31
}
32
 
33
/* ADC Mehrfachmessung mit Mittelwertbbildung */
34
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t average )
35
{
36
  uint32_t result = 0;
37
 
38
  for (uint8_t i = 0; i < average; ++i )
39
    result += ADC_Read( channel );
40
 
41
  return (uint16_t)( result / average );
42
}
43
 
44
int main()
45
{
46
  uint16_t adcval;
47
  ADC_Init();
48
49
 
50
  while( 1 ) {
51
    adcval = ADC_Read(0);  // Kanal 0
52
    // mach was mit adcval
53
 //int val = adcval;
54
    if(adcval==1023)
55
  {
56
    DDRD = 0xFF;
57
    PORTD = 0x00; //->LED leuchtet
58
  }
59
  }
60
}

von Karl H. (kbuchegg)


Lesenswert?

Florian Schuller schrieb:

>>Wo kommt deine zu messende Spannung her?
>
> Über einen Widerstand auf VCC.

Dann ist es klar. Mit nur einem Widerstand liegt da ja tatsächlich immer 
Vcc (also Maximalspannung) an.

Du brauchst einen Spannungsteiler!
Im einfachsten Fall ein Poti.
http://www.mikrocontroller.net/articles/AVR-Tutorial:_ADC#Beschaltung_des_ADC-Eingangs

von Florian Schuller (Gast)


Lesenswert?

Ich habe mir schnell eonen Spannungsteiler mit einem Port gebastelt, 
doch welchen wert bekomme ich bei ADC_Read zurück? Ich habe einen 
Spannung von 3 V zu messen doch der wert ligt jenseits der 3V. Kann das 
sein ,dass bei 5 V der wert 1023 ist und bei 0V 0. Somit sollte er dann 
bei 3V bei 613,8 ligen. oder?

von Karl H. (kbuchegg)


Lesenswert?

Florian Schuller schrieb:
> Ich habe mir schnell eonen Spannungsteiler mit einem Port gebastelt,
> doch welchen wert bekomme ich bei ADC_Read zurück?

Einen Wert zwischen 0 und 1023  (inklusive)

Der Wert gibt das Verhältnis an von zu messender Spannung in Bezug auf 
die Referenzspannung (aber nicht als Zahl von 0 bis 1, sondern als Zahl 
von 0 bis 1023)

> Ich habe einen
> Spannung von 3 V zu messen doch der wert ligt jenseits der 3V. Kann das
> sein ,dass bei 5 V der wert 1023 ist und bei 0V 0. Somit sollte er dann
> bei 3V bei 613,8 ligen. oder?

Die Komma vergisst du gleich wieder, aber 613 klingt gut. Allerdings 
wird der Wert um ein paar Stellen um die 613 schwanken. Von 611 bis 615 
wird da alles dabei sein. Je nachdem, wie gut die Referenzspannung 
stabilisiert ist.

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.