Forum: Mikrocontroller und Digitale Elektronik ATxmega8E5 ADC_Problem


von DEU (Gast)


Lesenswert?

Hallo,

ich versuche zwei ADC-Kanäle vom ATxmega8E5 Microcontroller von Atmel 
auszulesen. Leider scheint die Auswertung nicht zu klappen am ADC PIN.2 
liegen 1,07V an und bei der if-Abfrage müsste es die Variable 
s_GLO.ucHardwareVersion = STD setzen es springt aber immer in den else 
Zweig.

Hat jemand eine Idee was im Quellcode noch verkehrt ist bzw. wie ich es 
noch testen könnten?
1
 
2
3
if ( (s_GLO.ui16VoltageHwVer > MIN_VOLT_TH_STD) /*&& (s_GLO.ui16VoltageHwVer < MAX_VOLT_TH_STD)*/ )
4
              {
5
                if( ui16TimerADC == 0)
6
                {
7
                  s_GLO.ucHardwareVersion = STD;
8
                  
9
                }
10
              }
11
              else
12
              {
13
                s_GLO.ucHardwareVersion = WL;
14
              }
15
16
17
void ADC_init (void)
18
{
19
  ADCA.CTRLB = ADC_RESOLUTION1_bm;    // set ADC-Resolution to 8Bit
20
  ADCA.REFCTRL = ADC_REFSEL2_bm;      // select Referenz Vcc/2 @3,19V = 1,595V = 1,514V (Offset = 13 = 81mV) @3,55V = 1,775V = 1,685V   (Offset = 13 = 90mV)
21
  ADCA.PRESCALER = ADC_PRESCALER2_bm;   // select Prescaler 64 = 125kHz
22
  ADCA.CH0.CTRL = ADC_CH_INPUTMODE0_bm; // select Single-ended mode
23
  //ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS1_bm;//ADC_CH_MUXPOS0_bm; // select ADC-Pin1
24
  ADCA.CTRLA = ADC_ENABLE_bm | ADC_START_bm; //set ADC enable  and start ADC-conversion 
25
  
26
}
27
28
29
30
31
 uint8_t ADCA_Selection(ADC_CH_t *Channel, uint8_t Pin)
32
 { 
33
   (*Channel).MUXCTRL = (Pin << 3); //selection of ADC-Pin
34
   
35
   while( !(*Channel).INTFLAGS);   
36
   
37
   return (*Channel).RESL;
38
 }
39
40
41
42
43
void ADC_read (void)
44
{      
45
  static uint8_t ucADC_Buffer_HW_Ver[ADC_BUFFER_SIZE], ucADC_Buffer_Bat_Volt[ADC_BUFFER_SIZE];
46
  static uint8_t ucADC_Buffer_Pointer;
47
  uint8_t uci;  
48
  
49
  if( (e_Zustand != DC_LADEN) )
50
  {
51
    ADC_init();  
52
    
53
    ucADC_Buffer_Pointer++;
54
    
55
    if(ucADC_Buffer_Pointer >= ADC_BUFFER_SIZE)
56
    {
57
      ucADC_Buffer_Pointer = 0;
58
    }    
59
    
60
    ucADC_Buffer_HW_Ver[ucADC_Buffer_Pointer] = ADCA_Selection( &(ADCA.CH0), 2); 
61
    
62
    ucADC_Buffer_Bat_Volt[ucADC_Buffer_Pointer] = ADCA_Selection( &(ADCA.CH0), 1); 
63
64
    s_GLO.ui16Akkuspannung = 0;
65
    s_GLO.ui16VoltageHwVer = 0;
66
    
67
    for (uci = 0; uci < ADC_BUFFER_SIZE; uci++)
68
    {
69
      s_GLO.ui16VoltageHwVer += ucADC_Buffer_HW_Ver[uci];
70
      s_GLO.ui16Akkuspannung += ucADC_Buffer_Bat_Volt[uci]; 
71
    }
72
    
73
    s_GLO.ui16VoltageHwVer /= ADC_BUFFER_SIZE;
74
    s_GLO.ui16Akkuspannung /= ADC_BUFFER_SIZE;  
75
  } 
76
}

von DEU (Gast)


Lesenswert?

Hat niemand einen Tipp? Ich habe mir schon gedacht das ich vllt das 
umschalten zwischen den beiden Kanälen verzögern muss oder so?

