Forum: Mikrocontroller und Digitale Elektronik ATMEGA32; 2 AD Wandlungen gleichzeitig laufen lassen


von Eeeeee (Gast)


Lesenswert?

Hallo zusammen, ich möchte bei einem ATMEGA32 einen Joystick 
anschließen, der ein Kettenfahrzeug antreiben soll. Allerdings scheitere 
ich gerade dabei, zwei analoge Signale gleichzeitig mit den AD Eingängen 
auszuwerten. Einen Eingang kann ich auswerten, das funktioniert, sobald 
ich aber den zweiten Eingang danach auch auslesen möchte, funktioniert 
nichts mehr. Bitte um Hilfe. Der zweite Teil der AD- Wandung der 
auskommentiert ist, funktioniert nicht. Hier die C Datei:


1
#ifndef  F_CPU
2
#define F_CPU 16000000  // Taktfrequenz = 16 MHz
3
4
#endif
5
#include <avr/io.h>
6
#include <util/delay.h>
7
#include <stdint.h>
8
9
10
float adwertx=0, adwerty=0;
11
12
13
14
int main (void)
15
{
16
  
17
  DDRB=0xff;      //alle portb´s sind ausgänge  
18
  DDRD=0xF0;      
19
  DDRA=0x00;      //alle PORTA´s sind eingänge
20
  
21
  ADCSRA=0b11111111;
22
  while(1)
23
{
24
  
25
  
26
//------------AD Wandlung--------
27
  
28
  
29
  ADMUX=0x04;
30
  
31
  adwertx=ADCL;
32
  adwertx+=(ADCH<<8);
33
34
  if (adwertx<=0x1c2)          // wert=450
35
  {
36
    PORTD=0x40;          //Motor1 linkslauf ; PORTD6=1
37
    PORTB=0x02;
38
  }
39
40
  if (adwertx>0x1c2&&adwertx<0x28a)
41
  {
42
    PORTD=0x00;
43
    PORTB=0x00;
44
  }
45
46
  if (adwertx>=0x28a)          //wert=650
47
  {
48
    PORTD=0x80;            //Motor1 rechtslauf; PORTD7=1
49
    PORTB=0x04;
50
  }
51
52
53
54
55
/*
56
ADMUX=0x05;
57
58
adwerty=ADCL;
59
adwerty+=(ADCH<<8);
60
61
if (adwerty<=0x1c2)          // wert=450
62
{
63
OCR1A=0x78;    
64
}
65
66
if (adwerty>0x1c2&&adwerty<0x28a)
67
{
68
  PORTB=0x00;
69
  PORTD=0x00;  
70
}
71
72
73
if (adwerty>=0x28a)          //wert=650
74
{
75
OCR1B=0x78;        
76
}
77
78
*/
79
}
80
}

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Einen Eingang kann ich auswerten, das funktioniert, sobald
>ich aber den zweiten Eingang danach auch auslesen möchte, funktioniert
>nichts mehr.

Dann zeig mal die Stellen im Programm, wo die einzelnen AD-Wandlungen 
gestartet werden und auf das Ende der Wandlungen gewartet wird.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Warum tust du dir nicht selbst einen Gefallen und benutzt die Routinen 
aus dem AVR-GCC-Tutorial?
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Analoge_Ein-_und_Ausgabe
Die würden funktionieren.
Mit so etwas
1
  ADCSRA=0b11111111;
tust du dir keinen Gefallen. Das ist nicht mehr durchschaubar, was du da 
eigentlich alles einschaltest bzw. aktivierst.

Nach Einbindung der ADC Routinen aus dem Tutorial (und Anpassung der 
Einstellung für die Referenzspannung), könnte dein Code so einfach sein 
wie
1
....
2
3
int main()
4
{
5
  uint16_t value_x, value_y;
6
7
  ADC_Init();
8
9
  while( 1 ) {
10
    value_x = ADC_Read( 4 );
11
    value_y = ADC_Read( 5 );
12
13
    if( value_x < 200 ..... werte die Werte aus
14
15
  }
16
}

: Bearbeitet durch User
von H.Joachim S. (crazyhorse)


Lesenswert?

"Gleichzeitig" geht sowieso nicht, da es nur einen Wandler gibt :-).
Du meinst wahrscheinlich kurz nacheinander.
Vor dem Auslesen musst du wartennnnnnnnnnnnn, bis der Wandler fertig 
ist.

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=(adc_input & 0x0f) | ADC_VREF_TYPE;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}

von Karl H. (kbuchegg)


Lesenswert?

Noch ein Hinweis.

So etwas
1
  if (adwertx<=0x1c2)          // wert=450

ist Unsinn. Wenn du wissen willst, ob der WErt kleiner/gleich 450 ist, 
dann schreib das auch so hin
1
  if( adcwertx <= 450 )

