Forum: Mikrocontroller und Digitale Elektronik STM32L011 ADC Temperatur zu hoch


von Mampf F. (mampf) Benutzerseite


Lesenswert?

Guten Morgen,

ich hab ein hartnäckiges Problem mit dem STM32-internen 
Temperatursensor.

Dieser liefert kontinuierlich Werte, die ca 10°C zu hoch sind.

Meine Initialisierung:
1
static void MX_ADC_Init(void) {
2
  __HAL_RCC_ADC1_CLK_ENABLE();
3
4
  ADC_ChannelConfTypeDef sConfig = { 0 };
5
  hadc.Instance = ADC1;
6
  hadc.Init.OversamplingMode = DISABLE;
7
  hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
8
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
9
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
10
  hadc.Init.SamplingTime = ADC_SAMPLETIME_160CYCLES_5;
11
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
12
  hadc.Init.ContinuousConvMode = DISABLE;
13
  hadc.Init.DiscontinuousConvMode = ENABLE;
14
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
15
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
16
  hadc.Init.DMAContinuousRequests = DISABLE;
17
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
18
  hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
19
  hadc.Init.LowPowerAutoWait = DISABLE;
20
  hadc.Init.LowPowerFrequencyMode = DISABLE;
21
  hadc.Init.LowPowerAutoPowerOff = DISABLE;
22
  if (HAL_ADC_Init(&hadc) != HAL_OK) {
23
    Error_Handler();
24
  }
25
  /**Configure for the selected ADC regular channel to be converted.
26
   */
27
  sConfig.Channel = ADC_CHANNEL_0;
28
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
29
30
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
31
    Error_Handler();
32
  }
33
  /**Configure for the selected ADC regular channel to be converted.
34
   */
35
  sConfig.Channel = ADC_CHANNEL_1;
36
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
37
    Error_Handler();
38
  }
39
  /**Configure for the selected ADC regular channel to be converted.
40
  */
41
  sConfig.Channel = ADC_CHANNEL_VREFINT; //Initialize VREFINT_CAL reference voltage
42
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
43
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
44
  {
45
    Error_Handler();
46
  }
47
48
  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; // Initialize the chip temperature sensor
49
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
50
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
51
  {
52
    Error_Handler();
53
  }
54
55
  HAL_ADCEx_EnableVREFINT();
56
  HAL_ADCEx_EnableVREFINTTempSensor();
57
}

Zur Umrechnung der ADC-Werte hab ich die Code-Snippts aus dem Reference 
Manual und auch aus Example-Projekten aus dem STM32Cube Repository für 
die L0.
1
#define TEMP30_CAL_ADDR   ((uint16_t*) ((uint32_t) 0x1FF8007A)) /* Internal temperature sensor, parameter TS_CAL1: TS ADC raw data acquired at a temperature of 30 DegC (+-5 DegC), VDDA = 3.3 V (+-10 mV). */
2
#define TEMP130_CAL_ADDR  ((uint16_t*) ((uint32_t) 0x1FF8007E)) /* Internal temperature sensor, parameter TS_CAL2: TS ADC raw data acquired at a temperature of  130 DegC (+-5 DegC), VDDA = 3.3 V (+-10 mV). */
3
4
#define VDDA_APPLI                     ((uint32_t) 3300)    /* Value of analog voltage supply Vdda (unit: mV) */
5
#define VDDA_TEMP_CAL                  ((uint32_t) 3000)        /* Vdda value with which temperature sensor has been calibrated in production (+-10 mV). */
6
7
/* Private macro -------------------------------------------------------------*/
8
/**
9
  * @brief  Computation of temperature (unit: degree Celsius) from the internal
10
  *         temperature sensor measurement by ADC.
11
  *         Computation is using temperature sensor calibration values done
12
  *         in production.
13
  *         Computation formula:
14
  *         Temperature = (TS_ADC_DATA - TS_CAL1) * (130degC - 30degC)
15
  *                       / (TS_CAL2 - TS_CAL1) + 30degC
16
  *         with TS_ADC_DATA = temperature sensor raw data measured by ADC
17
  *              Avg_Slope = (TS_CAL2 - TS_CAL1) / (130 - 30)
18
  *              TS_CAL1 = TS_ADC_DATA @30degC (calibrated in factory)
19
  *              TS_CAL2 = TS_ADC_DATA @130degC (calibrated in factory)
20
  *         Calculation validity conditioned to settings:
21
  *          - ADC resolution 12 bits (need to scale conversion value
22
  *            if using a different resolution).
23
  *          - Power supply of analog voltage set to literal VDDA_APPLI
24
  *            (need to scale value if using a different value of analog
25
  *            voltage supply).
26
  * @param TS_ADC_DATA: Temperature sensor digital value measured by ADC
27
  * @retval None
28
  */