von Dennis K. (scarfaceno1)


Lesenswert?

Ist das der ganze Code?
Was passiert vor der if Verzweigung?

Wird der ADC denn auch vorher mal ausgelesen?

von M. K. (sylaina)


Lesenswert?

DEU schrieb:
> Hat niemand einen Tipp? Ich habe mir schon gedacht das ich vllt das
> umschalten zwischen den beiden Kanälen verzögern muss oder so?

Dein Quellcode ist nicht komplett. Es ist z.B. nirgends zu sehen wo 
ui16TimerADC definiert ist geschweige denn wo es beschrieben wird.

Tipp: Reduziere deinen Code auf ein Minimum und schaue ob das Problem 
dann immer noch besteht. Von da aus erweiterst du deinen Code immer 
Schritt für Schritt bis zu deinem jetzigen Code. Somit solltest du recht 
zügig die Stelle finden, an der es nicht mehr passt.

von DEU (Gast)


Lesenswert?

Hallo,

ich habe den Quellcode jetzt möglichst reduziert habe aber nun das 
Problem wenn ich beide Pins vom ADC gleichzeitig auslese dann bekomme 
ich zufällige Werte eingelesen.
Wenn ich nur einen Pin auslese funktionert es.


Was könnte das Problem sein?
1
#define  F_CPU 8000000UL
2
#include <util/delay.h>
3
#include <avr/io.h>
4
#include <stdint.h>
5
#include "prototypen.h"
6
#include "defines.h"
7
8
/*######################## begin global variables ##########################*/
9
10
struct global
11
{  
12
  /*ADC-read*/
13
  uint16_t ui16VoltageHwVer;              // voltage for HW-Ver. detection
14
  uint16_t ui16Akkuspannung;              // Bat voltage
15
  
16
} s_GLO;
17
18
19
int main(void) 
20
{   
21
  /*initialization*/
22
     clock_init(); 
23
  port_init();  
24
  ADC_init();      
25
  
26
  /*main loop*/
27
    while (1)
28
    {  
29
    /*read ADC*/
30
    ADC_read();
31
    
32
    
33
    
34
    /*decision illumination HW-Rev.*/   
35
    if ( (s_GLO.ui16VoltageHwVer > 152) && (s_GLO.ui16VoltageHwVer < 195) ) //66
36
    {
37
        //LED grün Ver._STD
38
      PORTD.OUTCLR = PIN5_bm;      
39
    }
40
    
41
    else
42
    {
43
      PORTD.OUTSET = PIN5_bm;      
44
    }  
45
  
46
    if ( (s_GLO.ui16VoltageHwVer > 195) )
47
    {
48
        //LED magenta Ver._WL
49
      PORTD.OUTCLR = PIN7_bm;
50
      PORTD.OUTCLR = PIN6_bm;    
51
    }
52
    
53
    else
54
    {
55
      PORTD.OUTSET = PIN7_bm;
56
      PORTD.OUTSET = PIN6_bm;  
57
    }
58
  
59
    if ( (s_GLO.ui16VoltageHwVer < 152 ) )
60
    {
61
        //LED blau Ver_PDD    
62
      PORTD.OUTCLR = PIN7_bm;  
63
    }
64
    
65
    else
66
    {
67
      PORTD.OUTSET = PIN7_bm;
68
    }    
69
    
70
  }
71
 
72
    return 0;
73
}
74
75
76
void clock_init (void)
77
{
78
  OSC.CTRL |= OSC_RC8MEN_bm; //oscillator at 8 MHz 
79
  while(!(OSC.STATUS & OSC_RC8MRDY_bm)); //wait for oscillator ready
80
  CCP = CCP_IOREG_gc; //protect I/O register, interrupts are ignored
81
  CLK.CTRL = (CLK.CTRL &~ CLK_SCLKSEL_gm) | CLK_SCLKSEL_RC8M_gc; //activate internal oscillator
82
}
83
84
85
void port_init (void)
86
{  
87
  /*Pinout Port A*/
88
  // 1: Charge voltage (ADC 0,93V-1,4V)  -INPUT
89
  // 2: HW-Ver. detection (ADC)       -INPUT
90
  
91
  /*Charge voltage*/
92
  PORTA.DIRCLR = PIN1_bm;
93
  
94
  /*HW-Ver. detection*/
95
  PORTA.DIRCLR = PIN2_bm; 
96
  
97
  
98
  /*Pinout Port D*/  
99
  
100
  // 5: Status LED green -OUTPUT
101
  // 6: Status LED red   -OUTPUT
102
  // 7: Status LED blue  -OUTPUT  
103
  
104
  /*Status LED green*/
105
  PORTD.DIRSET = PIN5_bm; 
106
  PORTD.OUTSET = PIN5_bm; //Status LED green OFF
107
  
108
  /*Status LED red*/
109
  PORTD.DIRSET = PIN6_bm; 
110
  PORTD.OUTSET = PIN6_bm; //Status LED red OFF
111
  
112
  /*Status LED blue*/
113
  PORTD.DIRSET = PIN7_bm; 
114
  PORTD.OUTSET = PIN7_bm; //Status LED blue OFF  
115
      
116
}
117
118
void ADC_init (void)
119
{
120
  ADCA.CTRLB = ADC_RESOLUTION_8BIT_gc;            // set ADC-Resolution to 8 Bit
121
  ADCA.REFCTRL = ADC_REFSEL_INTVCC2_gc;           // select Reference Vcc/2 
122
  ADCA.PRESCALER = ADC_PRESCALER_DIV128_gc;       // select Prescaler 64 = 125kHz  
123
  ADCA.CTRLA = ADC_ENABLE_bm;            // set ADC enable    
124
}
125
126
 
