Forum: Mikrocontroller und Digitale Elektronik AVR AT90CAN128 ADC


von Michael S. (elmsfeuer)


Lesenswert?

Hallo,

ich bin etwas ratlos aufgrund meines Problems. Ich möchte eine einfache 
Single-Channel ADC-Wandlung vornehmen, aber egal ob sich die 
Eingangsspannung ändert bleibt der gewandelte Wert konstant. Vielleicht 
weiß von Euch jemand rat oder kann mir auf die Spünge helfen was ich 
falsch mache.

Ich benutze das STK500 + STK501 + AT90CAN128 + JTAGICEmkII + aktl. 
Version AVR-Studio + aktl. Version Win-AVR

Hier "mein" Code:
1
#include "config.h"
2
#include "uart_lib.h"
3
#include "uart_drv.h"
4
#include "rtc_drv.h"
5
6
void init(void)
7
{
8
   Uart_select(UART_0);
9
   uart_init(CONF_8BIT_NOPAR_1STOP,38400);
10
11
   //Liefert die zu messende Spannung
12
   DDRB |= (1<<PB0);
13
   PORTB |= (1<<PB0);
14
}
15
16
unsigned int ReadChannel(unsigned char mux)
17
{
18
  unsigned char i;
19
  unsigned int result;
20
21
  ADMUX = mux;                      // Kanal waehlen
22
  ADMUX |= (0<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen
23
 
24
  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);    // Frequenzvorteiler 
25
                               // setzen auf 8 (1) und ADC aktivieren (1)
26
 
27
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
28
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
29
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
30
  while ( ADCSRA & (1<<ADSC) ) {
31
     ;     // auf Abschluss der Konvertierung warten 
32
  }
33
  result = ADCW;  // ADCW muss einmal gelesen werden,
34
                  // sonst wird Ergebnis der nächsten Wandlung
35
                  // nicht übernommen.
36
 
37
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
38
  result = 0; 
39
  for( i=0; i<4; i++ )
40
  {
41
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
42
    while ( ADCSRA & (1<<ADSC) ) {
43
      ;   // auf Abschluss der Konvertierung warten
44
    }
45
    result += ADCW;        // Wandlungsergebnisse aufaddieren
46
  }
47
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
48
 
49
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
50
 
51
  return result;
52
}
53
54
int main(void)
55
{
56
   unsigned int counter, adc, i;
57
   counter = adc = 0;
58
   init();
59
   rtc_int_init();
60
61
   while(1)
62
   {
63
      for (i = 0; i< 8; i++)
64
      {
65
         wait_for(500);
66
         adc = ReadChannel(i);
67
         uart_mini_printf ("\r%04X Spannung(%u):%04X %umV\n", counter++, i, adc, (unsigned int)(((double)adc/1023)*5000));
68
      }
69
   }
70
   return 0;
71
}

Die Funktionen wait_for() und rtc_... stammen aus den Atmel-Libs vom 
DVK90CAN1-Board.

FOSC = 8 Mhz.

Als Messspannung hab ich den Ausgang von PB0 mit zwei 1k R 
runtergeteilt. Als Referenzspannung ist AVCC ausgewählt und auf AREF ist 
ein 100nF C dran. Die Spannungen an PB0 und AVCC sind wie erwartet mit 
dem Oszi zu messen.
PB0/2 = 2.50V -> PA1
AVCC = 5.01V

Den Jumper AREF vom STK500 hab ich entfernt. Den Chip hab ich auch 
bereits getauscht.

Die Schleife in main() hab ich eingebaut, weil mir beim wechseln des 
Eingangsports aufgefallen ist, daß auf den anderen Eingangskanälen 
ebenfalls "feste" Werte zu messen sind.

Lege ich PA1 auf GND ändert sich an den Ausgabewerten nichts und es ist 
definitiv nur ein Kabel vom Spannungsteiler auf dem PA1-Pin gelegt, der 
Rest des Port A ist nicht belegt.

Hier die UART-Ausgabe (2 Schleifendurchgänge):
1
0038 Spannung(0):034F 4139mV
2
0039 Spannung(1):02F0 3675mV
3
003A Spannung(2):02D9 3563mV
4
003B Spannung(3):02F3 3690mV
5
003C Spannung(4):03FF 5000mV
6
003D Spannung(5):03FF 5000mV
7
003E Spannung(6):03FF 5000mV
8
003F Spannung(7):03FF 5000mV
9
0040 Spannung(0):034F 4139mV
10
0041 Spannung(1):02F0 3675mV
11
0042 Spannung(2):02D9 3563mV
12
0043 Spannung(3):02F4 3695mV
13
0044 Spannung(4):03FF 5000mV
14
0045 Spannung(5):03FF 5000mV
15
0046 Spannung(6):03FF 5000mV
16
0047 Spannung(7):03FF 5000mV