wo liegt der Sinn, im C Code die Zahlenwerte als Hex-Werte anzuschreiben 
und dann im Kommentar den Dezimalwert dazu anzugeben?
Ob du eine Hex-Schreibweise benutzt oder die Zahlen als Dezimalzahl 
anschreibst, ist eine reine Frage der Schreibweise. In der Bedeutung 
sind beide aber gleich: beides sind Zahlen und 0x1c2 bzw. dezimal 450 
sind dieselbe Zahl.

von Felix P. (fixxl)


Lesenswert?

Solche Geschichten sind auch immer fehlerträchtig:
1
ADMUX=0x04;
2
3
...
4
5
ADMUX=0x05;

Das mag in deinem speziellen Fall gut gehen, wenn du mit der internen 
Referenzspannung und rechtsbündigem Ergebnis (Niedrigste 8 bit in ADCL, 
obere zwei Bit in ADCH) messen willst, ansonsten sind derartige 
Holzhammerzuweisungen eher zu vermeiden.

Würde dir eher zu folgender Vorgehensweise raten:
1
ADMUX &= 0xE0; // Oberste drei Bit bleiben bestehen, MUX4:0 werden 0
2
ADMUX |= 0x04; // Kanal 4 einstellen

Und das gleiche für Kanal 5:
1
ADMUX &= 0xE0; // Oberste drei Bit bleiben bestehen, MUX4:0 werden 0
2
ADMUX |= 0x05; // Kanal 5 einstellen

: Bearbeitet durch User
von Eeeeee (Gast)


Lesenswert?

Ich danke euch für die Antworten, ich habe alle Kommentare beachtet und 
das Programm neu überschreiben, indem ich Funktionen aus dem Tutorial 
was mir empfohlen wurde benutzt habe und die main Funktion daraufhin neu 
geschrieben habe. Allerdings Funktioniert immer nur ein Teil der 
Funktion, entweder die value_x ODER die value_y, beides leider nicht. 
Könnt ihr mir weiterhelfen?



1
#ifndef  F_CPU
2
#define F_CPU 16000000  // Taktfrequenz = 16 MHz
3
4
#endif
5
#include <avr/io.h>
6
#include <util/delay.h>
7
#include <stdint.h>
8
9
10
11
12
uint16_t ADC_Read ( uint8_t channel );
13
void ADC_Init (void);
14
int main (void)
15
{
16
  
17
  DDRB=0xff;      //alle portb´s sind ausgänge  
18
  DDRD=0xF0;      
19
  DDRA=0x00;      //alle PORTA´s sind eingänge
20
  
21
  TCCR1B=0x03;    
22
  OCR1B=0xF0;
23
  TCCR1A=0xA1;    
24
  OCR1A=0xF0;
25
26
  while(1)
27
{
28
  
29
  
30
    uint16_t value_x, value_y;
31
32
    ADC_Init();
33
34
      value_x = ADC_Read( 4 );
35
      
36
        if ( value_x <= 450 )
37
        {
38
          PORTD=0x40;            //Motor1 linkslauf ; PORTD6=1
39
          PORTB=0x02;
40
        }
41
42
43
        if ( value_x >= 650 )
44
        {
45
          PORTD=0x80;            //Motor1 rechtslauf; PORTD7=1
46
          PORTB=0x04;
47
        }
48
            
49
        if ( value_x <= 650 && value_x >= 450 )
50
        {
51
          PORTD=0x00;            //Motor1 rechtslauf; PORTD7=1
52
          PORTB=0x00;
53
        }
54
55
56
  value_y = ADC_Read( 5 );
57
58
      
59
        if ( value_y <= 450 )
60
        {
61
          PORTD=0x80;            //Motor1 linkslauf ; PORTD6=1
62
          
63
        }
64
65
        if ( value_y >= 650 )
66
        {
67
          
68
          PORTB=0x04;
69
        }
70
            
71
        if ( value_y <= 650 && value_y >= 450 )
72
        {
73
          PORTD=0x00;            //Motor1 rechtslauf; PORTD7=1
74
          PORTB=0x00;
75
        }
76
    
77
    
78
}
79
  
80
}
81
82
void ADC_Init(void) 
83
{
84
 
85
  // die Versorgungsspannung AVcc als Refernz wählen:
86
  ADMUX = (1<<REFS0);    
87
  // oder interne Referenzspannung als Referenz für den ADC wählen:
88
  // ADMUX = (1<<REFS1) | (1<<REFS0);
89
 
90
  // Bit ADFR ("free running") in ADCSRA steht beim Einschalten
91
  // schon auf 0, also single conversion
92
  ADCSRA = (1<<ADPS1) | (1<<ADPS0);     // Frequenzvorteiler
93
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
94
 
95
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
96
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
97
 
98
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
99
  while (ADCSRA & (1<<ADSC) ) {         // auf Abschluss der Konvertierung warten
100
  }
101
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
102
     Wandlung nicht übernommen. */
103
  (void) ADCW;
104
}
105
106
107
/* ADC Einzelmessung */
108
uint16_t ADC_Read ( uint8_t channel )
109
{
110
  // Kanal waehlen, ohne andere Bits zu beeinflußen
111
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
112
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
113
  while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
114
  }
