Forum: Mikrocontroller und Digitale Elektronik ADC Messung Atmega8 C


von Walter P. (walter_pf)


Lesenswert?

Hi

Ich möchte mit einem Atmega 8 eine Spannung über den ADC einlesen. Dazu 
habe ich folgenden Code geschrieben:
1
/*
2
 * GccApplication2.c
3
 *
4
 * Created: 31.07.2014 15:54:58
5
 *  Author: Walter
6
 */ 
7
#define F_CPU 8000000
8
9
#include <avr/io.h>
10
11
int main(void)
12
{
13
    while(1)
14
    {
15
        DDRB |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); // Pins der Leds auf Ausgang schalten
16
    
17
    ADMUX |= (1 << 1) | (1 << 2); // Interne Referenzspannung verwenden, Ergebnis linksbündig, Kanal 0 verwenden
18
    ADCSRA |= (0 << 1) | (1 << 5) | (1 << 6) | (1 << 7); // ADC einschalten, Vorteiler = 128
19
    
20
    if (PINB & (1 << 5)) // Wenn Taste gedrückt wird, Messung beginnen
21
    {
22
      ADMUX |= (1 << 1); // Wandlung starten
23
      
24
      if (ADMUX & (0 << 1)) // Überprüfen ob die Messung abgeschlossen ist
25
      {
26
        if (ADCH < 50)
27
        {
28
          PORTB |= (1 << 0); // Wenn der ADC Wert unter 50 1. Led einschalten
29
        }
30
        
31
        if (ADCH > 50 && ADCH < 100)
32
        {
33
          PORTB |= (1 << 0) | (1 << 1); // Wenn der ADC Wert zwischen 50 und 100 ist, 1. Led und 2. Led einschalten
34
        }
35
        
36
        if (ADCH > 100 && ADCH < 150)
37
        {
38
          PORTB |= (1 << 0) | (1 << 1) | (1 << 2); // Wenn der ADC Wert zwischen 100 und 150 ist, 1. Led, 2. Led und 3. Led einschalten
39
        }
40
        
41
        if (ADCH > 150 && ADCH < 200)
42
        {
43
          PORTB |= (1 << 0); // Wenn der ADC Wert zwischen 150 und 200 ist, 1. Led, 2. Led, 3. Led und 4. Led einschalten
44
        }
45
46
        if (ADCH > 200)
47
        {
48
          PORTB |= (1 << 0); // Wenn der ADC Wert zwischen 200 und 255 ist, 1. Led, 2. Led, 3. Led, 4. Led und 5, Led einschalten
49
        }
50
        
51
      }
52
    }
53
    }
54
}

Könnte bitte einer von euch kurz darüber schauen und mir sagen ob es so 
funktioniert?

von spess53 (Gast)


Lesenswert?

Hi

>ADMUX |= (1 << 1); // Wandlung starten

Halte ich für ein Gerücht. Eine Messung wird durch setzen von ADSC in 
ADCSRA gestartet. Der ADC ist fertig wenn ADSC wieder Null wird.

Und tue dir und anderen den Gefallen und benutze die Bitbezeichner statt 
der Bitnummern.

MfG Spess

von Peter II (Gast)


Lesenswert?

das sieht auch werkwürdig aus
1
if (ADMUX & (0 << 1)) // Überprüfen ob die Messung abgeschlossen ist

so schnell wird diese Bedingung wohl nicht wahr werden.

von spess53 (Gast)


Lesenswert?

Hi

>so schnell wird diese Bedingung wohl nicht wahr werden.

Ich habe beim ersten offensichtlichen Fehler abgebrochen.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

1
    ADMUX |= (1 << 1) | (1 << 2); // Interne Referenzspannung verwenden, Ergebnis linksbündig, Ka

Das hier ist auch nicht die Referenzspannungseinstellung.

Wie Spess schon sagte: Benutze die Bitbezeichnungen aus dem Datenblatt. 
Dann spielt es auch keine Rolle, wenn du von der falschen Seite mit der 
Zählung der Bits anfängst :-)

