Forum: Mikrocontroller und Digitale Elektronik STM32F103 interner Temperatursensor


von Max M. (maxmicr)


Lesenswert?

Ich versuche aktuell, die Formel für die Berechnung der Temperatur des 
internen Sensors zu verstehen. Den Wert, den ich vom ADC zurückbekomme 
ist 1733. Die Formel lautet:

((V25-Vsense) / Slope) + 25

V25 ist 1.43V, Vsense wäre:

1733 * 3.0/4096 = 1.27V

((1.43V-1.27V)*1000 / 4.3mV/°C) + 25°C = 62°C

Das ist schon recht weit von der Wirklichkeit entfernt, hab ich 
irgendwas in meinen Berechnung vergessen / übersehen?
Sind 3.0V nun die Referenzspannung? Wenn ich Channel 17 messe, der laut 
dieser Application Note mit Vrefint verbunden ist 
(http://www.st.com/content/ccc/resource/technical/document/application_note/b9/21/44/4e/cf/6f/46/fa/DM00035957.pdf/files/DM00035957.pdf/jcr:content/translations/en.DM00035957.pdf) 
bekomme ich den Wert 2080 zurück. Damit kann ich doch jetzt nicht viel 
anfangen, da ich die Referenzspannung nicht weiß und somit nicht auf die 
tatsächliche Spannung schließen kann?

Im selben Abschnitt ist noch diese Formel gegeben:

Val_VREFINT = VREFINT 2^12 / VREF+ =  VREFINT x 4096 / VDDA

Von der kenne ich aber Vref+ und VDDA nicht? Ich blick da nicht durch.

: Bearbeitet durch User
von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

Fuer modernere STM32 als den F1 gibt es im "ROM" Kalibarationswerte, die 
bei Chiptest bei 30 und 110 und 3.3V gemessen wurden. Setzt man die 
Werte aus dem Rom in die Formel ein, bekommt man deutlich realistischere 
Werte.

Fuer den F103 finde ich keine Hinweis, dass diese Kalibrationswerte 
verfuegbar sind. Wenn Du nur die typischen Datenblattwerte nimmt, musst 
Du mit grossen Toleranzen leben, kannst aber immer noch Tendenzen sehen. 
Zwischen V25 Max und Min liegen 180 mV, bei Min Avg-Slope 4 mV sind das 
45 (+/- 22.5) Grad moegliche Abweichung.

Ausserdem musst Du die Samplezeiten fuer den Temperaturkanal sehr lang 
waehlen, den richtigen Kanal wahlen und TSVREFE richtig gesetzt haben. 
Viel raum also fuer Fehler. Lass Dir doch erstmal nur die Spannung des 
Temperatursensor ausgeben und schau ob die Werte ungefaehr passen.

von Max M. (maxmicr)


Lesenswert?

Uwe B. schrieb:
> Ausserdem musst Du die Samplezeiten fuer den Temperaturkanal sehr lang
> waehlen, den richtigen Kanal wahlen und TSVREFE richtig gesetzt haben.
> Viel raum also fuer Fehler.

So sieht die ADC-Initialisierung aus:
1
    RCC->APB2ENR |= (1<<9); //Enable ADC1 Clock
2
    RCC->CFGR |= (0b10<<14); //set ADC Prescaler '6'
3
    ADC1->SMPR1 |= (0b100<<18); //41.5 cycles sample time
4
    ADC1->CR2 |= (1<<23); //Enable Temperature Sensor & Vref
5
6
    ADC1->CR2 |= (1<<0); //Enable ADC and start conversion
7
8
    ADC1->CR2 |= (1<<3); //Initialize calibration register
9
    while(ADC1->CR2 & (1<<3)){ //Wait until calibration register initialized
10
        ;
11
    }
12
13
    ADC1->CR2 |= (1<<2); //Enable calibration
14
    while(ADC1->CR2 & (1<<2)){ //Wait until calibration completed
15
        ;
16
    }

und so das Auslesen:
1
uint32_t getADCTempValue(uint16_t channel){
2
  ADC1->SQR3 |= (channel<<0);
3
    ADC1->CR2 |= (1<<0); //Enable ADC and start conversion
4
    while(!(ADC1->SR & (1<<1))){ //Wait until end of conversion
5
        ;
6
    }
7
    return ADC1->DR;
8
}

Uwe B. schrieb:
> Lass Dir doch erstmal nur die Spannung des
> Temperatursensor ausgeben

Die Spannung direkt bekomme ich doch nicht, oder? Sondern nur einen 
Wert, den ich mit der Formel:

adc_value *  Vref / (2^12)

in eine Spannung umrechnen kann.

: Bearbeitet durch User
von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

Max M. schrieb:
>     RCC->CFGR |= (0b10<<14); //set ADC Prescaler '6'
>     ADC1->SMPR1 |= (0b100<<18); //41.5 cycles sample time

Gibt das die in rm0008 geforderten mindestens 17.1 us?
(
> Die Spannung direkt bekomme ich doch nicht, oder? Sondern nur einen
> Wert, den ich mit der Formel:
>
> adc_value *  Vref / (2^12)
>
> in eine Spannung umrechnen kann.

Ja, genau, lass der den ADC Wert als Spannung oder einfach nur als Wert 
ausgeben und schau als erstes, ob der Wert der Erwartung entspricht. Mit 
mit "finger drauf" und/oder Kaeltespray kanns Du dann die Chiptemperatur 
veraendern un dem Wert beobachten.

von Max M. (maxmicr)


Lesenswert?

Uwe B. schrieb:
> Ja, genau, lass der den ADC Wert als Spannung oder einfach nur als Wert
> ausgeben und schau als erstes, ob der Wert der Erwartung entspricht.

Ich hab den Wert 1750 bekommen, laut diversen Tutorials im Internet 
entspräche ein Wert von 1750 ca. 25°C (darauf komm ich rechnerisch 
auch, wenn 3.3V Vref wäre). Der Wert würde also ungefähr passen (gefühlt 
sind es aber eher 23°C).

Das Board unter eine Glühlampe zu halten, senkt den Wert.

Uwe B. schrieb:
> Gibt das die in rm0008 geforderten mindestens 17.1 us?

Der ADC Prescaler ist auuf 6, also wäre die Frequenz: 72MHz / 6 = 
12MHz. Das sind 12 * 10^6 Cycles pro Sekunde. Jeder Cycle braucht also 
12us, wenn 41.5 cycles gewartet wird, entspricht das 498u us, müsste 
also rein rechnerisch passen, oder?

Edit: Das stimmt ja gar nicht, ein Cycle braucht 1 / 12MHz, also 
8.3*(10^-8)s. 41.5 cycles brauchen also 3.46us.

: Bearbeitet durch User
von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

Max M. schrieb:
> 41.5 cycles brauchen also 3.46us.

Das ist zu kurz.

Setze doch erst einmal auf maximale Sampledauer (111 statt 100) in 
ADC_SMPRx.

von Max M. (maxmicr)


Lesenswert?

Uwe B. schrieb:
> Setze doch erst einmal auf maximale Sampledauer (111 statt 100) in
> ADC_SMPRx.

Hab ich gemacht, jetzt hat sich der Wert leicht nach oben korrigiert, 
auf 1755. Ich hab inzwischen eine Vermutung aufgestellt, wie die 
Formel funktionieren könnte. Um die Spannung zu bekommen, die der 
Temperatursensor liefert, muss man diesen mit der Referenzspannung und 
der Auflösung des ADC verrechnen (wie oben schon angemerkt). Dann läuft 
das so:

(V25 - Vtemp) * 1000 / 4.3 + 25

Soweit ich das sehe, bekommt man aber den richtigen Wert von V25 nicht 
raus, da mein STM32F103 keinen Referenzpin hat, er liegt aber wohl im 
Bereich zwischen 1.34V und 1.52V.

Mit der oben genannten Formel komme ich dann auf ~25°C.

von Jakob (Gast)


Lesenswert?

Hm, kenne mich ja nur mit AVRs aus, aber wenn ich beim STM32F103
in's Datenblatt schaue, kann ich die interne Temperatur-Erfassung
mit 12 Bit Auflösung nicht sooo toll finden:

Guckt man tiefer in's Datenblatt:

2.3.28 Temperature sensor
-------------------------
The temperature sensor has to generate a voltage that varies
linearly with temperature. The conversion range is between
2 V < V DDA < 3.6 V.

Erkenntnis 1:
=============
2 V ... 3,6 V an V_DDA macht einen großen Unterschied
- Da sollte man schon mal wissen, was an V_DDA anliegt - und
  in die Berechnung einfließen lassen.


5.3.21 Temperature sensor characteristics
-----------------------------------------
Für 25 °C liefert der Sensor 1,34...1,52 V
Die Änderung für 1 °C ist 4,0 mV/°C ... 4,6 mV/°C

Bei 4,3 mV / °C entspricht die Spanne von 1,52 V - 1,34 V
= 0,18 V = 180 mV, einer Unsicherheits-Spanne von
180 mV / 4,3 mV/°C = 41 °C

Erkenntnis 2:
=============
25 °C +/- 20,5 °C sind für Absolut-Messung SEHR ungeeignet.
Ohne eine Kalibrierung ist also nur Müll zu messen....

Fazit:
======
Deine schlechten Testergebnisse zeigen einfach nur die
grottenschlechte Temperaturerfassung des STM32F103.

von Gerd E. (robberknight)


Lesenswert?

Jakob schrieb:
> The temperature sensor has to generate a voltage that varies
> linearly with temperature. The conversion range is between
> 2 V < V DDA < 3.6 V.
>
> Erkenntnis 1:
> =============
> 2 V ... 3,6 V an V_DDA macht einen großen Unterschied
> - Da sollte man schon mal wissen, was an V_DDA anliegt - und
>   in die Berechnung einfließen lassen.

Das hast du falsch verstanden: Vdda, die Versorgungsspannung für den 
Analogbereich des µCs, muss innherhalb von 2V bis 3,6V liegen, damit der 
Temperatursensor spezifikationsgemäß funktioniert.

Wo Vdda innerhalb dieses Bereichs genau liegt, ist nicht so spannend:

Zuerst misst Du den Wert der internen Referenz. Deren Wert kennst Du 
relativ genau, daraus kannst Du dann den aktuellen Wert von Vdda 
zurückrechnen.

Und damit liest Du nun den Temperatursensor aus.

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Gerd E. schrieb:
> Zuerst misst Du den Wert der internen Referenz. Deren Wert kennst Du
> relativ genau, daraus kannst Du dann den aktuellen Wert von Vdda
> zurückrechnen

Dafür hatte ich ja diese Formel:

Val_Vrefint = Vrefint * 4096 / Vdda

Vdda (also die Versorgungsspannung) ist in meinem Fall 3.3V. Der 
gemessene Wert von Channel 17 (Val_Vrefint nach: 
http://www.st.com/content/ccc/resource/technical/document/application_note/b9/21/44/4e/cf/6f/46/fa/DM00035957.pdf/files/DM00035957.pdf/jcr:content/translations/en.DM00035957.pdf) 
schwankt zwischen diesen Werten:
1
1983
2
2189
3
2008
4
2199
5
2016
6
2202

Vrefint = Val_Vrefint * Vdda / 4096

Mit einem Wert von z.B. 2060 für Val_Vrefint:

Vrefint = 2060 * 3.3V / 4096 = 1.66V

Kommt mir irgendwie wenig vor?

Gerd E. schrieb:
> daraus kannst Du dann den aktuellen Wert von Vdda
> zurückrechnen.

Okay, wie würde das funktionieren?

: Bearbeitet durch User
von Demo (Gast)


Lesenswert?

Wenn du im Ref.Manual mal ganz unten schaust sollte es ein Code-Beispiel 
geben, das dir das ganze auch direkt in °C umrechnet. Die Rechnung ist 
echt Hahnebüchen, aber funktioniert.

von Demo (Gast)


Lesenswert?

Here we go.
1
#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
2
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
3
#define VDD_CALIB ((uint16_t) (330))
4
#define VDD_APPLI ((uint16_t) (300))
5
int32_t temperature; /* will contain the temperature in degree Celsius */
6
temperature = (((int32_t) ADC1->DR * VDD_APPLI / VDD_CALIB) - (int32_t) *TEMP30_CAL_ADDR );
7
temperature = temperature * (int32_t)(110 - 30);
8
temperature = temperature / (int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR);
9
temperature = temperature + 30;

von Gerd E. (robberknight)


Lesenswert?

Max M. schrieb:
> Vdda (also die Versorgungsspannung) ist in meinem Fall 3.3V.

Konstant, z.B. mit Spannungsregler?

> Der
> gemessene Wert von Channel 17 (Val_Vrefint)...
> schwankt zwischen diesen Werten:
>
>
1
> 1983
2
> 2189
3
> 2008
4
> 2199
5
> 2016
6
> 2202
7
>

Während Du konstante 3.3V an Vdda anliegen hast?

Dann ist schon an der Stelle was ziemlich faul:

Spannungsversorgung mit Kondensatoren nach Datenblatt genau des 
verwendeten Controllers abgeblockt? Siehe u.a. Fig. 14 im Datenblatt.

ADC nach jedem Booten des µC kalibriert?

ADC läuft die ganzen 17.1µs für das Sampeln der Vrefint?

Den richtigen ADC verwendet? Lt. Refman ist der Vrefint nur an ADC1 
verfügbar.

Geh auch nochmal das Errata-Sheet durch, vielleicht gibt es irgendwelche 
Pins bei deren Benutzung der ADC empfindlich reagiert. So ähnlich wie 
PC13, PC14, PC15.

Die Vrefint muss lt. Datenblatt Tabelle 5.3.4 zwischen 1.16V und 1.24V 
liegen. Bei 3,3V und 1,2V kommt da 1487 raus, Dein Wert sollte da 
irgendwo in der Nähe liegen.

von Max M. (maxmicr)


Lesenswert?

Gerd E. schrieb:
> Konstant, z.B. mit Spannungsregler?

Hm, der Strom kommt von einem USB / UART Konverter, auf dem Board 
(dieses: http://img.dxcdn.com/productimages/sku_395848_2.jpg) ist einer 
drauf.

Gerd E. schrieb:
> Während Du konstante 3.3V an Vdda anliegen hast?

Okay, gerade nachgemessen: Es sind 3.24V

Gerd E. schrieb:
> Spannungsversorgung mit Kondensatoren nach Datenblatt genau des
> verwendeten Controllers abgeblockt? Siehe u.a. Fig. 14 im Datenblatt.

Ich benutze ein fertiges Board, ich gehe mal davon aus, dass es richtig 
verschaltet ist, auch wenn es Chinaware ist.

Gerd E. schrieb:
> ADC nach jedem Booten des µC kalibriert?
>
> ADC läuft die ganzen 17.1µs für das Sampeln der Vrefint?
>
> Den richtigen ADC verwendet? Lt. Refman ist der Vrefint nur an ADC1
> verfügbar.

Nicht böse gemeint, aber mein Code steht hier: 
Beitrag "Re: STM32F103 interner Temperatursensor"

Der erfüllt, denke ich, die Anforderungen.
Die Anzahl der Wartezyklen wurde inzwischen auf 239.5 cycles erhöht, das 
dürfte passen (Nach 5.3.18 entsprechen 239.5 cycles 17.1us bei 14MHz 
ADC-Frequenz, da mein Prescaler bei 6 liegt, ist die Frequenz 12 MHz).

Gerd E. schrieb:
> Die Vrefint muss lt. Datenblatt Tabelle 5.3.4 zwischen 1.16V und 1.24V
> liegen. Bei 3,3V und 1,2V kommt da 1487 raus, Dein Wert sollte da
> irgendwo in der Nähe liegen.

Ich hab gerade mal debugged. Wenn ich durchsteppe, bekomme ich auf dem 
Terminal Window auf einmal eine ziemlich stabile Zahl um 1513. Warum 
ändert sich der Wert auf über 2000 wenn ich den Controller nicht 
debugge? Mit einem Wert von 1513 und 3.24V kommt man auf 1.20V.

: Bearbeitet durch User
von Gerd E. (robberknight)


Lesenswert?

Max M. schrieb:
>> Konstant, z.B. mit Spannungsregler?
>
> Hm, der Strom kommt von einem USB / UART Konverter, auf dem Board
> (dieses: http://img.dxcdn.com/productimages/sku_395848_2.jpg) ist einer
> drauf.

Vielleicht nicht perfekt, verursacht aber vermutlich nicht solche 
Sprünge.

Häng es vielleicht aber zur Sicherheit dennoch mal an nen anderen 
USB-Hub, USB-Port oder PC. Ich hatte schon einen USB-Hub mit einem 
Netzteil, dessen Kondensatoren defekt waren. Das machte ähnliche Effekte 
schon unter leichter Last.

>> Spannungsversorgung mit Kondensatoren nach Datenblatt genau des
>> verwendeten Controllers abgeblockt? Siehe u.a. Fig. 14 im Datenblatt.
>
> Ich benutze ein fertiges Board, ich gehe mal davon aus, dass es richtig
> verschaltet ist, auch wenn es Chinaware ist.

Da hab ich so meine Zweifel. Kontrolliere das bitte. Vor allem wenn Du 
solche Fehler siehst. Kerkos nachmessen oder gegen welche tauschen bei 
denen Du Dir wegen der Werte sicher bist.

> Ich hab gerade mal debugged. Wenn ich durchsteppe, bekomme ich auf dem
> Terminal Window auf einmal eine ziemlich stabile Zahl um 1513.

So sollte der Wert sein.

> Warum
> ändert sich der Wert auf über 2000 wenn ich den Controller nicht
> debugge?

Es gibt 2 Möglichkeiten:

a) Software

Dein Programm macht im Hintergrund (Interrupts, PWM,...) irgendetwas, 
was den ADC durcheinanderbringt. Beim Debuggen läuft das nicht, oder nur 
so langsam, daß es keinen Schaden anrichtet.

b) Hardware

Beim Debuggen zieht der µC nicht kontinuierliche Peaks aus den falsch 
dimensionierten Kondensatoren, sondern zwischen den Debugger-Steps haben 
die genug Zeit sich wieder aufzuladen.

oder

Beim Debuggen wartet der µC meist auf den nächsten Befehl, dadurch 
entstehen nicht so starke Schwankungen auf Masse / Vcc. Die bringen den 
ADC durchandender wg. schlechtem Routing oder ähnlichem.

: Bearbeitet durch User
von Jakob (Gast)


Lesenswert?

Gerd E. (robberknight) schrieb:

> das hast du falsch verstanden...

Dass der Grundwert für 25 °C mit (typically) +/-20,5 °C Unsicherheit
belastet ist, muss man nur als Pipifax abtun...

Schon hat man: Endlosen Spaß beim weiteren Schwafeln!

von Gerd E. (robberknight)


Lesenswert?

Jakob schrieb:
> Gerd E. (robberknight) schrieb:
>
>> das hast du falsch verstanden...
>
> Dass der Grundwert für 25 °C mit (typically) +/-20,5 °C Unsicherheit
> belastet ist, muss man nur als Pipifax abtun...

Wo habe ich das geschrieben oder angedeutet?

Ich bin nur auf Deine "Erkenntnis 1" eingegangen, weil die meiner 
Meinung nach nicht korrekt war. Das sagt nichts über Deine "Erkenntnis 
2" aus.

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Gerd E. schrieb:
> a) Software
>
> Dein Programm macht im Hintergrund (Interrupts, PWM,...) irgendetwas,
> was den ADC durcheinanderbringt. Beim Debuggen läuft das nicht, oder nur
> so langsam, daß es keinen Schaden anrichtet.

Hm, Interrupts und PWM hab ich nicht im Code. Fällt euch da etwas auf?
1
#include "stm32f10x_gpio.h"
2
#include <stdio.h>
3
#include <string.h>
4
5
void delay(long cycles);
6
void delay(long cycles){
7
  while(cycles > 0)
8
    cycles--;
9
}
10
11
void initADC(){
12
    RCC->APB2ENR |= (1<<9); //Enable ADC1 Clock
13
    RCC->CFGR |= (0b11<<14); //set ADC Prescaler '8'
14
15
    ADC1->SMPR1 |= (0b111<<18); //239.5 cycles sample time
16
    ADC1->CR2 |= (1<<23); //Enable Temperature Sensor & Vref
17
    ADC1->CR2 |= (1<<0); //Enable ADC and start conversion
18
    ADC1->CR2 |= (1<<3); //Initialize calibration register
19
    while(ADC1->CR2 & (1<<3)){ //Wait until calibration register initialized
20
        ;
21
    }
22
23
    ADC1->CR2 |= (1<<2); //Enable calibration
24
    while(ADC1->CR2 & (1<<2)){ //Wait until calibration completed
25
        ;
26
    }
27
}
28
29
uint32_t getADCTempValue(uint16_t channel){
30
  ADC1->SQR3 |= (channel<<0);
31
    ADC1->CR2 |= (1<<0); //Enable ADC and start conversion
32
    while(!(ADC1->SR & (1<<1))){ //Wait until end of conversion
33
        ;
34
    }
35
    return ADC1->DR;
36
}
37
38
void initUART(){
39
  RCC->APB2ENR |= (1<<2); //Port A clock enable
40
41
  GPIOA->CRH |= (0xB << 4); //Pin A9 as Alternative PP & 50MHz Output mode
42
43
  RCC->APB2ENR |= (1<<14); //USART1 Clock enable
44
45
  USART1->CR1 |= (1<<13); //Enable UART
46
  USART1->BRR = 72000000 / 9600; //72MHZ / 9600 BAUD
47
  USART1->CR1 |= (1<<3); //Enable Transmitter
48
}
49
50
void sendChar(char c){
51
  while(!(USART1->SR & (1<<7))){ //Wait until data is transferred to shift register
52
    ;
53
  }
54
  USART1->DR = c;
55
  while(!(USART1->SR & (1<<6))){ //Wait until transmission is complete
56
    ;
57
  }
58
}
59
60
void sendString(const char *str){
61
  while(*str){
62
    sendChar(*str++);
63
  }
64
}
65
66
int main(void){
67
  initUART();
68
  initADC();
69
  char buffer[100];
70
  uint32_t adcVal = 0;
71
  while(1){
72
    adcVal = getADCTempValue(17);
73
    itoa((int)adcVal,buffer,10);
74
    sendString("ADC-Value: ");
75
    sendString(buffer);
76
77
    sendChar('\n');
78
    delay(500000);
79
  }
80
}

: Bearbeitet durch User
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.