mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik STM32F103 interner Temperatursensor


Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht 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/d...) 
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
Autor: Uwe Bonnes (Firma: TU Darmstadt) (uwebonnes)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht 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:
    RCC->APB2ENR |= (1<<9); //Enable ADC1 Clock
    RCC->CFGR |= (0b10<<14); //set ADC Prescaler '6'
    ADC1->SMPR1 |= (0b100<<18); //41.5 cycles sample time
    ADC1->CR2 |= (1<<23); //Enable Temperature Sensor & Vref

    ADC1->CR2 |= (1<<0); //Enable ADC and start conversion

    ADC1->CR2 |= (1<<3); //Initialize calibration register
    while(ADC1->CR2 & (1<<3)){ //Wait until calibration register initialized
        ;
    }

    ADC1->CR2 |= (1<<2); //Enable calibration
    while(ADC1->CR2 & (1<<2)){ //Wait until calibration completed
        ;
    }

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

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
Autor: Uwe Bonnes (Firma: TU Darmstadt) (uwebonnes)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Uwe Bonnes (Firma: TU Darmstadt) (uwebonnes)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jakob (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Gerd E. (robberknight)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht 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/d...) 
schwankt zwischen diesen Werten:
1983
2189
2008
2199
2016
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
Autor: Demo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Demo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Here we go.

#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 degree Celsius */
temperature = (((int32_t) ADC1->DR * VDD_APPLI / VDD_CALIB) - (int32_t) *TEMP30_CAL_ADDR );
temperature = temperature * (int32_t)(110 - 30);
temperature = temperature / (int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR);
temperature = temperature + 30;


Autor: Gerd E. (robberknight)
Datum:

Bewertung
1 lesenswert
nicht 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:
>
>
> 1983
> 2189
> 2008
> 2199
> 2016
> 2202
> 

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.

Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Gerd E. (robberknight)
Datum:

Bewertung
1 lesenswert
nicht 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
Autor: Jakob (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Gerd E. (robberknight)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Max MMM (maxmicr)
Datum:

Bewertung
0 lesenswert
nicht 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?
#include "stm32f10x_gpio.h"
#include <stdio.h>
#include <string.h>

void delay(long cycles);
void delay(long cycles){
  while(cycles > 0)
    cycles--;
}

void initADC(){
    RCC->APB2ENR |= (1<<9); //Enable ADC1 Clock
    RCC->CFGR |= (0b11<<14); //set ADC Prescaler '8'

    ADC1->SMPR1 |= (0b111<<18); //239.5 cycles sample time
    ADC1->CR2 |= (1<<23); //Enable Temperature Sensor & Vref
    ADC1->CR2 |= (1<<0); //Enable ADC and start conversion
    ADC1->CR2 |= (1<<3); //Initialize calibration register
    while(ADC1->CR2 & (1<<3)){ //Wait until calibration register initialized
        ;
    }

    ADC1->CR2 |= (1<<2); //Enable calibration
    while(ADC1->CR2 & (1<<2)){ //Wait until calibration completed
        ;
    }
}

uint32_t getADCTempValue(uint16_t channel){
  ADC1->SQR3 |= (channel<<0);
    ADC1->CR2 |= (1<<0); //Enable ADC and start conversion
    while(!(ADC1->SR & (1<<1))){ //Wait until end of conversion
        ;
    }
    return ADC1->DR;
}

void initUART(){
  RCC->APB2ENR |= (1<<2); //Port A clock enable

  GPIOA->CRH |= (0xB << 4); //Pin A9 as Alternative PP & 50MHz Output mode

  RCC->APB2ENR |= (1<<14); //USART1 Clock enable

  USART1->CR1 |= (1<<13); //Enable UART
  USART1->BRR = 72000000 / 9600; //72MHZ / 9600 BAUD
  USART1->CR1 |= (1<<3); //Enable Transmitter
}

void sendChar(char c){
  while(!(USART1->SR & (1<<7))){ //Wait until data is transferred to shift register
    ;
  }
  USART1->DR = c;
  while(!(USART1->SR & (1<<6))){ //Wait until transmission is complete
    ;
  }
}

void sendString(const char *str){
  while(*str){
    sendChar(*str++);
  }
}

int main(void){
  initUART();
  initADC();
  char buffer[100];
  uint32_t adcVal = 0;
  while(1){
    adcVal = getADCTempValue(17);
    itoa((int)adcVal,buffer,10);
    sendString("ADC-Value: ");
    sendString(buffer);

    sendChar('\n');
    delay(500000);
  }
}

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.