127
 uint8_t ADCA_Conversion(ADC_CH_t *Channel, uint8_t Pin)
128
 { 
129
   Channel->CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
130
   Channel->MUXCTRL = (Pin << 3);
131
   Channel->CTRL |= ADC_CH_START_bm;
132
   
133
   while(!Channel->INTFLAGS);
134
   return Channel->RES;
135
 }
136
137
138
void ADC_read (void)
139
{  
140
    s_GLO.ui16VoltageHwVer = ADCA_Conversion( &(ADCA.CH0), 2);    
141
  
142
    s_GLO.ui16Akkuspannung = ADCA_Conversion( &(ADCA.CH0), 1);          
143
}

von DEU (Gast)


Lesenswert?

Hat Jemand eine Idee?

Es muss doch möglich sein zwei ADC-Eingänge durch Umschalten zu messen, 
oder?

von Gerhard G. (xmega)


Lesenswert?

Hallo,

DEU schrieb:
> Channel->MUXCTRL = (Pin << 3);

ist eine Schreibweise die ich nicht kenne.

Meine Zugriffe starte ich zum Beispiel mit:

ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc ;
ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc ;
usw.


Gruß G.G.

von Falk B. (falk)


Lesenswert?

@ DEU (Gast)

>Hat jemand eine Idee was im Quellcode noch verkehrt ist bzw. wie ich es
>noch testen könnten?