Oder aber du benutzt die ADC Routinen aus dem Tutorial
AVR-GCC-Tutorial/Analoge Ein- und Ausgabe

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Der generelle Codeaufbau ist normalerweise
1
int main()
2
{
3
4
  alle Initialisierungen.
5
  Darunter fallen Variablen, die ihre Startwerte erhalten
6
  Aber auch die Initialisierung der Hardware in den für
7
  den Betrieb notwendigen Zustand fällt da drunter
8
  Also zb die Portpins auf Ausgang schalten, Pullup an Eingängen einschalten,
9
  Timer initialisieren, ADC initialisieren, etc.
10
11
  while( 1 )
12
  {
13
14
     hier steht dann die Programmlogik
15
     also zb das Abfragen von Eingängen, das Setzen von Ausgängen abhängig
16
     von irgendwelchen Bedingungn. Aber auch natürlich die Wandlung einer
17
     Spannung durch den ADC und das Abholen des Ergebnisses
18
19
  }
20
}

wenn dann einmal Interrupts ins Spiel kommen, verändert sich diese 
Struktur noch mal. Aber erst mal bist du damit gut bedient, wenn du dich 
an diese Struktur hältst. Denn
1
int main(void)
2
{
3
    while(1)
4
    {
5
        DDRB |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); // Pins der Leds auf Ausgang schalten
6
 ...
beispielsweise das Ständige Setzen der immer gleichen Pins auf Ausgang 
ist nicht dauernd notwendig sondern nur ein einziges mal. Daher hat das 
innerhalb der Schleife auch nichts verloren.

von H.Joachim S. (crazyhorse)


Lesenswert?

Walter P. schrieb:
> {
>         if (ADCH < 50)
>         {
>           PORTB |= (1 << 0); // Wenn der ADC Wert unter 50 1. Led
> einschalten
>         }
>
>         if (ADCH > 50 && ADCH < 100)
>         {
>           PORTB |= (1 << 0) | (1 << 1); // Wenn der ADC Wert zwischen 50
> und 100 ist, 1. Led und 2. Led einschalten
>         }
>
>         if (ADCH > 100 && ADCH < 150)
>         {
>           PORTB |= (1 << 0) | (1 << 1) | (1 << 2); // Wenn der ADC Wert
> zwischen 100 und 150 ist, 1. Led, 2. Led und 3. Led einschalten
>         }
>
>         if (ADCH > 150 && ADCH < 200)
>         {
>           PORTB |= (1 << 0); // Wenn der ADC Wert zwischen 150 und 200
> ist, 1. Led, 2. Led, 3. Led und 4. Led einschalten
>         }
>
>         if (ADCH > 200)
>         {
>           PORTB |= (1 << 0); // Wenn der ADC Wert zwischen 200 und 255
> ist, 1. Led, 2. Led, 3. Led, 4. Led und 5, Led einschalten
>         }
>
>       }

Und das geht auch schöner (abgesehen von den Fehlern - was ist  bei 
ADCH=50 oder 100 etc?)

if (ADCH<50)
   {
   }
   else if (ADCH<100)
           {
           }
.
.
           else {
                }

von Walter P. (walter_pf)


Lesenswert?

Hi

Danke für die vielen Antworten!

Ich habe den Code mal angepasst:
1
/*
2
 * GccApplication3.c
3
 *
4
 * Created: 01.08.2014 11:01:32
5
 *  Author: Walter
6
 */ 
7
#define F_CPU 8000000
8
9
#include <avr/io.h>
10
11
void ADC_Init(void) {
12
13
    ADMUX = (1 << REFS1) | (1 << ADLAR2;   // Interne Referenzspannung benutzen, Die 8 höchstwertigen Bits in ein Register schreiben 
14
    
15
    ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);     // Frequenzvorteiler
16
    ADCSRA |= (1<<ADEN);                  // ADC aktivieren
17
 
18
 
19
    ADCSRA |= (1 << ADSC);                  // eine ADC-Wandlung ( Dummy Conversion )
20
    
21
    while (ADCSRA & (1 << ADSC) ) {         // auf Abschluss der Konvertierung warten
22
    }
23
24
  
25
26
int main(void)
27
{
28
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4); // Pins der Leds auf Ausgang schalten
29
  ADC_Init(); // ADC initialisieren
30
    while(1)
31
    {  
32
    if (PINB & (1 << PB5)) // Überprüfen ob der Taster gedrückt wurde
33
    {
34
      ADCSRA |= (1<<ADSC); // Wandlung starten
35
      
36
      while (ADCSRA & (1<<ADSC) ) {   // auf Abschluss der Konvertierung warten
37
      }
38
      
39
      if (ADCH < 50)
40
      {
41
        PORTB |= (1 << PB0);
42
      }
43
      
44
      else if (ADCH < 100)
45
      {
46
        PORTB |= (1 << PB0) | (1 << PB1);
47
      }
48
      
49
      else if (ADCH < 150)
50
      {
51
        PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2);
52
      }
53
      
54
      else if (ADCH < 200)
55
      {
56
        PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3);
57
      }
58
      
59
      else
60
      {
61
        PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4);
62
      }
63
    }