115
  return ADCW;                    // ADC auslesen und zurückgeben
116
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Und noch ein guter Rat.

Lerne mit Einzelbitoperationen umzugehen!
Das ist eigentlich so ziemlich das wichtigste, wenn man seine 
µC-Karriere beginnt.

Denn hier
1
         PORTD=0x00;            //Motor1 rechtslauf; PORTD7=1
2
          PORTB=0x00;
setzt du alle 8 Bits vom Port auf 0. Auch die die für deinen Motor gar 
nicht zuständig sind!

Bit setzen
1
   Register |= ( 1 << Bitnummer );
Bit löschen
1
   Register &= ~( 1 << Bitnummer );

und wenn du dann noch mittels einem #define dem Bit einen schönen 
'Namen' gibts, dann wird das ganze auch noch übersichtlich und leicht zu 
lesen.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Siehe Bitmanipulation. Mehr gibt es da nicht zu sagen oder 
diskutieren.

von Eeeeee (Gast)


Lesenswert?

Danke erstmal für eure Hilfe, habe jetzt nocheinmal alles umgeschrieben, 
indem ich die Bits der Ports jetzt einzeln setzte und lösche. Allerdings 
ist irgendetwas noch falsch, vielleicht die schreibweise mit dem value_x 
oder value_y ? Wenn ich value_x auskommentiere, funktioniert value_y 
einwandfrei, andersrum genauso, beides zusammen immernoch nicht. Woran 
liegt das?
1
while(1)
2
{
3
  
4
  
5
    uint16_t value_x, value_y;
6
7
    ADC_Init();
8
value_x = ADC_Read( 4 );
9
      
10
        if ( value_x <= 450 )
11
        {
12
          PORTD |= ( 1 << 6 );      //PORTD=0x40;  Motor1 linkslauf ; 
13
          PORTB |= ( 1 << 1 );      //PORTB=0x02;  
14
          PORTD &= ~( 1 << 7 );
15
          PORTB &= ~( 1 << 2 );
16
        }
17
18
19
        if ( value_x >= 650 )
20
        {
21
          //PORTD=0x80;            //Motor1 rechtslauf; PORTD7=1
22
          //PORTB=0x04;
23
          PORTD |= ( 1 << 7 );      
24
          PORTB |= ( 1 << 2 );
25
          PORTD &= ~( 1 << 6 );
26
          PORTB &= ~( 1 << 1 );
27
        }
28
            
29
        if ( value_x <= 650 && value_x >= 450 )
30
        {
31
          PORTD &= ~( 1 << 7 );
32
          PORTB &= ~( 1 << 1 );
33
          PORTD &= ~( 1 << 6 );
34
          PORTB &= ~( 1 << 2 );
35
          //PORTD=0x00;            //Motor1 rechtslauf; PORTD7=1
36
          //PORTB=0x00;
37
        }
38
  _delay_ms(10);
39
40
  value_y = ADC_Read( 5 );
41
42
      
43
        if ( value_y <= 450 )
44
        {
45
          PORTD |= ( 1 << 7 );      //PORTD=0x80;  Motor1 linkslauf 
46
          PORTD &= ~( 1 << 6);
47
        }
48
49
        if ( value_y >= 650 )
50
        {
51
          
52
          PORTB |= ( 1 << 2 );                //PORTB=0x04;
53
          PORTB &= ~( 1 << 1);
54
        }
55
            
56
        if ( value_y <= 650 && value_y >= 450 )
57
        {
58
          PORTD &= ~( 1 << 7 );
59
          PORTB &= ~( 1 << 1 );
60
          PORTD &= ~( 1 << 6 );
61
          PORTB &= ~( 1 << 2 );
62
63
          //PORTD=0x00;            //Motor1 rechtslauf; PORTD7=1
64
          //PORTB=0x00;
65
        }
66
    
67
    _delay_ms(10);
68
69
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Wieviele Motoren hast du eigentlich?
Und warum setzt du da manchmal 4 Bits, manchmal nur 2?

Ich denke, dein Hauptproblem ist, dass dir eigentlich nicht wirklich 
klar ist, wie die beiden Knüppel auf den/die Motor(en) einwirken sollen.
IN deinem Code macht der Y Knüppel jeweils das rückgängig, was der X 
Knüppel eingestellt hat und umgekehrt.

: Bearbeitet durch User
von Felix P. (fixxl)


Lesenswert?

Die ADC_Init() solltest du aus der Endlosschleife herausnehmen. Es ist 
nicht nötig, den ADC bei jedem Schleifendurchlauf neu zu initialisieren, 
das reicht einmal.

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.