Forum: Compiler & IDEs Atmega 168 ADC Problem


von Rudi (Gast)


Lesenswert?

Hallo, ich möchte 3 - 4 Kanäle am AD Wandeln.
den Code aus dem Tut habe ich leicht abgewandelt und will damit testen, 
ob das Wandeln klappt. Mein Problem ist, dass es Nur bei Channel 5 
klappt, nicht aber bei Channel 4 und 3.
An Port B 1-3 habe ich einen RGB Stripe angeschlossen.
Ich habe bisher nur im Code den Channel geändert und dann aufgespielt...

Ich kann keinen Fehler finden.  Danke für die Hilfe!!
1
 /*
2
 * test.c
3
 *
4
 * Created: 03.02.2012 09:26:14
5
 *  Author: Rudi
6
 */ 
7
8
#include <avr/io.h>
9
10
// Diese Beispiel zeigt die Anwendung des ADC eines ATmega169
11
// unter Verwendung der internen Referenzspannung von nominell 1,1V.
12
// Zur Anpassung an andere AVR und/oder andere Referenzspannungen
13
// siehe Erläuterungen in diesem Tutorial und im Datenblatt
14
 
15
/* ADC initialisieren */
16
void ADC_Init(void) {
17
 
18
  uint16_t result;
19
 
20
  // interne Referenzspannung als Refernz für den ADC wählen:
21
  ADMUX = (0<<REFS1) | (1<<REFS0);
22
  
23
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
24
  // schon auf 0, also single conversion
25
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
26
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
27
 
28
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
29
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
30
 
31
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
32
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der Konvertierung warten
33
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
34
     Wandlung nicht übernommen. */
35
  result = ADCW;
36
}
37
 
38
/* ADC Einzelmessung */
39
uint8_t ADC_Read( uint8_t channel )
40
{
41
  // Kanal waehlen, ohne andere Bits zu beeinflußen
42
 
43
  ADMUX =(0b10100000|channel) ;
44
  
45
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
46
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
47
  return ADCH;                    // ADC auslesen und zurückgeben
48
}
49
 
50
/* ADC Mehrfachmessung mit Mittelwertbbildung */
51
/* beachte: Wertebereich der Summenvariablen */
52
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t nsamples )
53
{
54
  uint32_t sum = 0;
55
 
56
  for (uint8_t i = 0; i < nsamples; ++i ) {
57
    sum += ADC_Read( channel );
58
  }
59
 
60
  return (uint16_t)( sum / nsamples );
61
}
62
 
63
64
 
65
/* Beispielaufrufe: */
66
 
67
int main()
68
{
69
  DDRB=0xff;
70
  uint8_t adcval;
71
  ADC_Init();
72
 
73
  while( 1 ) {
74
    adcval = ADC_Read(3);  // Kanal 3
75
  if (adcval<=128)
76
  {
77
    PORTB=0x02;
78
  }
79
  else{
80
    PORTB=0x00;
81
  }
82
  
83
    
84
 
85
    //adcval = ADC_Read_Avg(2, 4);  // Kanal 2, Mittelwert aus 4 Messungen
86
    // mach was mit adcval
87
  }
88
}

von Stefan E. (sternst)


Lesenswert?

1
  ADMUX =(0b10100000|channel) ;
Warum stellst du hier eine andere Konfiguration ein, als bei der 
Initialisierung?
(und eine ungültige Referenz-Spannung übrigens)

von Krapao (Gast)


Lesenswert?

> Mein Problem ist, dass es Nur bei Channel 5
> klappt, nicht aber bei Channel 4 und 3.

Wundert mich, dass es bei Channel 5 klappt, denn in deinem Quelltext 
fragst du Channel 5 (und 4) gar nicht ab, sondern Channel 3:

>     adcval = ADC_Read(3);  // Kanal 3

Ansonsten besteht auch noch die Möglichkeit, dass die Schaltung falsch 
ist (Schaltplan?).

von Karl H. (kbuchegg)


Lesenswert?

Rudi schrieb:
> Hallo, ich möchte 3 - 4 Kanäle am AD Wandeln.
> den Code aus dem Tut habe ich leicht abgewandelt

Das war keine gute Idee.
Nimm doch den Code erst mal so, wie er ist. Einzig die Referenzspannung 
passt du an.

