cancel
Showing results for 
Search instead for 
Did you mean: 

Internal Temperature STM32H7 delivers wrong value

GS1
Senior III

Hi all,

I need to measure the internal CPU temperature sensor from the STM32H743. I activated the ADC3 using STM32CUBEMX (V. 5.0.1) and selected Temperature Channel.

However the calculated temperatures are far too high. When switching on the cooled system at 22° Temperature, the delivered value is 55 °C. This is definitely by far too high!

Can anybody tell what might be wrong with my init code?

ADC input clock is set to 32 MHz

Clock Prescaler = devided by 1

Resolution 16 Bit

Continuous Mode Enabled

In Rank 1:

Sampling Time set to 810.5 Cycles

Generated initialisation code is as follows:

//////////////////////////////////////////////////////////////////

void MX_ADC3_Init(void)

{

 ADC_ChannelConfTypeDef sConfig = {0};

 /**Common config

 */

 hadc3.Instance = ADC3;

 hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;

 hadc3.Init.Resolution = ADC_RESOLUTION_16B;

 hadc3.Init.ScanConvMode = ADC_SCAN_DISABLE;

 hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV;

 hadc3.Init.LowPowerAutoWait = DISABLE;

 hadc3.Init.ContinuousConvMode = ENABLE;

 hadc3.Init.NbrOfConversion = 1;

 hadc3.Init.DiscontinuousConvMode = DISABLE;

 hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START;

 hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;

 hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;

 hadc3.Init.Overrun = ADC_OVR_DATA_PRESERVED;

 hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;

 hadc3.Init.BoostMode = DISABLE;

 hadc3.Init.OversamplingMode = DISABLE;

 if (HAL_ADC_Init(&hadc3) != HAL_OK)

 {

   Error_Handler();

 }

 /**Configure Regular Channel

 */

 sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;

 sConfig.Rank = ADC_REGULAR_RANK_1;

 sConfig.SamplingTime = ADC_SAMPLETIME_810CYCLES_5;

 sConfig.SingleDiff = ADC_SINGLE_ENDED;

 sConfig.OffsetNumber = ADC_OFFSET_NONE;

 sConfig.Offset = 0;

 if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK)

 {

   Error_Handler();

 }

}

////////////////////////////////////////////////////////////////////

// Reading Temperature in Interrupt mode:

// started after power on in continuous mode:  

//   HAL_ADC_Start_IT(&hadc3);

#define TEMP110_CAL_ADDR   ((uint16_t*)((uint32_t)0x1FF1E840))

#define TEMP30_CAL_ADDR      ((uint16_t*)((uint32_t)0x1FF1E820))

double adc_ReadInternalTemp(void)

{

   double dTemp;

   uint32_t adcConVal;

   uint32_t T1_30;

   uint32_t T2_110;

    adcConVal = HAL_ADC_GetValue(&hadc3);   

   // Temp = 80 / (TS_CAL2 - TS_CAL1) * (ValTS - TS_CAL1) + 30

   // TS_CAL1 = T1_30 = at 30 Degrees

   // TS_CAL2 = T2_110 = at 110 Degrees

   T1_30 = (int32_t)* TEMP30_CAL_ADDR;   // Reads 0x3073  

   T2_110 = (int32_t)* TEMP110_CAL_ADDR;   // Reads 0x3F63  

   dTemp = (double)(110 - 30) / (double)(T2_110 - T1_30);

   dTemp *= (double)(adcConVal - T1_30);

   dTemp += 30;

   return dTemp; // 55-59 °C at startup, 80°C after warming up

}

Any help is very much appreciated!

18 REPLIES 18

Such things can be done with integer operations only - do multiplications first and division last. You only have to care for a wrap-around of integer, but with uint32_t for these low numbers it's not a problem. And even division can be done with rounding:

#define DIVU_ROUND(n, d)    ( ((n) + (d) / 2) / (d) )

S.Ma
Principal

Actually the only function you problably need is something like this:

int32_t Interpolate_s32 (int32_t x0, int32_t x1, int32_t y0, int32_t y1, int32_t x) { 
  int32_t dwQ;
  dwQ = ((y1-y0))*x+(x1*y0)-(x0*y1);	// overflow not checked yet
  dwQ = dwQ / (x1-x0);// we can also do roundings here
  
  return dwQ;
}

I got a bit tired of the coding marathon for getting the temperature right, here is my code for STM32L4R5, if someone can fix the 3.0V and test it, thanks!

uint32_t ADC_Normal_LsbTo_mV(ADC_t* A, uint32_t Lsb) {
  uint32_t mV = Interpolate_s32 (0, 0x0FFF, 0, A->VRef_mV, Lsb);  
  return mV;
}
 
uint32_t ADC_Injected_LsbTo_mV(ADC_t* A, uint32_t Lsb) {
  uint32_t mV = Interpolate_s32 (0, (int32_t)0x7FF8, 0, A->VRef_mV, (int32_t)(int16_t)Lsb);  
  return mV;
}
 
  int32_t Deg30Lsb;
  int32_t Deg130Lsb;
  int32_t ADC_Num, ADC_Denum;
  int32_t DegC_x10;
 
