Forum: Mikrocontroller und Digitale Elektronik ATxmega128A4 ADC Problem


von Alex K. (ko-j)


Lesenswert?

Hallo,

ich habe angefangen erstmals einen xmega, genauer einen ATxmega128A4,
zu programmieren jedoch scheitere ich am ADC.
Ich habe es bereits geschafft den µC auf 12 MHz mit Hilfe des internen
Oszillators zu takten, einen Timer einzustellen und mit dem USART Daten
an meinen PC zu senden.

Es geht darum einen Spannungswert an PA0 zu messen und das 12 bit 
Ergebniss
an den PC zu senden nur das funktioniert nicht so wie ich mir das
vorgestellt habe.
Wenn ich am PC in meinem Terminalprogramm den ADC Wert anschaue
wenn ich den Pin PA0 auf Masse ziehe, dann kommt eine binäre Zahl 
zwischen
0000100000100100 (=2084) und 0000100000011111 (=2079) dabei raus.
Normalerweise müsste mir doch 0 angezeigt werden, oder?


Hier mal der Code:
1
#ifndef F_CPU
2
#define F_CPU 12000000UL
3
#endif
4
5
#include <avr/io.h>
6
#include <avr/interrupt.h>
7
8
uint8_t adc_val_h;
9
uint8_t adc_val_l;
10
11
int main(void)
12
{
13
  
14
//Clock Init 
15
16
  OSC.PLLCTRL = OSC_PLLSRC_RC2M_gc | 0x06;  //PLL on 2 MHz Internal Oscillator | PLL Factor = 6
17
  OSC.CTRL |= OSC_PLLEN_bm;          //PLL Enable
18
  while (!(OSC.STATUS & (OSC_PLLRDY_bm)));  //Wait till the PLL is Ready
19
  CCP = CCP_IOREG_gc;              //Save I/O Register
20
  CLK.CTRL |= CLK_SCLKSEL_PLL_gc;        //System Clock Selection = PLL  
21
  
22
//Timer C0 10 ms
23
  TCC0.CTRLA = TC_CLKSEL_DIV256_gc;      //Prescaler = 256
24
  TCC0.CTRLB = TC_WGMODE_FRQ_gc;        //Frequency Mode
25
  TCC0.CCA = 469-1;
26
  TCC0.INTCTRLB |= TC_CCAINTLVL_HI_gc;    //Compare or Capture A Interrupt high level 
27
  
28
//USART C0
29
  PORTC.DIR |= PIN3_bm;            //TxD Pin (PC3) = Output
30
  PORTC.OUT |= PIN3_bm;            //TxD Pin (PC3) = High
31
  USARTC0.CTRLB |= USART_CLK2X_bm;      //Double Transmission Speed
32
  USARTC0.BAUDCTRLA = 38;            //Baud = 38400
33
  USARTC0.CTRLC = USART_CHSIZE_8BIT_gc;    //Character Size = 8-bit
34
  USARTC0.CTRLB |= USART_RXEN_bm;        //Receiver enable
35
  USARTC0.CTRLB |= USART_TXEN_bm;        //Transmitter enable
36
  
37
//ADC
38
  PORTA.DIR &= ~PIN0_bm;            //PA0 = Input
39
  ADCA.REFCTRL |= ADC_BANDGAP_bp;        //Enable Bandgapvoltage
40
  ADCA.REFCTRL |= ADC_REFSEL_INT1V_gc;    //Reference = 1V Bandgapvoltage
41
  ADCA.PRESCALER |= ADC_PRESCALER_DIV256_gc;  //Prescaler = 256
42
  ADCA.CH0.INTCTRL |= ADC_CH_INTLVL_HI_gc;  //High Level Interrupt
43
  ADCA.EVCTRL  |= ADC_EVACT_CH0_gc;      //Event Mode Select = CH0
44
  ADCA.CTRLB |= ADC_FREERUN_bm;        //Free Running Mode
45
  ADCA.CTRLA |= ADC_ENABLE_bm;        //ADC Enable
46
  
47
//Other Settings
48
  PMIC.CTRL |= PMIC_HILVLEN_bm | PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm;  //Enable all levels of Interrupts
49
  sei();                                //Enable global Interrupts
50
  
51
  
52
  //PORTD.DIR |= PIN2_bm;
53
  
54
  
55
    while(1)
56
    {
57
    
58
    }
59
}
60
61
ISR (TCC0_CCA_vect) {
62
  
63
  if (USARTC0.STATUS & (USART_DREIF_bm)) {
64
    USARTC0.DATA = adc_val_h;
65
  }
66
  
67
  while (!(USARTC0.STATUS & (USART_DREIF_bm)));
68
  
69
  USARTC0.DATA = adc_val_l;  
70
}
71
72
ISR (ADCA_CH0_vect) {
73
   
74
   adc_val_h = (ADCA_CH0RES >> 8);
75
   adc_val_l = ADCA_CH0RES;
76
}

