Forum: Mikrocontroller und Digitale Elektronik Atmega8 mehrere ADC verwenden


von Robotico (Gast)


Lesenswert?

Hallo,

ich hab ein Problem damit, mehrere A/D-Wandler gleichzeitig zu 
verwenden.
Habe gelesen, dass man das ADMUX nur umschalten sollte, wenn die Messung 
fertig ist, also hab ich das versucht folgendermaßen zu lösen:

1
while(1)
2
    {
3
    if (ad_con == 2)
4
    {
5
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX3) | (0<<MUX2) | (1<<MUX1) | (0<<MUX0);
6
      ADCSRA = (1<<ADEN) | (1<<ADSC) | (0<<ADFR) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
7
      adc_x = ADCW;
8
      itoa(adc_x,Buffer,10);
9
      lcd_setcursor(3,1);
10
      lcd_string(Buffer);
11
      lcd_data(' ');
12
    }
13
    
14
    if (ad_con == 3)
15
    {
16
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX3) | (0<<MUX2) | (1<<MUX1) | (1<<MUX0);
17
      ADCSRA = (1<<ADEN) | (1<<ADSC) | (0<<ADFR) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
18
      adc_y = ADCW;
19
      itoa(adc_y,Buffer,10);
20
      lcd_setcursor(3,2);
21
      lcd_string(Buffer);
22
      lcd_data(' ');
23
    }
24
    
25
    if (ADIF == 1)
26
    {
27
      ad_con += 1;
28
      
29
      if (ad_con == 4)
30
      {
31
        ad_con = 2;
32
      }
33
    }
34
    
35
    }


ad_con ist der gewählte Pin. Bisher möchte ich erstmal nur zwischen 2 
Pins umschalten, aber das soll noch auf mehrere erweitert werden können. 
Hier verwende ich jetzt Eingang ADC2 und ADC3.

Das Problem ist, es scheint, als würde der µC gar nicht in die zweite 
Verzweigung gehen, er schaltet also gar nicht um. Ich frage mich nur 
warum, da beim Beenden der Messung der ADIF Pin ja auf 1 geschalten wird 
und somit ad_con 1 hochgezählt werden sollte.

Ich rätsel da gerade ein bisschen rum, da ich nicht genau weiß, wie man 
das am besten macht. So eine richtige Lösung habe ich beim Googeln nicht 
gefunden, nur Ansätze wie es gehen könnte.

von spess53 (Gast)


Lesenswert?

Hi

>    if (ADIF == 1)

Wenn du ADIF nicht wieder manuell löschst (Beschreiben mit einer 1) 
bleibt es nach der ersten Messung immer auf 1. Allerdings ist es besser 
du überprüfst ADSC. Das wird nach der Messung wieder Null.

MfG Spess

von Rainer B. (katastrophenheinz)


Lesenswert?

Hi, die gröbsten Schnitzer: ADIF ist eine Konstante, die sich nie 
ändert, also wird deine if-Bedingung nie wahr.
Du willst wahrscheinlich die entsprechende Bitposition in ADCSRA 
abfragen.
Das ginge über "if ( ADCSRA & ( 1 << ADIF ) )
Aber auch dann ist dein code noch falsch. Du musst nach dem Start einer 
Wandlung warten, bis das ADIF-Bit 1 wird, erst dann ist das Ergebnis 
deiner Wandlung fertig. Und danach musst du das ADIF-Bit löschen, indem 
du eine 1 reinschreibst. Also fürs erste:
1
 
2
while(1) {
3
    if (ad_con == 2)
4
    {
5
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX3) | (0<<MUX2) | (1<<MUX1) | (0<<MUX0);
6
      ADCSRA = (1<<ADEN) | (1<<ADSC) | (0<<ADFR) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
7
      
8
      while ( ( ADCSRA & ( 1 << ADIF )) == 0) ; 
9
10
      adc_x = ADCW;
11
      itoa(adc_x,Buffer,10);
12
      lcd_setcursor(3,1);
13
      lcd_string(Buffer);
14
      lcd_data(' ');
15
    }
16
    
17
    if (ad_con == 3)
18
    {
19
      ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX3) | (0<<MUX2) | (1<<MUX1) | (1<<MUX0);
20
      ADCSRA = (1<<ADEN) | (1<<ADSC) | (0<<ADFR) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
21
22
      while ( ( ADCSRA & ( 1 << ADIF )) == 0) ; 
23
 
24
      adc_y = ADCW;
25
      itoa(adc_y,Buffer,10);
26
      lcd_setcursor(3,2);
27
      lcd_string(Buffer);