int32_t ADC_Convert_mV_to_DegC_x10(ADC_t* A, uint32_t Lsb) {
  Deg30Lsb = (int32_t)(*((uint16_t*) 0x1FFF75A8));  // 3.0V +/- 10mV
  Deg130Lsb = (int32_t)(*((uint16_t*) 0x1FFF75CA)); // 3.0V +/- 10mV or compensate for it (scale the result back if it was 3.0V). Here 3.3V, so there are some errors
  // we first convert the LSB calibrated Flash values to mV (let's remove the sign issue between normal and injected channels)
  DegC_x10 = Interpolate_s32 (Deg30Lsb, Deg130Lsb, 300U, 1300U, (int32_t)(int16_t)Lsb);
//  uint32_t DegC_x10 = (int32_t)300+(((int32_t)(1100-300))*((int32_t)Lsb-Deg30Lsb))/(Deg110Lsb-Deg30Lsb);//Interpolate_s32 (Deg30Lsb, Deg110Lsb, 300U, 1100U, (int32_t)(int16_t)Lsb);
  A->Temp_degC_x10 = DegC_x10; // capture the value for debugging or reference
  return DegC_x10;
}
 
int32_t ADC_Convert_VRefByLsb(ADC_t* A, uint32_t Lsb) {
  // Vref = 1.212V
  int32_t VRefLsb3p3V_Lsb = (int32_t)(*((uint16_t*) 0x1FFF75AA)); // this contains the ADC 12 bit right aligned injected raw data for Vref LSB at 30C and 3.0V
  uint32_t Vdd_mV = (3000*VRefLsb3p3V_Lsb)/Lsb;
  A->MeasuredVdd_mV = Vdd_mV;
  return 0;
}
 
void ADC_UpdateConveredValues(ADC_t* A) {
  
  uint8_t i;
  A->VRef_mV = 3300;
  // mmeasure the real Vdd (3.3V?)
  ADC_Convert_VRefByLsb(A, A->Normal_Lsb[1]);
  // use the precise measurement as reference
  //A->VRef_mV = A->MeasuredVdd_mV; // hmm....  
  // convert all channels to their mV equivalent
  for(i=0; i<COUNTOF(sConfigs); i++)
    A->Normal_mV[i] = ADC_Normal_LsbTo_mV(A, A->Normal_Lsb[i]);
  // get the Vbat value
  A->MeasuredVBat_mV = A->Normal_mV[0]*(5000/750); // external divider 100k over 200k, followed with divider which is also always on... CH18 is static!!!!
  // get the temperature
  ADC_Convert_mV_to_DegC_x10(A,A->Normal_Lsb[2]);
  
}

This is generic ADC sweep by DMA to a buffer within a context struct

typedef struct {
  
  uint16_t      Normal_Lsb[20]; // if no RAM buffer allocated for more than a single normal channel, this one will be used instead. It will also have its conversion in mV for debugging and bringup
  
  uint32_t      VRef_mV; // The ADC supply voltage in mV
  uint32_t      Clock_Hz;
  
  // this is for debug only
  uint16_t      Normal_mV[20]; // this can be updated by interrupts or manually
  int16_t       Injected_Lsb[4];
  int16_t       Injected_mV[4];
  
  int16_t       Temp_degC_x10;
  int16_t       MeasuredVdd_mV; // estimation of Vdd from Vref
  int16_t       MeasuredVBat_mV; // estimation of Vbat from readings
  int16_t       MeasuredDAC_1_mV;
  int16_t       MeasuredDAC_2_mV;
  
  uint8_t       NormalDone : 1;
  uint8_t       InjectedDone : 1;
  uint8_t       Overflow : 1;
  uint8_t       Calibration :1;
  
} ADC_t;

PS: Just checked the specs:

Temperature sensor raw data acquired value at 30 °C / 110°C , VDDA=3.3 V

So no further compensation necessary. for the SMT32H743

MikeDB
Lead

I think this emphasises that for H7 devices you really should fix an aluminium heat spreader to the top of the package as these are fairly powerful devices and there isn't so much margin available as for smaller STM32 devices.

You are right. We already did that and it reduced the temperature only by 1-2 degrees which is not so much. We are now trying some other solutions to reduce heat: temporarily switch off unused peripherals, reduce CPU clock speed etc.

I managed 3-4 degrees on a board with six H750s which I think is worthwhile but yes proper power management is just as important. However often I find this still leads to hot spots around the chip.

GS1
Senior III

Ok, my system has seven H743s on one board and another H743 on the board attached below - which holds the DC/DCs and a TFT. So we have quite some heat creators which have to be optimized...

Fan time 🙂

GS1
Senior III

yes indeed, but not enough space in the housing :see_no_evil_monkey: