3
\$\begingroup\$

I'm trying to obtain the internal voltage and temperature of my STM32L073RZ with the mbed librairy (Intialize and read an internal channel with ADC). Currently I didn't know how to do that and all the tutorials speak about other version of my chip.

How do I get the internal temperature of my STM32L0?

EDIT: This script work for me


/#define TEMP130_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FF8007E))
/#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FF8007A))
/#define VDD_CALIB ((uint16_t) (300))
/#define VDD_APPLI ((uint16_t) (330))

int32_t ComputeTemperature(uint32_t measure) { int32_t temperature; temperature = ((measure * VDD_APPLI / VDD_CALIB) - (int32_t)*TEMP30_CAL_ADDR ); temperature = temperature *(int32_t)(130-30); temperature = temperature /(int32_t)(*TEMP130_CAL_ADDR -*TEMP30_CAL_ADDR); temperature = temperature + 30; return(temperature); }

void ConfigTemperature(void){ RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; ADC1->CFGR2 |= ADC_CFGR2_CKMODE;

if ((ADC1->CR & ADC_CR_ADEN) != 0) /* (1) */
{
 ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */
}
ADC1->CR |= ADC_CR_ADCAL; /* (3) */
while ((ADC1->ISR & ADC_ISR_EOCAL) == 0) /* (4) */
{
    pcMain.printf("Calib");
}
ADC1->ISR |= ADC_ISR_EOCAL;

ADC1->ISR |= ADC_ISR_ADRDY; /* (1) */
ADC1->CR |= ADC_CR_ADEN; /* (2) */
if ((ADC1->CFGR1 & ADC_CFGR1_AUTOFF) == 0)
{
 while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) /* (3) */
 {
     pcMain.printf("Enable");
 }
}



ADC1->ISR |= ADC_ISR_ADRDY; /* (1) */
ADC1->CR |= ADC_CR_ADEN; /* (2) */


ADC1->CFGR1 |= ADC_CFGR1_CONT; /* (2) */
ADC1->CHSELR = ADC_CHSELR_CHSEL18; /* (3) */
ADC1->SMPR |= ADC_SMPR_SMP; /* (4) */
ADC->CCR |= ADC_CCR_TSEN;
uint32_t measure = ADC1->DR;
pcMain.printf("Measure %i\n\r", measure);
pcMain.printf("The temperature value is %i\n\r",ComputeTemperature(measure));

}

int main(void) { ConfigTemperature(); }

\$\endgroup\$
  • \$\begingroup\$ look into the datasheet. The internal temp sensor probably simply has a separate channel that you can put into your ADC sampling sequence \$\endgroup\$ – Marcus Müller Feb 17 '17 at 9:35
  • \$\begingroup\$ Yes the channel is "ADC_CHANNEL_TEMPSENSOR". But how to use it ? \$\endgroup\$ – Simon NOWAK Feb 17 '17 at 9:37
  • 1
    \$\begingroup\$ The TSEN bit must be set to enable the temperature sensor, after that the ADC_CHANNEL_TEMPSENSOR can be sampled with the correct sampling rate, clear the TSEN bit to disable the sensor and reduce consumption. Also it does not matter which version you find tutorial for. The internal temperature sensor is kind the same for all STM32. \$\endgroup\$ – Bence Kaulics Feb 17 '17 at 9:50
  • 1
    \$\begingroup\$ @BenceKaulics I found the problem, when I try to write the configuration value in the registers nothing append. Indeed after my attemp to write somthing I display the values in the different registers but they are always empty. Maybe I need to clear a write protection ? In my opinion this is my last problem. If you have any idea it would be much appreciated... Thank you very much \$\endgroup\$ – Simon NOWAK Feb 21 '17 at 9:16
  • 1
    \$\begingroup\$ @BenceKaulics It's work ! Thank you very much ! To do that I apply the section A.7 to A.8.2. I'll edit my post with the final script \$\endgroup\$ – Simon NOWAK Feb 22 '17 at 11:03
4
\$\begingroup\$

This script works for me. It's very important to initialize and configure ADC before configure the temp sensor part.

/#define TEMP130_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FF8007E))
/#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FF8007A))
/#define VDD_CALIB ((uint16_t) (300))
/#define VDD_APPLI ((uint16_t) (330))

int32_t ComputeTemperature(uint32_t measure)
{
  int32_t temperature;
  temperature = ((measure * VDD_APPLI / VDD_CALIB) - (int32_t)*TEMP30_CAL_ADDR );
  temperature = temperature *(int32_t)(130-30);
  temperature = temperature /(int32_t)(*TEMP130_CAL_ADDR -*TEMP30_CAL_ADDR);
  temperature = temperature + 30;
  return(temperature);
}


void ConfigTemperature(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    ADC1->CFGR2 |= ADC_CFGR2_CKMODE;

    if ((ADC1->CR & ADC_CR_ADEN) != 0) 
    {
     ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); 
    }

    ADC1->CR |= ADC_CR_ADCAL; 

    while ((ADC1->ISR & ADC_ISR_EOCAL) == 0)
    {
        pcMain.printf("Calib");
    }

    ADC1->ISR |= ADC_ISR_EOCAL;
    ADC1->ISR |= ADC_ISR_ADRDY; 
    ADC1->CR |= ADC_CR_ADEN; 

    if ((ADC1->CFGR1 & ADC_CFGR1_AUTOFF) == 0)
    {
     while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) 
     {
         pcMain.printf("Enable");
     }
    }

    ADC1->ISR |= ADC_ISR_ADRDY; 
    ADC1->CR |= ADC_CR_ADEN; 
    ADC1->CFGR1 |= ADC_CFGR1_CONT;
    ADC1->CHSELR = ADC_CHSELR_CHSEL18; 
    ADC1->SMPR |= ADC_SMPR_SMP; 
    ADC->CCR |= ADC_CCR_TSEN;

    uint32_t measure = ADC1->DR;
    pcMain.printf("Measure %i\n\r", measure);
    pcMain.printf("The temperature value is %i\n\r",ComputeTemperature(measure));
}