Ich hoffe es kann mir jemand helfen. Danke.

MfG
Michael

von Lutz (Gast)


Lesenswert?

Hallo Michael,

die Zeile
ADMUX |= (0<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen
bringt vermutlich schon mal nicht das, was Du wolltest (eine Null 
schieben ist schon schlecht).

Gruß
Lutz

von Lutz (Gast)


Lesenswert?

Hallo Michael,

in diesem Falle scheint es egal zu sein, wenn Du die Null um 7 Stellen 
nach links schieben willst. Falsch ist auf den ersten Blick aber der 
Kommentar
>> ADMUX |= (0<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen
da Du ja, wie weiter unten auch geschrieben, AVCC als Referenzspannung 
auswählst.

Vielleicht wählst Du für den Eingang des ADC aber lieber, wie im 
Datenblatt beschrieben, PORTF als ADC-Eingang und nicht PORTA? Die 
Beschreibung AD1 bei PA1 ist nämlich für External Memory Interfaces und 
nicht für den ADC ...

Gruß
Lutz

von Michael S. (elmsfeuer)


Lesenswert?

Guten morgen,

danke Dir Lutz für den Hinweis, das wär mir in tausend Jahren nicht 
aufgefallen, den Fehler der Portverwechselung habe ich gleich am Anfang 
gemacht und mit durchgeschleppt und war mir bei jedem Blick auf die 
Pin-Konfiguration im Datenblatt sicher AD0-AD7 wären die ADC-Eingänge. 
Also vielen Dank für den Hinweis und entschuldige bitte das ich durch 
meine Nachlässigkeit Deine Zeit verschwendet habe.

Du hast auch Recht im Bezug auf das ADMUX-Register. Habe beim ändern des 
Codes das Kommentar nicht geändert und war zu faul (1<<REFS1) zu löschen 
und habe stattdessen aus der 1 eine 0 gemacht.

Also in der Schaltung PB0/2 auf PF0 und zack es funktioniert.

Das ist der jetzt funktionierende Code:
1
#include "config.h"
2
#include "uart_lib.h"
3
#include "uart_drv.h"
4
#include "rtc_drv.h"
5
6
void init(void)
7
{
8
   Uart_select(UART_0);
9
   uart_init(CONF_8BIT_NOPAR_1STOP,38400);
10
11
   //Liefert die zu messende Spannung
12
   DDRB |= (1<<PB0);
13
   PORTB |= (1<<PB0);
14
}
15
16
unsigned int ReadChannel(unsigned char mux)
17
{
18
  unsigned char i;
19
  unsigned int result;
20
21
  ADMUX = mux;                      // Kanal waehlen
22
  ADMUX |= (1<<REFS0); // AVCC als Referenzspannung nutzen
23
 
24
  ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);    // Frequenzvorteiler 
25
                               // setzen auf 8 (1) und ADC aktivieren (1)
26
 
27
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
28
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
29
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
30
  while ( ADCSRA & (1<<ADSC) ) {
31
     ;     // auf Abschluss der Konvertierung warten 
32
  }
33
  result = ADCW;  // ADCW muss einmal gelesen werden,
34
                  // sonst wird Ergebnis der nächsten Wandlung
35
                  // nicht übernommen.
36
 
37
  /* Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen */
38
  result = 0; 
39
  for( i=0; i<4; i++ )
40
  {
41
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
42
    while ( ADCSRA & (1<<ADSC) ) {
43
      ;   // auf Abschluss der Konvertierung warten
44
    }
45
    result += ADCW;        // Wandlungsergebnisse aufaddieren
46
  }
47
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
48
 
49
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
50
 
51
  return result;
52
}
53
54
int main(void)
55
{
56
   unsigned int counter, adc, i;
57
   counter = adc = 0;
58
   init();
59
   rtc_int_init();
60
61
   while(1)
62
   {
63
      wait_for(500);
64
      adc = ReadChannel(0);
65
      uart_mini_printf ("\r%04X Spannung:%04X %umV\n", counter++, adc, (unsigned int)(((double)adc/1023)*5000));
66
   }
67
   return 0;
68
}

Danke für die Hilfe!!! Kann man den Thread eigentlich als erledigt 
markieren/schließen?

MfG
Michael

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.