29
#define COMPUTATION_TEMPERATURE_TEMP30_TEMP130(TS_ADC_DATA)                    \
30
  (((( ((int32_t)((TS_ADC_DATA * VDDA_APPLI) / VDDA_TEMP_CAL)                  \
31
        - (int32_t) *TEMP30_CAL_ADDR)                                          \
32
     ) * (int32_t)(130 - 30)                                                   \
33
    ) / (int32_t)(*TEMP130_CAL_ADDR - *TEMP30_CAL_ADDR)                        \
34
   ) + 30                                                                      \
35
  )

Eigentlich kann man doch da nicht viel falsch machen - statt 25°C 
erhalte ich Werte, die rund 10°C höher sind.

Ich kann mir beim besten Willen nicht vorstellen, dass ich die 
Temperatur meines µCs um 10°C falsch einschätze.

Mein ADC wird mit 3,3V bepowert - das ist aber in der Umrechnung von als 
VDD_APPLI einberechnet.

ADC Kalibrierung kann es nicht sein, weil ich auch eine 
Batterie-Spannungsmessung eingebaut hab, die auf 5mV genau misst.

Hat jemand ähnliche Probleme mal festgestellt?

Viele Grüße,
Mampf

*edit*: Die beiden Kalibrierwerte:
658 für 30°C bei 3,0Vadc entspricht 598 @ 3,3Vadc
931 für 130°C bei 3,0Vadc entspricht 846 @ 3,3Vadc

ADC hat gelesen: 612 bei 3,3Vadc

Das wären definitiv mehr als 30°C

m = (130-30)/(846-598) = 0,40322
t = 30 - 0,40322 * 598 = -211,12556

Temp = 612 * 0,40322 - 211,12556 = 35,645°C

Hmm, kann da keinen Fehler entdecken.

*edit2*: Die slope (über die Kalibrierwerte) ist außerhalb des 
max-Wertes: 1,81mv/°C

Angegeben ist: 1,48 (min), 1,61 (typ), 1.75 (max).

Das ist ja seltsam ...

: Bearbeitet durch User
von A. B. (Gast)


Lesenswert?

Das ist nicht die Umgebungstemperatur, sondern die Chip-Temperatur. Die 
Eigenerwärmung ist da zu berücksichtigen. Bei einem G030 hatte ich vor 
einiger Zeit das mal ausprobiert, da hatte ich so etwa 2 bis 3 Grad über 
Raumtemperatur (im Halt!!!) (inkl. Kalibrierung). 10 Grad ist schon ein 
wenig viel, aber je nach Last/Leistungsaufnahme kann das trotzdem 
hinkommen.

"The temperature sensor can be used to measure the junction temperature 
(T J ) of the device."

"junction", nicht "ambient"!

Für Messungen der Umgebungstemperatur ist der interne Sensor nur sehr 
bedingt geeignet.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

A. B. schrieb:
> "The temperature sensor can be used to measure the junction temperature
> (T J ) of the device."
>
> "junction", nicht "ambient"!
>
> Für Messungen der Umgebungstemperatur ist der interne Sensor nur sehr
> bedingt geeignet.

Ups, okay - ja du hast recht!

Ich hab mal alles angehalten (GPIOs im Reset, Clock runtergefahren) und 
hab jetzt 28°C statt 35°C.