64
    
65
        
66
    }
67
}

von H.Joachim S. (crazyhorse)


Lesenswert?

Und wer schaltet die Leds jemals wieder aus? :-)

von Karl H. (kbuchegg)


Lesenswert?

Gewöhn dir Sauberkeit im Code an!
Dazu gehört auch, dass du die Einrückungen korrekt machst.
Bei jedem { wird eingerückt (die meisten machen 2 Leerzeichen 
Einrückung), bei jeder } wird wieder ausgerückt.
Das hat auch Konsequenzen darüber, in welcher Spalte Anweisungen stehen 
werden.
Nach jeder { müssen die darauffolgenden Anweisungen um 2 Leerzeichen 
eingerückt sein.
Die entsprechende schliessende } wird um 2 Spalten ausgerückt und die 
darauf folgenden Anweisungen, die in derselben Ebene liegen müssen dann 
in genau derselben Spalte anfangen, in der auch die } steht.

So wie in
1
int main()
2
{
3
   while( irgendwas )
4
   {
5
     if( irgendwas )
6
     {
7
       abhängige Anweisung
8
       noch eine abhängige Anweisung
9
     }
10
11
     Anweisungen, die nicht vom if abhängen
12
13
   }  // diese } gehört zur öffnenden Klammer vom while. Daher steht sie
14
      // wieder in derselben Spalte wie die zugehörige {
15
16
   Anweisungen, die nicht mehr in der while sind
17
}   // diese } gehört zur { gleich nach dem Funktionsheader.
18
    // da hier die Funktion zu Ende ist, muss das auch so sein, dass diese
19
    // } in Spalte 0 steht.

Anhand der EInrückungen kann man ganz genau sehen, welche Anweisungen wo 
dazu gehören und von welchen darüber liegenden Bedingunen sie abhängen.


Hier in deinem Code
1
    ADCSRA |= (1 << ADSC);                  // eine ADC-Wandlung ( Dummy Conversion )
2
    
3
    while (ADCSRA & (1 << ADSC) ) {         // auf Abschluss der Konvertierung warten
4
    }
5
6
  
7
8
int main(void)
9
{
kann man ganz klar sehen, dass da etwas nicht stimmen kann.
Denn nach der letzten schliessenden } kann der Beginn von 'int main...' 
nicht in der Spalte 0 anfangen!
D.h. da fehlt mit Sicherheit eine schliessende }, wenn der Rest sauber, 
konsistent und korrekt eingerückt ist.

Wohingegen hier
1
      else
2
      {
3
        PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4);
4
      }
5
    }
6
    
7
        
8
    }
9
}
es völlig unmöglich ist, dass 2 schliessende } in derselben Spalte 
untereinander stehen.