Und dann testest du erst mal diesen Code.
Und wenn der dann zur Zufriedenheit funktioniert, dann fängst du an ihn 
abzuwandeln.

Wie willst du denn unterscheiden, ob der Ursprungscode schon nicht 
funktioniert hat oder ob du Fehler eingeführt hast?

>   // Kanal waehlen, ohne andere Bits zu beeinflußen
>
>   ADMUX =(0b10100000|channel) ;


Damit hast du schon mal
* Funktionalität weggeworfen.
  Der Originalcode war insofern richtig, dass er Kanalwechsel
  korrekt ausgeführt hat.
  Deiner verändert das ADMUX Register komplett, wobei man dann
  wieder raussuchen muss, was eigentlich die anderen Bits in ADMUX
  machen. INteressiert hier doch keinen: Der Rest von ADMUX ist in
  der Init Funktion korrekt eingestellt worden. Hier soll nur
  noch der Kanal gewechselt werden

* mit der Schreibweise (kryptische Bitverschlüsselung) hast du dir
  auch keinen Gefallen getan. Der restliche Code (den du übernommen
  hast), hat überall die benannten Konstanten verwendet. Mit ein bischen
  Übung weiß man, welche Konstante was macht. Bei dir muss man erst
  mal das Datenblatt rauskramen

*   return ADCH;                    // ADC auslesen und zurückgeben

  Hast du ADLAR gesetzt? Man weiss es nicht, weil die ADMUX Zuweisung
  möglichst kryptisch geschrieben ist.
  Ist das ADLAR Bit überhaupt in ADMUX? Man weiß es nicht, weil
  die ADMUX Zuweisung möglichst kryptisch geschrieben ist.

von Rudi (Gast)


Lesenswert?

Danke für die schnelle Reaktion!

Ich habe jetzt alles zurück gesetzt, nur um das ADCH nutzen zu können 
ADLAR gesetzt.

Klappt auch, aber nur eben Channel 5.



/*
 * test.c
 *
 * Created: 03.02.2012 09:26:14
 *  Author: Rudi
 */

#include <avr/io.h>

// Diese Beispiel zeigt die Anwendung des ADC eines ATmega169
// unter Verwendung der internen Referenzspannung von nominell 1,1V.
// Zur Anpassung an andere AVR und/oder andere Referenzspannungen
// siehe Erläuterungen in diesem Tutorial und im Datenblatt

/* ADC initialisieren */
void ADC_Init(void) {

  uint16_t result;

  // interne Referenzspannung als Refernz für den ADC wählen:
  ADMUX = (1<<REFS1) | (1<<REFS0);

  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
  // schon auf 0, also single conversion
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren

  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man 
liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu 
lassen" */

  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der 
Konvertierung warten
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
     Wandlung nicht übernommen. */
  result = ADCW;
}

/* ADC Einzelmessung */
uint8_t ADC_Read( uint8_t channel )
{
  // Kanal waehlen, ohne andere Bits zu beeinflußen
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
  ADMUX=(1<<ADLAR);
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung 
warten
  return ADCH;                    // ADC auslesen und zurückgeben
}

/* ADC Mehrfachmessung mit Mittelwertbbildung */
/* beachte: Wertebereich der Summenvariablen */
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t nsamples )
{
  uint32_t sum = 0;

  for (uint8_t i = 0; i < nsamples; ++i ) {
    sum += ADC_Read( channel );
  }

  return (uint16_t)( sum / nsamples );
}



/* Beispielaufrufe: */

int main()
{
  DDRB=0xff;
  uint8_t adcval;
  ADC_Init();

  while( 1 ) {
    adcval = ADC_Read(5);  // Kanal 5
  if (adcval<=128)
  {
    PORTB=0xff;
  }
  else{
    PORTB=0x00;
  }

    // mach was mit adcval

    //adcval = ADC_Read_Avg(2, 4);  // Kanal 2, Mittelwert aus 4 
Messungen
    // mach was mit adcval
  }
}

von Rudi (Gast)


Lesenswert?

sorry, es ist
ADMUX = (0<<REFS1) | (1<<REFS0);
aber geht noch nicht...

von Stefan E. (sternst)


Lesenswert?

Rudi schrieb:
> Ich habe jetzt alles zurück gesetzt, nur um das ADCH nutzen zu können
> ADLAR gesetzt.
>
> Klappt auch, aber nur eben Channel 5.