int main(void)
{
    ConfigTemperature();
}
| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

I have been working with the STM32's internal temperature sensor based on the datasheet and reference manual.

You can find the step by step guide for the sensor in the reference manual:

Reading the temperature

  1. Select the ADC_IN18 input channel

  2. Select an appropriate sampling time specified in the device datasheet (TS_temp).

  3. Set the TSEN bit in the ADC_CCR register to wake up the temperature sensor from power down mode and wait for its stabilization time (tSTART)

  4. Start the ADC conversion by setting the ADSTART bit in the ADC_CR register (or by external trigger)

  5. Read the resulting VSENSE data in the ADC_DR register

  6. Calculate the temperature using the following formula:

enter image description here

Where:

•TS_CAL2 is the temperature sensor calibration value acquired at 130°C

•TS_CAL1 is the temperature sensor calibration value acquired at 30°C

•TS_DATA is the actual temperature sensor output value converted by ADC Refer to the specific device datasheet for more information about TS_CAL1 and TS_CAL2 calibration points.

The temperature sensor characteristics can be found in the datasheet. The TS_temp, the tSTART and the calibration data addresses.

enter image description here

And finally the reference manual has some example code both for configuring and for calculating the temperature value. From there

Temperature configuration code example

/* (1) Select HSI16 by writing 00 in CKMODE (reset value) */
/* (2) Select continuous mode */
/* (3) Select CHSEL18 for temperature sensor */
/* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater
       than 2.2us */
/* (5) Wake-up the Temperature sensor (only for Temp sensor and
       VRefInt) */
//ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_CONT;/* (2) */
ADC1->CHSELR = ADC_CHSELR_CHSEL18;/* (3) */
ADC1->SMPR |= ADC_SMPR_SMP;/* (4) */
ADC->CCR |= ADC_CCR_TSEN;/* (5) */

Temperature computation code example

/* Temperature sensor calibration value address */
#define TEMP130_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FF8007E))
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FF8007A))
#define VDD_CALIB ((uint16_t) (300))
#define VDD_APPLI ((uint16_t) (330)) // <-- change this to according to your supply voltage

int32_t ComputeTemperature(uint32_t measure)
{
  int32_t temperature;
  temperature = ((measure * VDD_APPLI / VDD_CALIB) - (int32_t)*TEMP30_CAL_ADDR );
  temperature = temperature *(int32_t)(130-30);
  temperature = temperature /(int32_t)(*TEMP130_CAL_ADDR -*TEMP30_CAL_ADDR);
  temperature = temperature + 30;
  return(temperature);
}

The internal voltage reference can be configured in a similar way. The reference voltage source is enabled by the VREFEN control bit in the ADC_CCR register.

The characteristics of the reference voltage can be found in the datasheet just like the temperature sensors' and the reference manual contains some useful equations. For example how to calculate the actual VDDA voltage using the internal reference voltage.


Also this application note for the L1 series is a good resource as well for both the internal temperature sensor and the voltage reference: AN3964 STM32L1x temperature sensor example.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thank you very much for this very complete answer. I'll tell you in few hours if all of this work fine for me. Again thank you. \$\endgroup\$ – Simon NOWAK Feb 20 '17 at 7:33
  • \$\begingroup\$ @SimonNOWAK Note that these examples from the reference manual do not cover the ADC reading part. \$\endgroup\$ – Bence Kaulics Feb 20 '17 at 9:51
  • \$\begingroup\$ @SimonNOWAK I you require I can share a code that uses HAL library. \$\endgroup\$ – Bence Kaulics Feb 20 '17 at 10:17
  • \$\begingroup\$ I've one more question for you... How can I get the "measure" value ? This value come from the ADC_DR register but after a lot of test the returned value is wrong. All of your last sample is very useful and thanks to you this part of my project look easier and I understand how this part work. \$\endgroup\$ – Simon NOWAK Feb 20 '17 at 13:22
  • 1
    \$\begingroup\$ @SimonNOWAK The ADC_DR registers is defined as follows in the CMSIS header __IO uint32_t DR; /*!< ADC data register, Address offset:0x40 */, (inside the ADC_TypeDef struct). So accessing it would be like uint32_t measure = ADC1->DR. Make sure that you start the ADC conversion correctly, wait for the conversion to complete and only read the DR value after that. \$\endgroup\$ – Bence Kaulics Feb 20 '17 at 15:04
2
\$\begingroup\$

Some things to be careful of:

  • The max ADC clock is 16MHz so if you are running the CPU at 32MHz, make sure to use PCLK/2 or an appropriate prescaler
  • The minimum sample time for the temperature sensor is 10uS which requires that you choose the maximum sample time in ADC1->SAMPR (that just barely gets you to 10us).
  • The internal voltage reference also requires at least 10us sample time.

I have found accuracy to generally be within +3C, but sometimes significantly worse (and the error is always too high); it's possible that this is due to die heating, but I would expect that to be more consistent since it is being read in a battery-powered data logging system. I'm comparing readings against two +-1C digital temperature sensors on the same board taken at the same time.

Measuring battery voltage using the internal voltage reference also has some error (about 40mv low) that I can't account for and this may be related to the error in the temperature sensor since it is dependent on the voltage.

I am using CKMODE=PCLK/2->ADC_CLK=16MHz SMPR=160.5clks (max), and 16x oversampling. I calibrate the ADC at startup and use the stored (130/30) factory calibration values.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.