>void ADC_init (void)
>{
>  ADCA.CTRLB = ADC_RESOLUTION1_bm;    // set ADC-Resolution to 8Bit
>  ADCA.REFCTRL = ADC_REFSEL2_bm;      // select Referenz Vcc/2 @3,19V = 1,595V = 
1,514V (Offset = 13 = 81mV) @3,55V = 1,775V = 1,685V   (Offset = 13 = 90mV)
>  ADCA.PRESCALER = ADC_PRESCALER2_bm;   // select Prescaler 64 = 125kHz
>  ADCA.CH0.CTRL = ADC_CH_INPUTMODE0_bm; // select Single-ended mode
>  //ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS1_bm;//ADC_CH_MUXPOS0_bm; // select ADC-Pin1

Das sieh soweit OK aus.

>  ADCA.CTRLA = ADC_ENABLE_bm | ADC_START_bm; //set ADC enable  and start 
ADC-conversion

Warum startest du den ADC hier? Einschalten (enable) reich vollkommen.

> uint8_t ADCA_Selection(ADC_CH_t *Channel, uint8_t Pin)

Der Name dieser Funktion ist irreführend! Es wird nicht nur der 
ADC-Kanal ausgewählt, es wird auch gemessen! Also sollte diese Funktion 
irgendwie eas mit read heißen!

https://www.mikrocontroller.net/articles/Strukturierte_Programmierung_auf_Mikrocontrollern#Benennung_von_Variablen.2C_Makros.2C_Nutzung_von_Anweisungen

> {
>   (*Channel).MUXCTRL = (Pin << 3); //selection of ADC-Pin

Falscher Fehler. Beim ATXmega arbeitet man mit den vordefinerten 
Bitmasken, NICHT den Bitnummern!
Siehe Bitmanipulation. Hier muss du per switch() die Pinnummer in 
das passende Bitmuster umrechnen. Im einfachsten Fall geht es mit einem 
direkten Reinschreiben, das muss man aber prüfen, ob das so im 
Datenblatt steht (das hah ich nicht)

   (*Channel).MUXCTRL = Pin;

>void ADC_read (void)

Auch der Name ist falsch, denn sie macht deutlich mehr als den ADC 
auslesen.

>{
>  static uint8_t ucADC_Buffer_HW_Ver[ADC_BUFFER_SIZE], 
ucADC_Buffer_Bat_Volt[ADC_BUFFER_SIZE];
>  static uint8_t ucADC_Buffer_Pointer;
>  uint8_t uci;

>  if( (e_Zustand != DC_LADEN) )
>  {
>    ADC_init();

Das hier ist falsch! Du willst den ADC nicht bei jedem Auslesen 
initialisieren! Das macht man nur einmal beim Programmstart.

von Dominik B. (odysseus1710)


Lesenswert?

DEU schrieb:
> Channel->MUXCTRL = (Pin << 3);

Damit setzt du für MUX beim ersten Aufruf '10000' und beim zweiten 
'1000'
Folgich wählst du als Eingänge ADC0 und ADC8, das sicher nicht die 
Kanäle sind, die an deinen Pins anliegen.

: Bearbeitet durch User
von DEU (Gast)


Angehängte Dateien:

Lesenswert?

Dominik B. schrieb:
> Damit setzt du für MUX beim ersten Aufruf '10000' und beim zweiten
> '1000'

das ist genau das was ich machen will denn laut Datenblatt ist Bitnummer 
4 Pin 1 und Bitnummer 3 Pin2.

An PortA Pin 1 und Pin 2 liegen genau meine Spannungen an die ich 
einlese.

Falk B. schrieb:
>>   (*Channel).MUXCTRL = (Pin << 3); //selection of ADC-Pin
>
> Falscher Fehler. Beim ATXmega arbeitet man mit den vordefinerten
> Bitmasken, NICHT den Bitnummern!
> Siehe Bitmanipulation. Hier muss du per switch() die Pinnummer in
> das passende Bitmuster umrechnen. Im einfachsten Fall geht es mit einem
> direkten Reinschreiben, das muss man aber prüfen, ob das so im
> Datenblatt steht (das hah ich nicht)
>
>    (*Channel).MUXCTRL = Pin;

ich habe es im Simulator durchgespielt und die Richtigen Bits werden im 
Register MUXCTRL gesetzt





ich habe auch schon versucht zwischen den Kanalwechsel zu warten, hat 
aber keinen Effekt. Das Problem ist das ein Channel für sich 
funktioniert nehme ich aber den zweiten dazu werden falsche werte 
eingelesen.


Was könnte noch das Problem im Quellcode sein?

von Dominik B. (odysseus1710)


Lesenswert?

DEU schrieb:
> Dominik B. schrieb:
>> Damit setzt du für MUX beim ersten Aufruf '10000' und beim zweiten
>> '1000'
>
> das ist genau das was ich machen will denn laut Datenblatt ist Bitnummer
> 4 Pin 1 und Bitnummer 3 Pin2.

Mein Fehler. Habe mich auf ein falsches Datenblatt bezogen bei dem 
MUXPOS die Bits 0 bis 3 sind.


DEU schrieb:
> ich habe auch schon versucht zwischen den Kanalwechsel zu warten, hat
> aber keinen Effekt

Du wartest nach dem Start einer Wandlung auf das Interrupt-Flag
DEU schrieb:
> while(!Channel->INTFLAGS)

Laut Datenblatt wird dieses Flag entweder zurückgesetzt, wenn der 
Interrupt Vector aufgerufen wird oder das Bit manuell mit einer '1' 
beschrieben wird.
Vermutlich wird das Flag also nach der ersten Wandlung nie zurückgesetzt 
und beim Start der zweiten Wandlung nicht mehr gewartet bis diese fertig 
ist.

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.