Du veräppelst uns. Der aktuelle Code geht auch für Channel 5 garantiert 
nicht.

Das Setzen von ADLAR gehört in geeigneter Form mit nach ADC_Init().

von Rudi (Gast)


Lesenswert?

Hi,
zum testen habe ich ADC_Read();
nacheinander 5,4,3 eingesetzt, aufgespielt und getestet. Angeschissen 
habe ich an die Kanäle jeweils ein 1k Poti. Eine Reaktion bekomme ich 
aber nur an Port 5.

von Rudi (Gast)


Lesenswert?

Woher das jetzt auch kam...
Angeschissen=Angeschlossen

von Rudi (Gast)


Lesenswert?

So, ADLAR ist jetzt in der INIT...
Aber warum sollte es nicht gehen, was mache ich falsch? Was verstehe ich 
falsch???
1
/*
2
 * test.c
3
 *
4
 * Created: 03.02.2012 09:26:14
5
 *  Author: Rudi
6
 */ 
7
8
#include <avr/io.h>
9
10
// Diese Beispiel zeigt die Anwendung des ADC eines ATmega169
11
// unter Verwendung der internen Referenzspannung von nominell 1,1V.
12
// Zur Anpassung an andere AVR und/oder andere Referenzspannungen
13
// siehe Erläuterungen in diesem Tutorial und im Datenblatt
14
 
15
/* ADC initialisieren */
16
void ADC_Init(void) {
17
 
18
  uint16_t result;
19
 
20
  // interne Referenzspannung als Refernz für den ADC wählen:
21
  ADMUX = (0<<REFS1) | (1<<REFS0) | (1<<ADLAR);
22
  
23
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
24
  // schon auf 0, also single conversion
25
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
26
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
27
 
28
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
29
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
30
 
31
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
32
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der Konvertierung warten
33
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
34
     Wandlung nicht übernommen. */
35
  result = ADCW;
36
}
37
 
38
/* ADC Einzelmessung */
39
uint8_t ADC_Read( uint8_t channel )
40
{
41
  // Kanal waehlen, ohne andere Bits zu beeinflußen
42
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
43
  
44
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
45
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
46
  return ADCH;                    // ADC auslesen und zurückgeben
47
}
48
 
49
/* ADC Mehrfachmessung mit Mittelwertbbildung */
50
/* beachte: Wertebereich der Summenvariablen */
51
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t nsamples )
52
{
53
  uint32_t sum = 0;
54
 
55
  for (uint8_t i = 0; i < nsamples; ++i ) {
56
    sum += ADC_Read( channel );
57
  }
58
 
59
  return (uint16_t)( sum / nsamples );
60
}
61
 
62
63
 
64
/* Beispielaufrufe: */
65
 
66
int main()
67
{
68
  DDRB=0xff;
69
  uint8_t adcval;
70
  ADC_Init();
71
 
72
  while( 1 ) {
73
    adcval = ADC_Read(5);  // Kanal 5
74
  if (adcval<=128)
75
  {
76
    PORTB=0xff;
77
  }
78
  else{
79
    PORTB=0x00;
80
  }
81
  
82
    // mach was mit adcval
83
 
84
    //adcval = ADC_Read_Avg(2, 4);  // Kanal 2, Mittelwert aus 4 Messungen
85
    // mach was mit adcval
86
  }
87
}

von Stefan E. (sternst)


Lesenswert?

Rudi schrieb:
> Aber warum sollte es nicht gehen, was mache ich falsch? Was verstehe ich
> falsch???
1
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
2
  ADMUX=(1<<ADLAR);
1
x = 1;
2
x = 2;
Welchen Inhalt hat x nach diesen beiden Zeilen?

von Rudi (Gast)


Lesenswert?

Das war dumm, sehe ich ein.
Ging aber...

Aber warum gehen die anderen Channel nicht?

von Karl H. (kbuchegg)


Lesenswert?

Die funktionieren schon, wenn deine Hardwareanschaltung der Potis 
richtig ist.

von Rudi (Gast)


Lesenswert?

Jetzt werde ich verrückt!!!!!

Ich habe die anderen Pinne nicht verlötet...

Wird Zeit für ne Pause!!

Herzlichen Dank!

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.