28
      lcd_data(' ');
29
    }
30
31
    ADCSRA = ( 1 << ADIF );
32
    if (++ad_con == 4) ad_con = 2;
33
    
34
}
Ansonsten wirf mal einen Blick in das Atmega-Manual zum Thema ADC.
Gruss, Rainer

von Robotico (Gast)


Lesenswert?

spess53 schrieb:
> Hi
>
>>    if (ADIF == 1)
>
> Wenn du ADIF nicht wieder manuell löschst (Beschreiben mit einer 1)
> bleibt es nach der ersten Messung immer auf 1. Allerdings ist es besser
> du überprüfst ADSC. Das wird nach der Messung wieder Null.
>
> MfG Spess

Im AVR-GCC Tutorial steht, dass ADIF automatisch wieder gelöscht wird, 
sobald lesend auf das Inhaltsregister des ADC zugegriffen wird.


@Rainer B:

Was bedeutet denn
1
while ( ( ADCSRA & ( 1 << ADIF )) == 0) ;
?
Ich kenne while nur als Schleife mit while (Bedingung {Anweisungen;}
So hab ich das Konstrukt noch nie gesehen.
Laut dem AVR-GCC Tutorial ist ADIF aber keine Konstante, sondern wie 
schon gesagt, steht dort, das Bit wird automatisch wieder gelöscht, wenn 
lesend auf das Inhaltsregister zugegriffen wird und das tue ich ja mit
1
adc_x = ADCW;

von TriHexagon (Gast)


Lesenswert?

Robotico schrieb:
> Was bedeutet dennwhile ( ( ADCSRA & ( 1 << ADIF )) == 0) ; ?
> Ich kenne while nur als Schleife mit while (Bedingung {Anweisungen;}
> So hab ich das Konstrukt noch nie gesehen.

Sowas sieht man bei µC täglich. Warum sollte das keine Bedingung sein?

von Uwe (de0508)


Lesenswert?

Hallo  Robotico,

deine Annahme ist falsch, im Datenblatt des AVR m168 steht:
1
Bit 4 – ADIF: ADC interrupt flag
2
This bit is set when an ADC conversion completes and the Data Registers are updated. The
3
ADC Conversion Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set.
4
ADIF is cleared by hardware when executing the corresponding interrupt handling vector.
5
Alter-natively, ADIF is cleared by writing a logical one to the flag. 
6
Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt can be disabled.
7
This also applies if the SBI and CBI instructions are used.

von Max H. (hartl192)


Lesenswert?

Robotico schrieb:
> Laut dem AVR-GCC Tutorial ist ADIF aber keine Konstante
"ADIF" ist als konstante im header definiert. Diese konstante sagt 
welches bit im ADCSRA das Interrupt Flag ist: In diesem ist es das 4. 
Bit.


( ADCSRA & ( 1 << ADIF ))
Ist dann (1 << ADIF)=0b00010000 oder 0, je nachdem ob das Flag gesetzt 
ist oder nicht. Der Ausdruck ist also TRUE, wenn das Flag gesetzt ist 
und FALSE wenn nicht.

von Robotico (Gast)


Lesenswert?

Uwe S. schrieb:
> Hallo  Robotico,
>
> deine Annahme ist falsch, im Datenblatt des AVR m168 steht:
> Bit 4 – ADIF: ADC interrupt flag
> This bit is set when an ADC conversion completes and the Data Registers
> are updated. The
> ADC Conversion Complete Interrupt is executed if the ADIE bit and the
> I-bit in SREG are set.
> ADIF is cleared by hardware when executing the corresponding interrupt
> handling vector.
> Alter-natively, ADIF is cleared by writing a logical one to the flag.
> Beware that if doing a Read-Modify-Write on ADCSRA, a pending interrupt
> can be disabled.
> This also applies if the SBI and CBI instructions are used.

Ah okay, dann steht es im Tut aber etwas anders.

Okay alles klar, danke. Ich werds dann mal so testen.

von Robotico (Gast)


Lesenswert?

TriHexagon schrieb:
> Robotico schrieb:
>> Was bedeutet dennwhile ( ( ADCSRA & ( 1 << ADIF )) == 0) ; ?
>> Ich kenne while nur als Schleife mit while (Bedingung {Anweisungen;}
>> So hab ich das Konstrukt noch nie gesehen.
>
> Sowas sieht man bei µC täglich. Warum sollte das keine Bedingung sein?

Ich sag doch nicht, dass es keine Bedingung ist. Es ist nur keine 
Anweisung da. So ganz versteh ich das Konstrukt noch nicht.

von Robotico (Gast)


Lesenswert?

Die Lösung von Rainer B. scheint auch nicht so ganz zu funktionieren.

Ich stehe gerade ehrlich gesagt ein bisschen auf dem Schlauch und ich 
weiß auch nicht so genau, nach was ich suchen soll.

Also mir ist klar, dass erstmal eine Messung fertig abgeschlossen sein 
muss, bevor man das ADMUX Register auf den anderen Pin umstellt. Aber 
wie ich das abfrage genau, ist mir unklar und ich weiß leider auch 
nicht, was dieses while-Konstrukt aussagt.

von Thomas E. (thomase)


Lesenswert?

Robotico schrieb:
> und ich weiß leider auch
> nicht, was dieses while-Konstrukt aussagt.

das ist das gleiche wie
1
(( ADCSRA & ( 1 << ADIF)) == 0)
2
{
3
4
}

Ist das jetzt klarer? Es wird der Code in den Klammern ausgeführt, der 
hinter dem while(..) steht. In diesem Fall wird nichts ausgeführt, da 
nichts drin steht. Der Sinn ist, einfach nur zu warten, bis sich ADIF 
geändert hat.

Vereinfacht schreibt man das dann so:
1
( ( ADCSRA & ( 1 << ADIF )) == 0) ;
oder gebräuchlicher so:
1
(!(ADCSRA & ( 1 << ADIF )));

> Die Lösung von Rainer B. scheint auch nicht so ganz zu funktionieren.

Da wird das ADIF ja auch nicht gelöscht.
Normalerweise fragt man ohnehin das ADSC-Flag ab.

Man setzt das ADSC und startet die Wandlung:
1
ADCSRA |= (1 << ADSC);
2
//dann wartet man bis die Wandlung fertig ist:
3
while(ADCSRA & (1 << ADSC));
4
//Und kann jetzt das ADC auslesen und den Kanal wechseln.

Bei diesem Flag braucht man sich nicht um das Löschen zu kümmern.

>Ah okay, dann steht es im Tut aber etwas anders.

> Laut dem AVR-GCC Tutorial ist ADIF aber keine Konstante, sondern wie
> schon gesagt, steht dort, das Bit wird automatisch wieder gelöscht, wenn
> lesend auf das Inhaltsregister zugegriffen wird und das tue ich ja mit

Das stand bis eben tatsächlich so da drin. Ich habe mir erlaubt, das 
rauszuschmeissen,

mfg.

von Rainer B. (katastrophenheinz)


Lesenswert?

Robotico schrieb:
> Was bedeutet denn while ( ( ADCSRA & ( 1 << ADIF )) == 0) ;

könnte man auch so schreiben:
1
while ( ( ADCSRA & ( 1 << ADIF )) == 0) {
2
 /* tue nichts */
3
}
Bedeutet: Wiederhole die Überprüfung, ob ADIF in ADCSRA 0 ist, solange 
bis ADIF nicht mehr 0 ist.

Poste mal deinen gerade aktuellen Code

Gruss, Rainer

von Rainer B. (katastrophenheinz)


Lesenswert?

Thomas Eckmann schrieb:
> Da wird das ADIF ja auch nicht gelöscht.
Hi, was macht denn deiner Meinung nach diese Anweisung
1
ADCSRA = ( 1 << ADIF );
Gruss, Rainer

von Thomas E. (thomase)


Lesenswert?

Rainer B. schrieb:
> Thomas Eckmann schrieb:
>> Da wird das ADIF ja auch nicht gelöscht.
> Hi, was macht denn deiner Meinung nach diese Anweisung

Jetzt komm mir nicht so blöd. Das habe ich übersehen.

mfg.

von Robotico (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> das ist das gleiche wie(( ADCSRA & ( 1 << ADIF)) == 0)
> {
>
> }
>
> Ist das jetzt klarer? Es wird der Code in den Klammern ausgeführt, der
> hinter dem while(..) steht. In diesem Fall wird nichts ausgeführt, da
> nichts drin steht. Der Sinn ist, einfach nur zu warten, bis sich ADIF
> geändert hat.

Ja logisch, so leuchtets mir ein.
Danke!


Rainer B. schrieb:
> Poste mal deinen gerade aktuellen Code

Es funktioniert jetzt. Hatte leider auch einen "Hardwarefehler", bzw. 
habe einen Pin auf der Platine nicht richtig verbunden gehabt, so lagen 
an dem einen Trimmwiderstand nicht 0 und 5 Volt an, sondern 0 und 
nichts. Logisch, dass der eine Kanal dann nichts brauchbares ausgibt. 
Hab es jetzt nach dem Lösungsvorschlag von Thomas Eckmann gemacht:

Thomas Eckmann schrieb:
> ADCSRA |= (1 << ADSC);
> //dann wartet man bis die Wandlung fertig ist:
> while(ADCSRA & (1 << ADSC));
> //Und kann jetzt das ADC auslesen und den Kanal wechseln.

Hier mein Code:
1
/*
2
 * Joystick.c
3
 *
4
 * Created: 20.04.2014 13:29:57
5
 *  Author: Stefan
6
 */ 
7
8
#include <avr/io.h>
9
#include "lcd-routines.h"
10
#include <stdint.h>
11
#include <stdlib.h>
12
#include <util/delay.h>
13
14
15
int main(void)
16
{
17
  lcd_init();
18
  lcd_setcursor(0,1);
19
  lcd_string("X:");
20
  lcd_setcursor(0,2);
21
  lcd_string("Y:");
22
  
23
  uint16_t adc_x;
24
  uint16_t adc_y;
25
  char Buffer[20];
26
  
27
  uint8_t ad_con;
28
  ad_con = 2;
29
30
  ADCSRA = (1<<ADEN) | (1<<ADSC) | (0<<ADFR) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
31
  ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX3) | (0<<MUX2) | (1<<MUX1) | (0<<MUX0);
32
  
33
    while(1)
34
    {
35
    ADCSRA |= (1 << ADSC);
36
    
37
    while(ADCSRA & (1 << ADSC))
38
      {
39
      }
40
    
41
    switch (ad_con)
42
    {
43
      case 2:
44
        adc_x = ADCW;
45
        itoa(adc_x,Buffer,10);
46
        lcd_setcursor(3,1);
47
        lcd_string(Buffer);
48
        lcd_data(' ');
49
        break;
50
      case 3:
51
        adc_y = ADCW;
52
        itoa(adc_y,Buffer,10);
53
        lcd_setcursor(3,2);
54
        lcd_string(Buffer);
55
        lcd_data(' ');
56
        break;
57
    }    
58
    
59
    ad_con += 1;
60
      
61
    if (ad_con == 4)
62
      {
63
        ad_con = 2;
64
      }
65
      
66
    switch (ad_con)
67
    {
68
      case 2:
69
        ADMUX = (1<<MUX1) | (0<<MUX0);
70
        break;
71
      case 3:
72
        ADMUX = (1<<MUX1) | (1<<MUX0);
73
        break;
74
    }        
75
    }
76
}

Kann sicherlich noch optimiert werden!

Es handelt sich übrigens um einen Joystick (aus PS2 Controller), den ich 
auswerten möchte. Der ist ganz simpel mit jeweils einem Trimmwiderstand 
in X- und Y-Achse realisiert.


Danke an alle für die Hilfe!



Thomas Eckmann schrieb:
> Das stand bis eben tatsächlich so da drin. Ich habe mir erlaubt, das
> rauszuschmeissen,

Danke! Ist doch schön, wenn dabei noch Fehler verbessert werden.

von Karl H. (kbuchegg)


Lesenswert?

Robotico schrieb:

> Kann sicherlich noch optimiert werden!

Du meinst verbessert.
ZB. mit den Funktionen aus dem AVR-GCC_Tutorial.

Dann schreibt sich dein Code als
1
void lcd_puti( uint8_t col, uint8_t row, uint16_t value )
2
{
3
  char buff[15];
4
  sprintf( buff, "%5d", value );
5
6
  lcd_setcursor( col, row );
7
  lcd_string( buff );
8
}
9
10
int main(void)
11
{
12
  lcd_init();
13
  lcd_setcursor(0,1);
14
  lcd_string("X:");
15
  lcd_setcursor(0,2);
16
  lcd_string("Y:");
17
  
18
  uint16_t adc_x;
19
  uint16_t adc_y;
20
21
  ADC_init();
22
  
23
  while(1)
24
  {
25
    adc_x = ADC_read( 2 );
26
    adc_y = ADC_read( 3 );
27
28
    lcd_puti( 3, 1, adc_x );
29
    lcd_puti( 3, 2, adc_y );
30
  }
31
}
... und da kann man nicht mehr meckern, dass man ihn nicht lesen könnte.

von Robotico (Gast)


Lesenswert?

Ja das stimmt, ist deutlich übersichtlicher!

Wobei ich sowieso gerade nur am Testen und Ausprobieren bin und die 
Programme bisher noch nicht sehr länglich werden, da ist es auch so noch 
überschaubar.
Wenns nötig wird, werde ich natürlich in Funktionen zusammenfassen.

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.