Könnt ihr mir sagen was ich falsch gemacht habe?

Gruß Alex

von Detlef K. (adenin)


Lesenswert?

Sieht so aus, als hättest Du vergessen den Muxer und den Inputmode zu 
setzen.
Ich nehme mal an, das jetzt die Temperatur ausgegeben werden würde, wenn 
der Temperatursensor aktiviert wäre, hmm, ist er aber wohl nicht.

von Gerhard G. (g_g)


Lesenswert?

Hallo,



so was in der Art:

// ADC channel 0 gain: 1
// ADC channel 0 input mode: Single-ended positive input signal
ADCA.CH0.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc;

// ADC channel 0 positive input: ADC0 pin
// ADC channel 0 negative input: GND
ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;

// ADC channel 1 gain: 1
// ADC channel 1 input mode: Single-ended positive input signal
ADCA.CH1.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc;

// ADC channel 1 positive input: ADC1 pin
// ADC channel 1 negative input: GND
ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;


ADCA.EVCTRL=ADC_SWEEP_01_gc | ADC_EVACT_NONE_gc; // CH0 und CH1

// Free Running mode: On
ADCA.CTRLB|=ADC_FREERUN_bm;

// Enable the ADC
ADCA.CTRLA|=ADC_ENABLE_bm;


Gruß G.G.

von Alex K. (ko-j)


Lesenswert?

Ok
Danke für die Antworten.
Ich habe es bisher noch nicht geschaft es auszuprobieren aber wenn es 
nicht funktioniert,
dann melde ich mich wieder.

Gruß
Alex

von Alex K. (ko-j)


Lesenswert?

Heute hatte ich mal wieder etwas Zeit um das ganze mal auszuprobieren.
Jedoch mit mäßigem Erfolg.
Also der ADC misst jetzt zwar eine Spannung,
jedoch erreicht er den den Wert 2048 schon bei 0,943V (mit Multimeter 
gemessen)
und wenn ich den Pin auf Masse ziehe,
gibt er immer noch einen Wert von +-179 aus.

Ich habe dann versucht den ADC wie hier 
http://kampis-elektroecke.de/?page_id=1302
zu kalibrieren. Aber am Messergebniss hat sich nicht viel geändert.

Aktueller Code:
1
//ADC
2
  PORTA.DIR &= ~PIN0_bm;            //PA0 = Input
3
  ADCA.CH0.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc;
4
  ADCA.CH0.MUXCTRL |= ADC_CH_MUXPOS_PIN0_gc;
5
  ADCA.REFCTRL |= ADC_BANDGAP_bp;        //Enable Bandgapvoltage
6
  ADCA.REFCTRL |= ADC_REFSEL_INT1V_gc;    //Reference = 1V Bandgapvoltage
7
  ADCA.PRESCALER |= ADC_PRESCALER_DIV256_gc;  //Prescaler = 256
8
  ADCA.CH0.INTCTRL |= ADC_CH_INTLVL_HI_gc;  //High Level Interrupt
9
  ADCA.EVCTRL  |= ADC_EVACT_CH0_gc;      //Event Mode Select = CH0
10
  ADCA.CTRLB |= ADC_FREERUN_bm;        //Free Running Mode
11
  ADCA.CTRLA |= ADC_ENABLE_bm;        //ADC Enable

Gruß
Alex

von Timmo H. (masterfx)


Lesenswert?

Logisch, hast ja auch die interne 1V-Referenz eingestellt. Das andere 
ist der Offset. Den musst du vorher messen und dann raus rechnen.

Und kalibrieren würde ich den ADC auch vorher:
1
uint8_t ReadCalibrationByte( uint8_t index ){
2
  uint8_t result;
3
4
  /* Load the NVM Command register to read the calibration row. */
5
  NVM_CMD = NVM_CMD_READ_CALIB_ROW_gc;
6
  result = pgm_read_byte(index);
7
8
  /* Clean up NVM Command register. */
9
  NVM_CMD = NVM_CMD_NO_OPERATION_gc;
10
11
  return( result );
12
}
13
14
void ADC_init(){
15
  
16
  
17
  //Read factory calibration bytes
18
  ADCA.CALL = ReadCalibrationByte( offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0) );
19
  ADCA.CALH = ReadCalibrationByte( offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1) );
20
21
...
22
}

: Bearbeitet durch User
von Alex K. (ko-j)


Lesenswert?

Warum ist denn der Offset so groß?
Wenn ich bei einem ATmega8 den ADC Pin auf Masse ziehe,
bekomme ich auch glatte 0 raus.
Warum hier nicht?

von Timmo H. (masterfx)


Lesenswert?

Weil der adc vom atxmega etwas anders aufgebaut ist. Schau dir mal im 
Datenblatt den signed und unsigned mode an.

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.