mirror of
https://github.com/rfivet/stm32bringup.git
synced 2025-10-18 08:54:01 -04:00
230 lines
8.1 KiB
HTML
230 lines
8.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>3.5 Internal Temperature Sensor Calibration</title>
|
|
<link type="text/css" rel="stylesheet" href="style.css">
|
|
</head>
|
|
<body>
|
|
<h1>3.5 Internal Temperature Sensor Calibration</h1>
|
|
|
|
When it comes to temperature measurement, the documented factory
|
|
calibration of STM32F030 family members does not provide enough
|
|
information to make a valid estimation. The datasheet tells us that the
|
|
value stored is the raw data of ADC conversion of the temperature sensor
|
|
reading at 3.3V (±10mV) and 30℃ (±5℃).
|
|
<p>
|
|
<img alt="TS_CAL1" src="img/34_tscal1.png">
|
|
<p>
|
|
There is only one point calibration documented and its reference
|
|
temperature is known with a precision of ±5℃. That's not enough to
|
|
calculate temperature but it shows that the sensor was tested in
|
|
production.
|
|
<p>
|
|
Notice that the calibration value name is <b>TS_CAL1</b>, some other STM32
|
|
chipset families do have a second temperature factory calibration point
|
|
<b>TS_CAL2</b>. Also some don't have any factory calibration stored at all.
|
|
So you have to refer to the datasheet that matches the chip you are
|
|
targeting when you port your code to a new chipset.
|
|
<p>
|
|
<img alt="Temperature Sensor Characteristics" src="img/35_tschar.png">
|
|
<p>
|
|
The sensor linearity with temperature is at worst ±2℃. So I am curious
|
|
to see how it performs over a range of temperature.
|
|
<p>
|
|
If you are so lucky that you have picked a STM32F030 chip that has the
|
|
typical average slope of <b>4.3 mV/℃</b> and was calibrated in factory at
|
|
exactly <b>3.3V</b> and <b>30℃</b>, then you could use the following formula
|
|
to calculate the temperature.
|
|
<p>
|
|
<b>T = 30 + (TS_CAL1 * 3.3 - TS_RAW * VDDA) / 4095 / 0.0043</b>
|
|
<p>
|
|
with
|
|
<p>
|
|
<b>VDDA = 3.3 * VREFINT_CAL / V_RAW</b>
|
|
<p>
|
|
that gives
|
|
<p>
|
|
<b>T = 30 + 3.3 * (TS_CAL1 - TS_RAW * V_CAL / V_RAW) / 4095 / 0.0043</b>
|
|
<p>
|
|
If I express the average slope in raw ADC units per ℃ instead of mV/℃
|
|
<p>
|
|
<b>5.336 = 4095 * 0.0043 / 3.3</b>
|
|
<p>
|
|
the final formula is
|
|
<p>
|
|
<b>T = 30 + (TS_CAL1 - TS_RAW * V_CAL / V_RAW) * 1000 / 5336</b>
|
|
<p>
|
|
which matches the sample code for temperature computation given in the
|
|
reference manual (RM0360 A.7.16).
|
|
|
|
<pre>
|
|
/* Temperature sensor calibration value address */
|
|
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
|
|
#define VDD_CALIB ((uint32_t) (3300))
|
|
#define VDD_APPLI ((uint32_t) (3000))
|
|
#define AVG_SLOPE ((uint32_t) (5336)) /* AVG_SLOPE in ADC conversion step
|
|
(@3.3V)/°C multiplied by 1000 for
|
|
precision on the division */
|
|
int32_t temperature; /* will contain the temperature in degrees Celsius */
|
|
temperature = ((uint32_t) *TEMP30_CAL_ADDR
|
|
- ((uint32_t) ADC1->DR * VDD_APPLI / VDD_CALIB)) * 1000;
|
|
temperature = (temperature / AVG_SLOPE) + 30;
|
|
</pre>
|
|
|
|
If I use the raw ADC readings from my last run
|
|
<p>
|
|
<b>VDDA = 3.3 * 1526 / 1538 = 3.274V</b>
|
|
<p>
|
|
<b>t = 30 + (1721 - 1718 * 1526 / 1538) * 1000 / 5336 = 33.07℃</b>
|
|
<p>
|
|
I confirm the voltage with a voltmeter (measured 3.282V versus 3.274V
|
|
computed). The computed internal temperature value is roughly 5℃ higher
|
|
than the room temperature.
|
|
|
|
<h2>Undocumented calibration data</h2>
|
|
|
|
For the STM32F030Fx, the 5KB space before the RAM contains the System
|
|
Memory (3KB) and the option bytes.
|
|
<pre>
|
|
| Content | Start Address | Size |
|
|
|---------------|---------------|------|
|
|
| System Memory | 0x1FFFEC00 | 3KB |
|
|
| Option Bytes | 0x1FFFF800 | 2KB |
|
|
| RAM Memory | 0x20000000 | 4KB |
|
|
</pre>
|
|
The calibration data are saved in the last 96 bytes of the System
|
|
Memory, starting at address 0x1FFFF7A0. So it's simple to dump the
|
|
content of that zone and compare the values for multiple chips.
|
|
|
|
<pre>
|
|
$ stm32flash -r - -S 0x1FFFF7a0:96 COM3 2>/dev/null | hexdump -C
|
|
00000000 ff ff ff ff 31 00 10 00 ff ff ff ff 1c 00 3a 00 |....1.........:.|
|
|
00000010 12 57 34 41 38 32 30 20 b9 06 f6 05 f0 ff ff ff |.W4A820 ........|
|
|
00000020 ff ff 11 05 ff ff ff ff fc ff ff ff 10 00 ff ff |................|
|
|
00000030 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
|
|
00000040 ff ff ff ff ff ff ff ff f3 ff ff ff ff ff ff ff |................|
|
|
00000050 ff ff ff ff ff ff ff ff 68 97 52 ad 3b c4 3f c0 |........h.R.;.?.|
|
|
00000060
|
|
</pre>
|
|
<pre>
|
|
| Location | Content | Size | F030 | F0x1/2/8 |
|
|
|------------|-----------------|------|------|----------|
|
|
| 0x1FFFF7AC | Unique ID | 12 | | √ |
|
|
| 0x1FFFF7B8 | TS_CAL1 | 2 | √ | √ |
|
|
| 0x1FFFF7BA | VREFINT_CAL | 2 | √ | √ |
|
|
| 0x1FFFF7C2 | TS_CAL2 | 2 | | √ |
|
|
| 0x1FFFF7CC | Flash size (KB) | 2 | √ | √ |
|
|
</pre>
|
|
This is the same layout as the one documented in <b>RM0091 <i>Reference Manual
|
|
STM32F0x1/STM32F0x2/STM32F0x8</i></b> which includes the following sample code
|
|
for temperature computation.
|
|
|
|
<pre>
|
|
/* Temperature sensor calibration value address */
|
|
#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
|
|
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
|
|
#define VDD_CALIB ((uint16_t) (330))
|
|
#define VDD_APPLI ((uint16_t) (300))
|
|
int32_t temperature; /* will contain the temperature in degrees Celsius */
|
|
temperature = ((int32_t) ADC1->DR * VDD_APPLI / VDD_CALIB)
|
|
- (int32_t) *TEMP30_CAL_ADDR;
|
|
temperature *= (int32_t)(110 - 30);
|
|
temperature /= (int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR);
|
|
temperature += 30;
|
|
</pre>
|
|
|
|
Factoring in the actual measured voltage, this gives
|
|
<p>
|
|
<b>T = 30 + (TS_CAL1 - TS_RAW * V_CAL / V_RAW) * 80 / (TS_CAL1 - TS_CAL2)</b>
|
|
<p>
|
|
If I use the raw ADC readings from my last run
|
|
|
|
<pre>
|
|
TSCAL2_SLOPE = (1721 - 1297) / 80 = 5.3 ADC step/℃
|
|
= 3.3 * 5.3 / 4095 = 4.271 mV/℃
|
|
</pre>
|
|
|
|
<b>t = 30 + (1721 - 1718 * 1526 / 1538) * 80 / (1721 - 1297) = 33.09℃</b>
|
|
<p>
|
|
Which is only 0.02℃ higher than the previous result based on the more
|
|
generic formula. Because the temperature measured is close to the
|
|
calibration temperature, the correction is negligible. For this
|
|
particular chip, to see a difference of 0.1℃ between the value computed
|
|
by the two formulas, you need a delta of 15℃ from the calibration
|
|
temperature.
|
|
|
|
<h2>Tuning</h2>
|
|
|
|
The factory calibration temperature is defined as 30℃ (±5℃). I can store
|
|
a reference temperature value in the first User Data Option Byte. This
|
|
way I don't need to modify the code for each chip.
|
|
<p>
|
|
I update the application to use <b>TS_CAL2</b> based temperature calculation
|
|
and access the tuned reference temperature from the option bytes.
|
|
|
|
<pre>
|
|
/* adcmain.c -- ADC reading of reference voltage and temperature sensor */
|
|
|
|
#include <stdio.h>
|
|
#include "system.h"
|
|
|
|
#define RAW
|
|
|
|
#define TS_CAL2 ((const short *) 0x1FFFF7C2)
|
|
#define USER0 ((const unsigned char *) 0x1FFFF804)
|
|
|
|
int main( void) {
|
|
unsigned last = 0 ;
|
|
short calV, calC ;
|
|
|
|
/* Initialize ADC and fetch calibration values */
|
|
adc_vnt( VNT_INIT, &calV, &calC) ;
|
|
#ifdef RAW
|
|
printf( "%u, %u\n", calV, calC) ;
|
|
|
|
int baseC = 300 ;
|
|
# ifdef USER0
|
|
if( 0xFF == (USER0[ 0] ^ USER0[ 1]))
|
|
baseC = USER0[ 0] * 10 ;
|
|
# endif
|
|
#endif
|
|
|
|
for( ;;)
|
|
if( uptime == last)
|
|
yield() ;
|
|
else {
|
|
short Vsample, Csample ;
|
|
|
|
last = uptime ;
|
|
#ifdef RAW
|
|
adc_vnt( VNT_RAW, &Vsample, &Csample) ;
|
|
printf( "%i, %i, %i, %i, ", calV, Vsample, calC, Csample) ;
|
|
Csample = baseC + (calC - (int) Csample * calV / Vsample)
|
|
# ifdef TS_CAL2
|
|
* 800 / (calC - *TS_CAL2) ;
|
|
# else
|
|
* 10000 / 5336 ;
|
|
# endif
|
|
Vsample = 330 * calV / Vsample ;
|
|
#else
|
|
adc_vnt( VNT_VNC, &Vsample, &Csample) ;
|
|
#endif
|
|
printf( "%i.%i, %i.%i\n", Vsample / 100, Vsample % 100,
|
|
Csample / 10, Csample % 10) ;
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<h2>Checkpoint</h2>
|
|
|
|
I have added an <a href="AA_factory.html">appendix</a> to track the factory
|
|
written content.
|
|
<p>
|
|
<a href="36_update.html">Next</a>, I will cover the toolchain update that I
|
|
made while working on the temperature sensors.
|
|
|
|
<hr>© 2020-2024 Renaud Fivet
|
|
</body>
|
|
</html>
|