1
0
mirror of https://github.com/rfivet/stm32bringup.git synced 2025-02-21 05:37:09 -05:00
stm32bringup/docs/35_calibrate.html

230 lines
8.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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℃. Thats 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 dont 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 its 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 &lt;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>