Skip to main content
GS1
Senior III
June 16, 2019
Question

Internal Temperature STM32H7 delivers wrong value

  • June 16, 2019
  • 18 replies
  • 6176 views

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!

This topic has been closed for replies.

18 replies

Uwe Bonnes
Chief
June 16, 2019

The H7 dissipates a lot of power and so heats up the chip even even when the ambiant is cooled. I think 55 degrees is normal. Do some calculation with the dissipated power and the thermal resitstance chip to ambient!

GS1
GS1Author
Senior III
June 16, 2019

Thank you for your input. I agree that 55 degrees would be normal after heating up the chip. But this temperature already os seen immediately after startup. We get values of 70 degrees when the system is heated up for appx. 10 minutes.

We are wondering however, which temperatures will be considered as "normal" ? And the system is not yet built into a housing, which will increase heating additionally. And for which ambient temperatures will the system then be able to work...

The chip is defined for working temperatures of appx. 85 degrees. So only 15 degrees would be left for increased ambient temperatures...

The system will be used in a sports car vehicle, so 35° could be reached very easily.

Peter Mather
Associate III
June 16, 2019

I'm seeing 43degC on a NUcleo H743Zi with a daughter board tight over the Nucleo and 22degC ambient. I'm using oversampling on the measurement hence the right shift in the equation

  temp = ((double)(80.0)) / ((double)(*TS_CAL2-*TS_CAL1)) * (((double)(HAL_ADC_GetValue(&hadc3)>>4))-((double)*TS_CAL1)) + ((double)30.0) ;

GS1
GS1Author
Senior III
June 16, 2019

Thank you for your information.

Could you please send me your initialisation code so I can compare it to mine? Maybe I did set a wrong sampling rate or so...

Peter Mather
Associate III
June 16, 2019

Cut and paste from a large program so variables not defined

 hadc3.Instance = ADC3;

 hadc3.Init.Resolution = ADC_RESOLUTION_16B;

 hadc3.Init.Oversampling.Ratio         = 256;  /* Oversampling ratio */

 hadc3.Init.Oversampling.RightBitShift     = ADC_RIGHTBITSHIFT_4;     /* Right shift of the oversampled summation */

 hadc3.Init.ClockPrescaler      = ADC_CLOCK_SYNC_PCLK_DIV4;   /* Synchronous clock mode, input ADC clock divided by 4*/

 hadc3.Init.ScanConvMode       = DISABLE;            /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */

 hadc3.Init.EOCSelection       = ADC_EOC_SINGLE_CONV;      /* EOC flag picked-up to indicate conversion end */

 hadc3.Init.LowPowerAutoWait     = DISABLE;            /* Auto-delayed conversion feature disabled */

 hadc3.Init.ContinuousConvMode    = DISABLE;            /* Continuous mode enabled (automatic conversion restart after each conversion) */

 hadc3.Init.NbrOfConversion     = 1;               /* Parameter discarded because sequencer is disabled */

 hadc3.Init.DiscontinuousConvMode  = DISABLE;            /* Parameter discarded because sequencer is disabled */

 hadc3.Init.NbrOfDiscConversion   = 1;               /* Parameter discarded because sequencer is disabled */

 hadc3.Init.ExternalTrigConv     = ADC_SOFTWARE_START;      /* Software start to trig the 1st conversion manually, without external event */

 hadc3.Init.ExternalTrigConvEdge   = ADC_EXTERNALTRIGCONVEDGE_NONE; /* Parameter discarded because software trigger chosen */

 hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;     /* DR mode selected */

 hadc3.Init.BoostMode        = ENABLE;            /* Enable Boost mode as ADC clock frequency is bigger than 20 MHz */

 hadc3.Init.Overrun         = ADC_OVR_DATA_OVERWRITTEN;   /* DR register is overwritten with the last conversion result in case of overrun */

 hadc3.Init.OversamplingMode     = ENABLE;            /* Oversampling enabled */

 hadc3.Init.Oversampling.TriggeredMode     = ADC_TRIGGEREDMODE_SINGLE_TRIGGER ;     /* Specifies whether or not a trigger is needed for each sample */

 hadc3.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE ; /* Specifies whether or not the oversampling buffer is maintained during injection sequence */

 if (HAL_ADC_Init(&hadc3) != HAL_OK)

 {

 /* ADC initialization Error */

 error("HAL_ADC_Init");

 }

 /* Run the ADC calibration in single-ended mode */

 if (HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)

 {

 /* Calibration Error */

 error("HAL_ADCEx_Calibration_Start");

 }

 }

uint16_t *TS_CAL1=(uint16_t *)0x1FF1E820;

uint16_t *TS_CAL2=(uint16_t *)0x1FF1E840;

    sConfig.Channel   = ADC_CHANNEL_TEMPSENSOR;       /* Sampled channel number */

 sConfig.Rank     = ADC_REGULAR_RANK_1;     /* Rank of sampled channel number ADCx_CHANNEL */

 sConfig.SamplingTime = ADC_SAMPLETIME_387CYCLES_5;  /* Sampling time (number of clock cycles unit) */

 sConfig.SingleDiff  = ADC_SINGLE_ENDED;      /* Single-ended input channel */

 sConfig.OffsetNumber = ADC_OFFSET_NONE;       /* No offset subtraction */

 sConfig.Offset = 0;                 /* Parameter discarded because offset correction is disabled */

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

 {

  /* Channel Configuration Error */

  error("HAL_ADC_ConfigChannel");

 }

  if (HAL_ADC_Start(&hadc3) != HAL_OK)

  {

   /* Start Conversation Error */

   error("HAL_ADC_Start");

  }

  /*##-4- Wait for the end of conversion #####################################*/

  /* For simplicity reasons, this example is just waiting till the end of the

    conversion, but application may perform other tasks while conversion

    operation is ongoing. */

  if (HAL_ADC_PollForConversion(&hadc3, 10) != HAL_OK)

  {

   /* End Of Conversion flag not set on time */

    error("HAL_ADC_PollForConversion");

  }

  else

  {

   /* ADC conversion completed */

   /*##-5- Get the converted value of regular channel ########################*/

   temp = ((double)(80.0)) / ((double)(*TS_CAL2-*TS_CAL1)) * (((double)(HAL_ADC_GetValue(&hadc3)>>4))-((double)*TS_CAL1)) + ((double)30.0) ;

  }

GS1
GS1Author
Senior III
June 16, 2019

Hi Peter,

thank you for sending me your init code. Looks similar to mine except of the oversampling setup.

As I now got reasonable values I keep it as it is. Thank you anyway!

GS1
GS1Author
Senior III
June 16, 2019

Ok folks, now I got reasonable results of 50-55 degrees C when the system is heated up.

I forgot to call the calibration before starting the ADC:

   HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);

S.Ma
Principal
June 16, 2019

One more thing, check the datasheet for what is Vdd for the calibrated values.

If the cal values are 3.0V but running on 3.3V, you'll be off much.

So while the example is not provided (no need?), first measure ADC Vdd through Vref measurements, then read the ADC Temp value, and compensate for the 3.0V difference. You don't need to go to double format for this. float will be more than enough when the desired accuracy won't reach more than 1 degree (most temp sensors aren't acurate anyway)

GS1
GS1Author
Senior III
June 16, 2019

ok, thank you for the hints!

Yes, I agree: float would be sufficient. Will change that ;)

S.Ma
Principal
June 16, 2019

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;

MikeDB
Senior II
June 17, 2019

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.

GS1
GS1Author
Senior III
June 17, 2019

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.

MikeDB
Senior II
June 17, 2019

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.