‎2019-06-16 02:59 AM
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!
‎2019-06-16 08:16 AM
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) )
‎2019-06-16 08:27 AM
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;
‎2019-06-16 10:22 AM
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
‎2019-06-16 11:56 PM
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.
‎2019-06-17 12:46 AM
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.
‎2019-06-17 01:32 AM
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.
‎2019-06-17 01:37 AM
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...
‎2019-06-17 01:44 AM
Fan time :)
‎2019-06-17 01:48 AM
yes indeed, but not enough space in the housing :see_no_evil_monkey: