Forum: Compiler & IDEs ATmega8 ADC unerwartete Werte


von Stundenblume (Sebastian) (Gast)


Lesenswert?

Hallo Forum,

ich versuche mich gerade im Umgang mit MC, für einen Maschbauer gar 
nicht so einfach. ;-)

Also, ich habe einen ATmega8 
(http://www.kramann.info/87_Archiv_Mikrocontroller/02_Programmierer/atmega8.png) 
mit zwei Temperatursensoren (KTY) und jeweils einem Spannungsteiler an 
PC3 und PC4. Dort kommen bei -20°C 2,4V und bei RT 1,9V an. Nun möchte 
ich mit dem ADC die Spannungen auslesen. Referenzspannung sind die 
internen 2,56V.

Aus irgend einem Grund erhalte ich allerdings immer für beide Eingänge 
den selben Wert (zwischen 452 - 457). Selbst wenn die Sensoren definitiv 
unterschiedliche Temperaturen haben. Auch wenn ich die Referenzspannung 
auf AVCC lege ändert sich daran nichts. Dann sollte der Wert ja 
eigentlich nur halb so groß sein. Z.zt. bekommt die ganze Schaltung 
Spannung über den Programmer (4,5V) wenn ich über einen 7805 speise und 
die Spannung auf 5V steigt erhalte ich an den Eingängen Werte um 617.

Ich gehe davon aus, dass ich einen Bock im Code habe. Vielleicht hat ja 
jemand eine Idee.


Vielen Dank im voraus

Sebastian
1
#include <avr/io.h>
2
#include "lcd.h"
3
#include <stdio.h>
4
#include <util/delay.h>
5
6
7
int main(void)
8
{
9
  // Variablen definieren
10
  uint16_t temp1,temp2,i;
11
  uint8_t buffer1[4];
12
  uint8_t buffer2[4];
13
14
15
  //Ausgänge definieren
16
  DDRD |= (1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
17
18
  //ACD einrichten
19
  ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);  // Frequenzvorteiler auf 8
20
  ADMUX |= (1<<REFS0) | (1<<REFS1);        //Interne 2,56 Volt als Referenzspannung
21
22
  //LCD aktivieren
23
  lcd_init(LCD_DISP_ON);
24
25
  
26
  //Endlose Schleife
27
  for (;;) 
28
  {
29
30
    PORTD = 0;                  //LEDs aus
31
32
33
    //1. Messung
34
    ADMUX |= PC3;                //Kanal für Messung wählen
35
    ADCSRA |= (1<<ADSC);            //Messung starten
36
    while ( ADCSRA & (1<<ADSC) ) {}        //warten bis Messung bestätigt
37
    temp1 = ADCW;                //Ergebnis abholen
38
39
    //2. Messung
40
    ADMUX |= PC4;                //gleiches Vorgehen
41
    ADCSRA |= (1<<ADSC);
42
    while ( ADCSRA & (1<<ADSC) ) {}
43
    temp2 = ADCW;
44
45
    
46
    //Übergabe an LCD
47
    lcd_home();
48
    
49
    sprintf(buffer1,"a%d",temp1);
50
    lcd_puts(buffer1);
51
    
52
    sprintf(buffer2,"b%d",temp2);
53
    lcd_puts(buffer2);
54
55
    
56
    // zusäztliche Visualisierung an LEDs
57
    if(temp1>temp2){PORTD |= (1<<PD5);}
58
    else if(temp1<temp2){PORTD |= (1<<PD7);}
59
    else if(temp1==temp2){PORTD |= (1<<PD5)|(1<<PD7);}
60
61
62
    //Warten und neuen Schleifendurchlauf durch LED anzeigen
63
    for (i=0;i<=5;i++)
64
    {
65
      _delay_ms(222);
66
    }
67
    
68
    PORTD |= (1<<PD6);
69
    
70
    _delay_ms(1);
71
  }
72
}

von Tilo (Gast)


Lesenswert?

Hast du die Spannungen an den ADC gemessen? Eventuell ist es ein 
Schaltuns- und kein Programmierproblem.

von Marco S (Gast)


Lesenswert?

ADMUX |= x ist nicht so toll, kann nur Bits setzen.
1
#define VOLTAGE 7
2
#define set_adc(mux) ADMUX = (1<<REFS1) | (1<<REFS0) | (0x1F & (mux))
3
4
set_adc(VOLTAGE);

von Roman K. (Gast)


Lesenswert?

Den Code habe ich mir jetzt nicht angeguckt.
Aber du kannst diesen ganz einfach testen wenn du mit einem 
Potentiometer an die ADC eingänge gehst.
Ich habe das Gefühl du kennst dich ganz gut aus, trotzdem nochmal:

Potentiometer hat 3 Pins. die beiden äußeren kommen an Spannung und 
Ground
der Pin in der Mitte an den ADC Eingang.
Jetzt kannst du die Spannung am ADC über das Poti einstellen.

Bitte stell die elektrische Schaltung auch mal rein,
also wie der Spannungsteiler an dem KTY angeschlossen ist.

von Sebastian S. (stundenblume)


Lesenswert?

Hallo,

@Tilo
Im Moment habe ich den einen Sensor unter meinem Laptop und den anderen 
frei im Raum. An PC3 sind es 1,84V und an PC4 1,78V.


@Roman
die Schaltung schaut so aus:

5V---KTY1----R1---GND
           |
           PC3

@Marco
Das verstehe ich noch nicht so ganz. Versuche mich aber daran...

von Marco S (Gast)


Lesenswert?

Mit der |= (oder-gleich) setzt du nur Bits in ADMUX. Du setzt einmalig 
die Ref-Bits. Danach oderst du 0x03 hinzu (PC3), danach 0x04 (PC4). Dass 
heißt, du wählst kontinuierlich ADC7 aus.

von Lutz (Gast)


Lesenswert?

Ist zwar eine Auswertungssache, aber Du solltest die Spannung über dem 
KTY messen und nicht über dem "Vor"widerstand. Das erklärt auch, warum 
Deine gemessene Spannung mit der steigender Temperatur fällt.

Und wie Marco schon geschrieben hatte, mußt Du zum Wählen des 
ADC-Eingangskanals die MUX-bits in ADMUX nehmen und nicht die "normalen" 
Port-bits.

von Sebastian S. (stundenblume)


Lesenswert?

Moin,

vielen Dank für die Hilfe. Es läuft :-) Ich habe es zwar noch nicht so 
ganz verstanden weshalb, aber das kommt hoffentlich noch.