Hmm - also nicht benutzbar als Umgebungstemperatur-Sensor.

Gut zu wissen.

Danke!

von A. B. (Gast)


Lesenswert?

Ach ja, bei manchen "abgespeckten" Varianten ist im Datenblatt entweder 
nur TS_CAL1 oder TS_CAL2 angegeben, so z. B. beim G030 (ggü. G031) und 
L011 (ggu. L031). Das heißt möglicherweise, dass man sich auf eine 
Zweipunktkalibrierung nicht verlassen kann/soll. Diese Unklarheit 
geistert schon seit Jahren durch die RMe bzw. Datenblätter, aber ST 
kümmert's anscheinend nicht.

von Gerd E. (robberknight)


Lesenswert?

Wie häufig misst Du?

Ich hatte das mal ausprobiert und desto häufiger ich gemessen hatte, 
desto weiter ging die Temp hoch. Dagegen nach 10 Sekunden Pause war es 
wieder realistisch.

Aber der interne ist echt nur ein Schätzeisen. Ich nehm normal kleine 
NTCs in 0603 wenn ich eine Umgebungstemperatur brauche. Die sind billig, 
klein und leicht auszuwerten.

von Günni (Gast)


Lesenswert?

Bei dieser Temperaturmessung über die "Bandgap"-Reference" wird die 
(Dioden-)Schwellspannung bei verschiedenen Strömen gemessen und daraus 
die Temperatur ermittelt. Die Ströme wird man sinnvoll erst während der 
Messung einschalten und so steigt die lokale Erwärmung während der 
Messung und häufige Messungen führen zu höheren Temperaturwerten.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Gerd E. schrieb:
> Wie häufig misst Du?
>
> Ich hatte das mal ausprobiert und desto häufiger ich gemessen hatte,
> desto weiter ging die Temp hoch. Dagegen nach 10 Sekunden Pause war es
> wieder realistisch.

Jede Sekunde :) Oh ok, so ähnlich wie bei den DS18B20 dann, die sich 
beim Messen auch selbst erwärmen.

>
> Aber der interne ist echt nur ein Schätzeisen. Ich nehm normal kleine
> NTCs in 0603 wenn ich eine Umgebungstemperatur brauche. Die sind billig,
> klein und leicht auszuwerten.

Hast du da zufällig eine erprobte Schaltung mit C-Code-Stückchen, das du 
teilen würdest?

Sowas würde ich doch dann echt in jeder zukünftigen Anwendung 
standardmäßig auf die Platine bauen :)

: Bearbeitet durch User
von Gerd E. (robberknight)


Angehängte Dateien:

Lesenswert?

Mampf F. schrieb:
>> Aber der interne ist echt nur ein Schätzeisen. Ich nehm normal kleine
>> NTCs in 0603 wenn ich eine Umgebungstemperatur brauche. Die sind billig,
>> klein und leicht auszuwerten.
>
> Hast du da zufällig eine erprobte Schaltung mit C-Code-Stückchen, das du
> teilen würdest?

Klar, kein Problem. Schaltung siehe Bild anbei.

Den Widerstand kann man etwas an den 25°C-Widerstand des NTC und die zu 
erwartenden Temperaturwerte anpassen. Für gängige Raumtemperaturen 
funktioniert ein 10K-NTC und 10K als Teiler bei mir bisher gut.

Das wäre einfacher Code zum Auswerten, hier für einen 12 Bit ADC und 
einen Controller mit Floatingpoint:
1
float calculate_ntc_temp(unsigned int adcvalue, unsigned int res_25c, unsigned int beta_constant, unsigned int res_divide)
2
{
3
    float resistance = (float)res_divide / ((4096 / (float)adcvalue) -1);
4
    
5
    // calculate the temp in celsius using the Steinhart-Hart equation
6
    float temp = (((float)beta_constant * 298.15) / ( (float)beta_constant + log( resistance / (float)res_25c) * 298.15)) - 273.15;
7
    
8
    return temp;
9
}

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Gerd E. schrieb:
> Klar, kein Problem. Schaltung siehe Bild anbei.

Vielen Dank! :)

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.