Obtener temperatura interna o voltaje STM32L0

Estoy tratando de obtener el voltaje interno y la temperatura de mi STM32L073RZ con la biblioteca mbed (Inicialice y lea un canal interno con ADC). Actualmente no sabía cómo hacerlo y todos los tutoriales hablan de otra versión de mi chip.

¿Cómo obtengo la temperatura interna de mi STM32L0?

EDITAR: Este script funciona para mí


/#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();
}

mire en la hoja de datos. El sensor de temperatura interno probablemente simplemente tenga un canal separado que puede colocar en su secuencia de muestreo ADC
Sí, el canal es "ADC_CHANNEL_TEMPSENSOR". Pero, ¿cómo usarlo?
bueno, en algún lugar puedes programar los canales que tus muestras de ADC... ponen ahí?
El bit TSEN debe configurarse para habilitar el sensor de temperatura, después de que ADC_CHANNEL_TEMPSENSOR se pueda muestrear con la frecuencia de muestreo correcta, borre el bit TSEN para deshabilitar el sensor y reducir el consumo. Además, no importa para qué versión encuentre el tutorial. El sensor de temperatura interno es el mismo para todos los STM32.
Este es exactamente el problema, después de muchas búsquedas no encontré nada sobre cómo hacer eso...
@BenceKaulics Gracias por su respuesta, trato de usar su solución y decirle si funciona bien;
¿Podría mostrarme un código de muestra para inicializar "ADC_CHANNEL_TEMPSENSOR"? He intentado muchas cosas pero nada funciona para mí... Muchas gracias
Lo usé en STM32F0 hace un tiempo, intentaré desenterrar el código. También algunos manuales de referencia tienen códigos de ejemplo de nivel de registro.
@BenceKaulics ¡Muchas gracias! Será muy útil.
Antes de leer ADC_DR, debe iniciar la conversión de ADC, esperar a que se complete la conversión y solo leer el valor después de eso. Actualmente no inicia la conversión en absoluto.
Desafortunadamente, no sé cómo se hace usando bibliotecas mbed, pero aquí hay un ejemplo usando HAL lib, fue escrito para un STM32F0, así que cambie los canales si es necesario. lectura de ejemplo adc.c y adc.h
Agrego esto "esperar(1); ADC1->CR |= ADC_CR_ADSTART;" debajo de "ADC->CCR |= ADC_CCR_TSEN;" Pero la temperatura de la pantalla es de -248 C... Muy cerca del cero absoluto jajaja
@BenceKaulics Encontré el problema, cuando intento escribir el valor de configuración en los registros, no se agrega nada. De hecho, después de mi intento de escribir algo, muestro los valores en los diferentes registros, pero siempre están vacíos. ¿Tal vez necesito borrar una protección contra escritura? En mi opinión, este es mi último problema. Si tienen alguna idea se los agradeceria mucho... Muchas gracias
Olvidó habilitar el ADC. Consulte la página 970 de este manual de referencia para ver los ejemplos de código. Sección A.8.2
@BenceKaulics ¡Es trabajo! Muchas gracias ! Para ello aplico del apartado A.7 al A.8.2. Editaré mi publicación con el guión final.
En su lugar, puede publicar todo su código de trabajo como respuesta. De esa manera, es más obvio desde dónde comenzaste y qué falta.

Respuestas (3)

Este script funciona para mí. Es muy importante inicializar y configurar ADC antes de configurar la parte del sensor de temperatura.

/#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();
}

He estado trabajando con el sensor de temperatura interno del STM32 según la hoja de datos y el manual de referencia .

Puede encontrar la guía paso a paso para el sensor en el manual de referencia:

Lectura de la temperatura

  1. Seleccione el canal de entrada ADC_IN18

  2. Seleccione un tiempo de muestreo adecuado especificado en la hoja de datos del dispositivo (TS_temp).

  3. Configure el bit TSEN en el registro ADC_CCR para despertar el sensor de temperatura del modo de apagado y esperar su tiempo de estabilización (tSTART)

  4. Inicie la conversión ADC configurando el bit ADSTART en el registro ADC_CR (o por disparador externo)

  5. Lea los datos VSENSE resultantes en el registro ADC_DR

  6. Calcule la temperatura usando la siguiente fórmula:

ingrese la descripción de la imagen aquí

Dónde:

•TS_CAL2 es el valor de calibración del sensor de temperatura adquirido a 130°C

•TS_CAL1 es el valor de calibración del sensor de temperatura adquirido a 30°C