Ich habe durch eure Beispiele das Tutorial zum ADC besser nachvollziehen 
können und habe meinen Code entsprechend umgebaut. Nun wird der ADC für 
jede Messung neu eingerichtet und das funktioniert. Was ich noch nicht 
verstehe, ist weshalb ich dem kompletten Register ADMUX die Kanalnummer 
gebe (ADMUX=3;)?!?


Gruß

Sebastian

1
#include <avr/io.h>
2
#include "lcd.h"
3
#include <stdio.h>
4
#include <util/delay.h>
5
6
7
int main(void)
8
{
9
  // Variablen definieren
10
  uint16_t temp1,temp2,i;
11
  uint8_t buffer1[4];
12
  uint8_t buffer2[4];
13
14
15
  //Ausgänge definieren
16
  DDRD |= (1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
17
18
  //LCD aktivieren
19
  lcd_init(LCD_DISP_ON);
20
21
  
22
  //Endlose Schleife
23
  for (;;) 
24
  {
25
26
    PORTD = 0;                            //LEDs aus
27
28
29
    //1. Messung
30
    ADMUX = 3;                            //Kanal für Messung wählen
31
    //ACD einrichten
32
    ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);  // Frequenzvorteiler auf 8
33
    ADMUX |= (1<<REFS0) | (1<<REFS1);            //Interne 2,56 Volt als Referenzspannung
34
    ADCSRA |= (1<<ADSC);                    //Messung starten
35
    while ( ADCSRA & (1<<ADSC) ) {}              //warten bis Messung bestätigt
36
    temp1 = ADCW;                          //Ergebnis abholen
37
    ADCSRA &= ~(1<<ADEN);                     // ADC deaktivieren
38
39
    //2. Messung
40
    ADMUX = 4;                            //gleiches Vorgehen
41
    //ACD einrichten
42
    ADCSRA |= (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);  // Frequenzvorteiler auf 8
43
    ADMUX |= (1<<REFS0) | (1<<REFS1);            //Interne 2,56 Volt als Referenzspannung
44
    ADCSRA |= (1<<ADSC);
45
    while ( ADCSRA & (1<<ADSC) ) {}
46
    temp2 = ADCW;
47
    ADCSRA &= ~(1<<ADEN);                     // ADC deaktivieren
48
49
    
50
    //Übergabe an LCD
51
    lcd_home();
52
    
53
    sprintf(buffer1,"a%d",temp1);
54
    lcd_puts(buffer1);
55
    
56
    sprintf(buffer2,"b%d",temp2);
57
    lcd_puts(buffer2);
58
59
    
60
    // zusäztliche Visualisierung an LEDs
61
    if(temp1>temp2){PORTD |= (1<<PD5);}
62
    else if(temp1<temp2){PORTD |= (1<<PD7);}
63
    else if(temp1==temp2){PORTD |= (1<<PD5)|(1<<PD7);}
64
65
66
    //Warten und neuen Schleifendurchlauf durch LED anzeigen
67
    for (i=0;i<=5;i++)
68
    {
69
      _delay_ms(222);
70
    }
71
    
72
    PORTD |= (1<<PD6);
73
    
74
    _delay_ms(1);
75
  }
76
}