Bei der Funktion hast du die schliessende } vergessen und hier in main 
hast du sie wieder dazugeschummelt. Da in deinem Programm danach nichts 
mehr passiert, hattest du Glück. Aber das ist nicht immer so.

Das hier
1
  ADC_Init(); // ADC initialisieren
2
    while(1)
3
    {  
4
    if (PINB & (1 << PB5)) // Überprüfen ob der Taster gedrückt wurde
5
    {
kann auch nicht sein.
das 'w' vom while kann nicht in dieser Spalte stehen, sondern muss in 
genau derselben Spalte wie das 'A' von ADC_init sein. Die beiden 
Anweisungen sind ja logisch gesehen auf der gleichen Stufe.
Genauso wie das 'i' vom if nicht direkt unter der { stehen kann! Nach 
einer { wird eingerückt!

Daher: Gewöhn dir eine ordentliche und konsistente Form an! Das ist 
nicht einfach nur Selbstzweck sondern kann dir stundenlanges 
Fehlersuchen ersparen, wenn es darum geht, wo du eine } vergessen hast 
und dein Programm dann nicht das tut, was du denkst das es tun sollte.
Ja ich weiß. Solange deine Programme klein sind, ist das alles noch kein 
Thema, das kann man alles auch noch so überblicken. Aber deine Programme 
bleiben nicht so klein. Und dann verlierst du mit Sicherheit den 
Überblick, wenn du nicht sauber und konsistent einrückst. Irgendwann 
musst du also mal anfangen, in deinen Programmen Ordnung zu halten. 
Warum also nicht gleich jetzt?

: Bearbeitet durch User
von Walter P. (walter_pf)


Lesenswert?

@Karl Heinz
Danke für die lange und ausführliche Antwort!
Ich habe versucht es so gut wie möglich umzusetzen.
1
/*
2
 * GccApplication3.c
3
 *
4
 * Created: 01.08.2014 11:01:32
5
 *  Author: Walter
6
 */ 
7
#define F_CPU 8000000
8
9
#include <avr/io.h>
10
11
void ADC_Init(void)
12
{
13
  ADMUX = (1 << REFS1) | (1 << ADLAR);   // Interne Referenzspannung benutzen, Die 8 höchstwertigen Bits in ein Register schreiben 
14
    
15
  ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);     // Frequenzvorteiler
16
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
17
 
18
 
19
  ADCSRA |= (1 << ADSC);                  // eine ADC-Wandlung ( Dummy Conversion )
20
    
21
  while (ADCSRA & (1 << ADSC)) // auf Abschluss der Konvertierung warten
22
  {         
23
  }
24
}
25
  
26
27
int main(void)
28
{
29
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4); // Pins der Leds auf Ausgang schalten
30
  ADC_Init(); // ADC initialisieren
31
  while(1)
32
  {  
33
    if (PINB & (1 << PB5)) // Überprüfen ob der Taster gedrückt wurde
34
    {
35
      ADCSRA |= (1<<ADSC); // Wandlung starten
36
      
37
      while (ADCSRA & (1<<ADSC)) // auf Abschluss der Konvertierung warten
38
      {   
39
      }
40
             
41
      if (ADCH < 50)
42
      {
43
        PORTB |= (1 << PB0);
44
      }
45
      
46
      else if (ADCH < 100)
47
      {
48
        PORTB |= (1 << PB0) | (1 << PB1);
49
      }
50
      
51
      else if (ADCH < 150)
52
      {
53
        PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2);
54
      }
55
      
56
      else if (ADCH < 200)
57
      {
58
        PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3);
59
      }
60
      
61
      else
62
      {
63
        PORTB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4);
64
      }
65
    }       
66
  }
67
}

von Walter P. (walter_pf)


Lesenswert?

Funktioniert der Code so?

von Bitflüsterer (Gast)


Lesenswert?

Walter P. schrieb:
> Funktioniert der Code so?

In knapp vier Stunden solltest Du das schon selbst festgestellt haben 
können. Oder gibt es einen Umstand, der Dich daran hindert?

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.