•TS_DATA es el valor de salida del sensor de temperatura real convertido por ADC Consulte la hoja de datos del dispositivo específico para obtener más información sobre los puntos de calibración TS_CAL1 y TS_CAL2.

Las características del sensor de temperatura se pueden encontrar en la hoja de datos. Las direcciones TS_temp, tSTART y de datos de calibración.

ingrese la descripción de la imagen aquí

Y finalmente el manual de referencia tiene algún código de ejemplo tanto para configurar como para calcular el valor de la temperatura. Desde allí

Ejemplo de código de configuración de temperatura

/* (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) */

Ejemplo de código de cálculo de temperatura

/* 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);
}

La referencia de tensión interna se puede configurar de forma similar. La fuente de voltaje de referencia está habilitada por el bit de control VREFEN en el registro ADC_CCR.

Las características del voltaje de referencia se pueden encontrar en la hoja de datos al igual que los sensores de temperatura y el manual de referencia contiene algunas ecuaciones útiles. Por ejemplo, cómo calcular el voltaje V DDA real usando el voltaje de referencia interno.


Además, esta nota de aplicación para la serie L1 es un buen recurso tanto para el sensor de temperatura interno como para la referencia de voltaje: ejemplo de sensor de temperatura AN3964 STM32L1x .

Muchas gracias por esta respuesta tan completa. Te diré en unas horas si todo esto funciona bien para mí. Una vez más, gracias.
@SimonNOWAK Tenga en cuenta que estos ejemplos del manual de referencia no cubren la parte de lectura de ADC.
@SimonNOWAK Si necesita, puedo compartir un código que use la biblioteca HAL.
Tengo una pregunta más para ti... ¿Cómo puedo obtener el valor de "medida"? Este valor proviene del registro ADC_DR pero después de muchas pruebas, el valor devuelto es incorrecto. Toda su última muestra es muy útil y gracias a usted esta parte de mi proyecto se ve más fácil y entiendo cómo funciona esta parte.
@SimonNOWAK measurees el valor convertido de ADC, así que sí, es el valor de ADC_DR. Estos ejemplos no cubren la parte de medición de ADC, ¿estás seguro de que el proceso de lectura de ADC es correcto? ¿Utiliza algún otro canal o solo el ADC_CHANNEL_TEMPSENSOR?
Solo uso este canal y uso exactamente su script, así que asumo que la configuración es correcta. Para obtener el valor de un registro, solo necesita usar * como un puntero. Este es mi principal problema.
@SimonNOWAK Los registros ADC_DR se definen de la siguiente manera en el encabezado CMSIS __IO uint32_t DR; /*!< ADC data register, Address offset:0x40 */(dentro de la ADC_TypeDefestructura). Así que acceder sería como uint32_t measure = ADC1->DR. Asegúrese de iniciar la conversión ADC correctamente, espere a que se complete la conversión y solo lea el valor DR después de eso.
Acabo de editar mi publicación para mostrarles cómo se ve mi script ahora. En mi opinión, estamos muy cerca de la solución final. Otra vez, gracias por tu tiempo.

Algunas cosas a tener en cuenta:

  • El reloj ADC máximo es de 16 MHz, por lo que si está ejecutando la CPU a 32 MHz, asegúrese de usar PCLK/2 o un preescalador apropiado
  • El tiempo de muestra mínimo para el sensor de temperatura es 10uS, lo que requiere que elija el tiempo de muestra máximo en ADC1->SAMPR (que apenas lo lleva a 10us).
  • La referencia de voltaje interno también requiere al menos 10 us de tiempo de muestra.

He encontrado que la precisión generalmente está dentro de +3C, pero a veces es significativamente peor (y el error siempre es demasiado alto); es posible que esto se deba al calentamiento del troquel, pero espero que sea más consistente ya que se lee en un sistema de registro de datos alimentado por batería. Estoy comparando lecturas con dos sensores de temperatura digitales de +-1C en la misma placa tomadas al mismo tiempo.

La medición del voltaje de la batería usando la referencia de voltaje interno también tiene algún error (alrededor de 40 mv bajo) que no puedo explicar y esto puede estar relacionado con el error en el sensor de temperatura ya que depende del voltaje.

Estoy usando CKMODE=PCLK/2->ADC_CLK=16MHz SMPR=160.5clks (max), y sobremuestreo 16x. Calibre el ADC al inicio y uso los valores de calibración de fábrica almacenados (130/30).