von Johannes M. (johnny-m)


Lesenswert?

Sebastian S. wrote:
> Was ich noch nicht
> verstehe, ist weshalb ich dem kompletten Register ADMUX die Kanalnummer
> gebe (ADMUX=3;)?!?
Das verstehe ich auch nicht, weil Du damit jedes Mal die 
Referenzspannungsauswahl löschst und sie zwei Zeilen weiter jeweils 
wieder setzen musst. Übersichtlicher und ohne Änderung der 
Referenzspannung wäre es z.B. mit
1
ADMUX = (ADMUX & 0xF0) | channel;
Dabei bleiben die oberen 4 Bits unberührt. channel wäre hier dann die 
Kanalnummer.

von Sebastian S. (stundenblume)


Lesenswert?

Hi,

ich hab den Code jetzt nochmal nach Deinem Vorschlag verändert und auch 
des ewige einrichten abgeschafft. Funktioniert.

Gruß

1
#include <avr/io.h>
2
#include "lcd.h"
3
#include <stdio.h>
4
#include <util/delay.h>
5
6
7
int main(void)
8
{
9
  // Variablen definieren
10
  uint16_t temp1,temp2,i;
11
  uint8_t buffer1[4];
12
  uint8_t buffer2[4];
13
14
15
  //Ausgänge definieren
16
  DDRD |= (1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
17
18
  //ADC einrichten
19
  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);  // Frequenzvorteiler setzen auf 8 und ADC aktivieren
20
  ADMUX |= (1<<REFS1) | (1<<REFS0);         // interne Referenzspannung nutzen
21
22
  //LCD aktivieren
23
  lcd_init(LCD_DISP_ON);
24
25
26
  uint16_t KanalMessen(uint8_t KanalX)
27
  {
28
      ADMUX = (ADMUX & 0xF8)| KanalX;                 // Kanal wählen
29
 
30
      ADCSRA |= (1<<ADEN);              // ADC aktivieren
31
 
32
       ADCSRA |= (1<<ADSC);                    // eine Wandlung statren
33
      while ( ADCSRA & (1<<ADSC) ) {}
34
      ADCSRA &= ~(1<<ADEN);                     // ADC deaktivieren
35
 
36
 
37
      return ADCW;
38
  }
39
  
40
41
  //Endlose Schleife
42
  for (;;) 
43
  {
44
45
    PORTD = 0;                    //alle LEDs aus
46
47
48
    //1. Messung
49
    temp1 = KanalMessen(3);              //Kanal 3 wandeln
50
51
    //2. Messung
52
    temp2 = KanalMessen(4)+2;            //Kanal 4 wandeln
53
54
    
55
    //Übergabe an LCD
56
    lcd_home();
57
    
58
    sprintf(buffer1,"a%d",temp1);
59
    lcd_puts(buffer1);
60
    
61
    sprintf(buffer2,"b%d",temp2);
62
    lcd_puts(buffer2);
63
64
    
65
    // zusäztliche Visualisierung an LEDs
66
    if(temp1>temp2){PORTD |= (1<<PD5);}          //LED 1 ein
67
    else if(temp1<temp2){PORTD |= (1<<PD7);}      //LED 3 ein
68
    else if(temp1==temp2){PORTD |= (1<<PD5)|(1<<PD7);}  //LED 1 und 3 ein
69
70
71
    //Warten und neuen Schleifendurchlauf durch LED anzeigen
72
    for (i=0;i<=5;i++)
73
    {
74
      _delay_ms(222);
75
    }
76
    
77
    PORTD |= (1<<PD6);                  //LED 2 ein
78
    
79
    _delay_ms(1);
80
  }
81
}

von lkmiller (Gast)


Lesenswert?

>Es läuft :-) Ich habe es zwar noch nicht so
>ganz verstanden weshalb...
War das nicht bei Frankenstein auch so???  ;-)

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.