Forum: Mikrocontroller und Digitale Elektronik XMega: ADC liefert seltsame Werte


von Thomas L. (thomas_kl)


Angehängte Dateien:

Lesenswert?

Hallo,

ich verzweifle hier langsam am ADC des XMega.
Board ist das XMega A1U xplainedd pro mit einem XMega128A1U.
1
Referenz: Bandgap (1 V)
2
Eingang: Bandgap
3
Ergebnis: 4095 (richtiger Wert)
4
5
Referenz: Bandgap (1 V)
6
Eingang: Scaled VCC (0,33 V)
7
Ergebnis: 1514 (erwartet ist 1350)
8
9
Referenz: 3,3 V an Aref
10
Eingang: Bandgap
11
Ergebnis: 1761 (erwartet ist 1241)
12
13
Referenz: 3,3 V an Aref
14
Eingang: Scaled VCC (0,33 V)
15
Ergebnis: 658 (erwartet ist 410)
16
17
Referenz: Vcc / 1,6 (ca. 2,1 V)
18
Eingang: Scaled VCC (0,33 V)
19
Ergebnis: 843 (erwartet ist 655)

Konfiguriert wird der ADC so (mit jeweils anderen Werten für reference, 
internalInput und input. Vollständiger Code ist im Anhang.
1
  struct adc_config adcConfig = { };
2
  struct adc_channel_config adcChannelConfig = { };
3
4
  adc_set_clock_rate(&adcConfig, 62500);
5
  adc_set_conversion_trigger(&adcConfig, ADC_TRIG_MANUAL, 1, 0);
6
  adc_set_conversion_parameters(&adcConfig, ADC_SIGN_OFF, ADC_RES_12, reference);
7
  adc_enable_internal_input(&adcConfig, internalInput);
8
  
9
  adcch_set_input(&adcChannelConfig, input, ADCCH_NEG_NONE, 1);
10
  
11
  adc_write_configuration(adc, &adcConfig);
12
  adcch_write_configuration(adc, channelMask, &adcChannelConfig);
13
  
14
  adc_enable(adc);

8 statt 12 Bit verwenden hilft auch nicht, die Werte sind genauso 
falsch.

Hat jemand eine Idee?

Danke & Gruß
Thomas

von Michael (Firma: HW Entwicklung) (mkn)


Lesenswert?

Kalibriert hast Du den vorher?
In meinen längst nebulösen Erinnerungen liegen Factory Werte für 
Nullpunkt und Steigung im Speicher die man dem ADC übergeben muss.
Ist aber alles lange her.

von Thomas L. (thomas_kl)


Lesenswert?

Ja, kalibriert ist er. ADCA.CAL ist 0x444.
Ohne Kalibrierung unterscheiden sich die Werte aber nur minimal von 
denen mit Kalibrierung.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

1. VREF darf AVCC-0,6V nicht überschreiten.  Speist du AVCC mit 3,3V, 
dann ist eine VREF von 3,3V nicht erlaubt (Datenblatt von 2018, Tabelle 
37.2.6)

2. Errata beachten:

"ADC is non-functional in SE unsigned mode with VREF below 1.8V"
"14. ADC is non-functional in SE unsigned mode with VREF below 1.8V
When the ADC is used on single ended unsigned mode and VREF is below 
1.8V, INL and DNL error is increased above
+/- 10LSB, i.e. the ADC have missing codes under this condition."

DB von 2018, Seite 215.

Der ADC hat noch ein paar andere Problemchen, die aber hauptsächlich den 
Betrieb bei tiefen Temperaturen betreffen. Kurz - für brauchbare 
Ergebnisse nimmst du besser einen externen ADC. Mich hat schon der 128A1 
(ohne U) zum Zweifeln gebracht. Als ich die Nase voll hatte, bin ich auf 
STM32 umgestiegen.

von Thomas L. (thomas_kl)


Lesenswert?

Danke für die Antworten.

Auch mit 2,5V an Aref sind die Werte komplett daneben:
1
Referenz: 2,5 V an Aref
2
Eingang: Bandgap
3
Ergebnis: 2072 (erwartet ist 1638)
4
5
Referenz: 2,5 V an Aref
6
Eingang: Scaled VCC (0,33 V)
7
Ergebnis: 743 (erwartet ist 540)

Komisch ist eben auch das hier, da nur interne Werte als Eingang bzw. 
Referenz verwendet werden:
1
Referenz: Vcc / 1,6 (ca. 2,1 V)
2
Eingang: Scaled VCC (0,33 V)
3
Ergebnis: 843 (erwartet ist 655=4095*1,6/10)

... ah, ich glaub, ich habs :-):
das delta V war verwirrend. Zum einen sagt das XMEGA A MANUAL:
ΔV = VREF × 0.05

und das Datenblatt vom ATxmega128A1U:
ΔV Fixed offset voltage 190 LSB

Sogar die Temperatur wird damit einigermaßen richtig geschätzt.

Der Code, vielleicht hilft er ja dem ein oder anderen:
1
static float CalcVinp(uint16_t res, float vref)
2
{
3
  const float deltav = 190;
4
  float r = (float)res - deltav;
5
  float top = 4095.0f - deltav;
6
  
7
  float vinp = r / top * vref;
8
  return vinp;
9
}
10
11
mit Hilfe von https://blog.world3.net/2012/04/working-xmega-temperature-sensor-code/
12
static float CalcTemp(uint16_t res)
13
{
14
  const float deltav = 190;
15
  float r = (float)res - deltav;
16
17
  uint16_t refHot;
18
  float degreesX10;
19
  float kelvinPerAdcC10;
20
21
  // get 358 K factory calibrated value
22
  // 2382 for our controller.
23
  refHot = adc_get_calibration_data(ADC_CAL_TEMPSENSE);
24
  
25
  kelvinPerAdcC10 = (float)((273 + 85) * 10) / (float)refHot; // reference is ADC reading at 85C, scaled by 10 to get units of 0.1C
26
27
  degreesX10 = r;
28
  degreesX10 *= kelvinPerAdcC10;
29
  degreesX10 -= 2730.0f;
30
31
  return degreesX10 / 10.0f;
32
}

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.