Acondicionamiento de señal del LM35 a 0–5 V con LM358 y PIC18F4550 (paso a paso)

 para acondicionar la señal del sensor LM35 al rango 0–5 V del ADC de un microcontrolador entre 20 °C y 70 °C, se usan dos etapas con un LM358: un amplificador no inversor de ganancia 10 (R1 = 90 kΩ, R2 = 10 kΩ) que lleva 0.2–0.7 V a 2–7 V, y un restador de 2 V (cuatro resistencias de 100 kΩ) que lo lleva a 0–5 V. La ecuación de diseño es V_{out} = 10\,V_{LM35} - 2\ \text{V}.

El LM35 es uno de los sensores de temperatura más usados en electrónica, pero su salida es muy pequeña: solo 10 mV por grado. Si lo conectas directo al ADC de un Arduino, un PIC o un ESP32, desperdicias casi toda la resolución del convertidor. En esta guía verás, paso a paso, cómo hacer el acondicionamiento de señal del LM35 con amplificadores operacionales para aprovechar todo el rango de 0 a 5 V, con cálculos, esquemas, lista de materiales y código de ejemplo.

Contenido

El problema: medir 20–70 °C con un ADC de 0–5 V

El LM35 entrega 10\ \text{mV}/{}^{\circ}\text{C}. Queremos medir temperatura entre 20 °C y 70 °C y que ese intervalo ocupe todo el rango del ADC del microcontrolador, es decir, de 0 V a 5 V. Para lograrlo diseñamos un circuito con amplificadores operacionales que desplace y amplifique la señal del sensor.

¿Por qué no conectarlo directo? Sin acondicionar, entre 20 °C y 70 °C el LM35 solo varía 0.5 V. En un ADC de 10 bits a 5 V eso son unos 102 niveles, una resolución cercana a 0.5 °C. Con el acondicionamiento usamos los 1024 niveles: la resolución mejora a unos 0.05 °C por paso, diez veces mejor.

Pines del LM35 (encapsulado TO-92): 1) +V_S (4 V a 20 V), 2) salida (OUT), 3) GND. Puedes consultar todas sus características en la hoja de datos oficial del LM35 de Texas Instruments.

Paso 1: voltaje de salida del LM35 según la temperatura

Como la salida del sensor es lineal, su voltaje es:

V_{LM35} = 10\ \tfrac{\text{mV}}{{}^{\circ}\text{C}} \cdot T
Temperatura Cálculo Voltaje del LM35
0 °C 10\ \text{mV} \cdot 0 0 V
20 °C 10\ \text{mV} \cdot 20 0.2 V ✔
25 °C 10\ \text{mV} \cdot 25 0.25 V
70 °C 10\ \text{mV} \cdot 70 0.7 V ✔
100 °C 10\ \text{mV} \cdot 100 1.0 V
Voltaje de salida del LM35. ✔ = extremos del intervalo de medición del diseño.

Paso 2: cómo calcular la ecuación de acondicionamiento

Necesitamos que 0.2 V se convierta en 0 V y que 0.7 V se convierta en 5 V:

Temperatura Entrada (LM35) Salida deseada
20 °C 0.2 V 0 V
70 °C 0.7 V 5 V

La relación es una recta, y = mx + b, así que buscamos:

V_{out} = m \cdot V_{entrada} + b

Cálculo de la pendiente (ganancia)

m = \frac{y_2 - y_1}{x_2 - x_1} = \frac{5\ \text{V} - 0\ \text{V}}{0.7\ \text{V} - 0.2\ \text{V}} = \frac{5}{0.5} = 10

Cálculo del intercepto (offset)

Con m = 10 y el punto (0.2\ \text{V},\ 0\ \text{V}):

\begin{aligned} 0 &= 10\,(0.2) + b \\ 0 &= 2 + b \\ b &= -2 \end{aligned}

Comprobación con el otro punto, (0.7\ \text{V},\ 5\ \text{V}):

\begin{aligned} 5 &= 10\,(0.7) + b \\ 5 &= 7 + b \\ b &= -2 \end{aligned}

La ecuación de acondicionamiento del LM35 es:

\boxed{V_{out} = 10 \cdot V_{entrada} - 2\ \text{V}}

Clave del diseño: el término 10 \cdot V_{entrada} se implementa con un amplificador no inversor y el término -2\ \text{V} con un amplificador restador.

Función de transferencia: V_{ADC} = 0.1\,T - 2. Por encima de 70 °C la salida supera los 5 V (ver recomendaciones).

Paso 3: amplificador no inversor de ganancia 10 con LM358

Etapa 1: el LM35 entra al pin no inversor (+) del LM358.

La salida de un amplificador no inversor es:

V_{out} = \left(1 + \frac{R_1}{R_2}\right) V_{in}

Con R_1 = 90\ \text{k}\Omega y R_2 = 10\ \text{k}\Omega, R_1/R_2 = 9 y la ganancia es 1 + 9 = 10. En los extremos del rango:

\begin{aligned} T = 20\,{}^{\circ}\text{C}: \quad V_{out} &= 10 \cdot 0.2\ \text{V} = 2\ \text{V} \\ T = 70\,{}^{\circ}\text{C}: \quad V_{out} &= 10 \cdot 0.7\ \text{V} = 7\ \text{V} \end{aligned}

Como 90 kΩ no es un valor comercial, en la práctica se usa un trimmer (potenciómetro de ajuste) de 100 kΩ, o una resistencia de 82 kΩ en serie con un trimmer de 20 kΩ, para calibrar la ganancia exacta.

Paso 4: restador de 2 V con amplificador operacional

Ahora restamos 2 V a la salida de la primera etapa. Usamos un restador (amplificador diferencial) con todas las resistencias de 100\ \text{k}\Omega. Por criterio práctico, estas resistencias deben ser al menos 100 veces mayores que la resistencia interna de lo que se conecta a sus entradas.


Etapa 2: restador con R_3 = R_4 = R_5 = R_6 = 100\ \text{k}\Omega.

\boxed{V_o = V_{in} - 2\ \text{V}} \begin{aligned} V_{in} = 2\ \text{V} &\;\Rightarrow\; V_o = 0\ \text{V} \;\checkmark \\ V_{in} = 7\ \text{V} &\;\Rightarrow\; V_o = 5\ \text{V} \;\checkmark \end{aligned}

El circuito entrega 0 V a 20 °C y 5 V a 70 °C: justo el rango del ADC.

¿Qué pasa por debajo de 20 °C?

T = 10\,{}^{\circ}\text{C} \;\Rightarrow\; V_{LM35} = 0.1\ \text{V} \;\Rightarrow\; 10 \cdot 0.1 = 1\ \text{V} \;\Rightarrow\; V_o = 1 - 2 = -1\ \text{V}

En teoría saldrían −1 V, pero el voltaje real depende de la alimentación del opamp. Con el LM358 alimentado solo con +9 V y GND, la salida no baja de 0 V, así que el microcontrolador leerá 0 V. Esto además protege la entrada del ADC de voltajes negativos.

Circuito completo de acondicionamiento del LM35

La referencia de 2 V se obtiene con un divisor de tensión desde la fuente de 5 V:

V_{ref} = 5\ \text{V} \cdot \frac{R_8}{R_7 + R_8} = 5\ \text{V} \cdot \frac{1\ \text{k}\Omega}{1.5\ \text{k}\Omega + 1\ \text{k}\Omega} = 2\ \text{V}

Circuito completo: LM35 → amplificador no inversor (×10) → restador (−2 V) → ADC.

El sistema completo cumple:

V_{ADC} = 0.1\ \tfrac{\text{V}}{{}^{\circ}\text{C}} \cdot T - 2\ \text{V}, \qquad 20\,{}^{\circ}\text{C} \le T \le 70\,{}^{\circ}\text{C}

Lista de materiales

Componente Valor Cantidad Función
Sensor de temperatura LM35 1 Sensor, 10 mV/°C
Amplificador operacional LM358 (doble) 1 Las dos etapas en un solo chip
R1 90 kΩ 1 Ganancia de la etapa 1
R2 10 kΩ 1 Ganancia de la etapa 1
R3, R4, R5, R6 100 kΩ 4 Restador
R7 1.5 kΩ 1 Divisor de 2 V
R8 1 kΩ 1 Divisor de 2 V
Fuentes +9 V y +5 V — Opamp y referencia

 

El LM358 trae dos amplificadores en un mismo encapsulado, así que con un solo chip se arman ambas etapas. Detalles de alimentación y excursión de salida en la hoja de datos del LM358.

PROGRAMA COMPLETO CON MICROCONTROLADOR PIC18F4550

este es el programa /*
 * ============================================================================
 *  Sensoricx - Medición de temperatura con LM35 acondicionado
 * ============================================================================
 *  PIC18F4550 - XC8 - Cristal externo de 8 MHz (HS)
 *
 *  Conexiones:
 *   - Pin 1  (MCLR): R de 10k a +5 V + pulsador de reset a GND
 *   - Pin 2  (AN0) : salida del acondicionamiento (0 a 5 V = 20 a 70 °C)
 *   - Pin 13 y 14  : cristal de 8 MHz con condensadores a GND
 *   - Pin 33 (RB0) : SDA del LCD I2C
 *   - Pin 34 (RB1) : SCL del LCD I2C
 *   - Pin 15 (RC0) : LED alarma (> 40 °C) / parpadea si el LCD no responde
 *   - Pin 16 (RC1) : LED normal
 * ============================================================================
 */

#include "configu.h"
#include <xc.h>
#include <stdint.h>
#include <stdio.h>

// ---------------------------------------------------------------------------
// LCD I2C (módulo PCF8574)
// ---------------------------------------------------------------------------
#define LCD_DIR_I2C   0x27    // 0x27 = PCF8574T, 0x3F = PCF8574AT

// Mapeo del módulo: 1 = estándar (luz en P3), 2 = alternativo (luz en P7)
#define LCD_MAPEO     1

#if LCD_MAPEO == 1
  #define LCD_RS   0x01
  #define LCD_E    0x04
  #define LCD_LUZ  0x08
  #define LCD_D4   0x10
  #define LCD_D5   0x20
  #define LCD_D6   0x40
  #define LCD_D7   0x80
#else
  #define LCD_RS   0x40
  #define LCD_E    0x10
  #define LCD_LUZ  0x80
  #define LCD_D4   0x01
  #define LCD_D5   0x02
  #define LCD_D6   0x04
  #define LCD_D7   0x08
#endif

// ---------------------------------------------------------------------------
// Parámetros de la medición
// ---------------------------------------------------------------------------
#define N_MUESTRAS     16      // Lecturas promediadas
#define ADC_LIM_BAJO   3       // <= 3    -> "<= 20 °C"
#define ADC_LIM_ALTO   1020    // >= 1020 -> ">= 70 °C"
#define T_ALARMA_X100  4000    // Alarma: 40,00 °C

static uint8_t i2c_error = 0;  // 1 = el LCD no respondió

// ===========================================================================
//  I2C a 100 kHz
// ===========================================================================
static void i2c_iniciar(void)
{
    TRISBbits.TRISB0 = 1;      // SDA (pin 33)
    TRISBbits.TRISB1 = 1;      // SCL (pin 34)
    SSPSTAT = 0x80;            // 100 kHz
    SSPCON1 = 0x28;            // I2C maestro
    SSPCON2 = 0x00;
    SSPADD  = 19;              // 8 MHz / (4 * 100 kHz) - 1 = 19
}

static void i2c_esperar(void)
{
    while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F)) {
        ;
    }
}

static void pcf_escribir(uint8_t dato)
{
    i2c_esperar();
    SSPCON2bits.SEN = 1;                         // Inicio
    i2c_esperar();
    SSPBUF = (uint8_t)(LCD_DIR_I2C << 1);        // Dirección + escritura
    i2c_esperar();
    i2c_error = SSPCON2bits.ACKSTAT;             // 1 = no respondió
    SSPBUF = dato;                               // Dato
    i2c_esperar();
    SSPCON2bits.PEN = 1;                         // Parada
}

// ===========================================================================
//  LCD 16x2 en modo 4 bits
// ===========================================================================
static void lcd_nibble(uint8_t nibble, uint8_t rs)
   {
       uint8_t dato = LCD_LUZ;
       if (rs)             dato |= LCD_RS;
       if (nibble & 0x01)  dato |= LCD_D4;
       if (nibble & 0x02)  dato |= LCD_D5;
       if (nibble & 0x04)  dato |= LCD_D6;
       if (nibble & 0x08)  dato |= LCD_D7;

       pcf_escribir(dato);            // 1. Datos listos con E = 0
       __delay_us(5);
       pcf_escribir(dato | LCD_E);    // 2. E = 1
       __delay_us(5);
       pcf_escribir(dato);            // 3. E = 0: el LCD toma el dato
       __delay_us(100);
   }

static void lcd_byte(uint8_t dato, uint8_t rs)
{
    lcd_nibble(dato >> 4, rs);
    lcd_nibble(dato & 0x0F, rs);
}

static void lcd_cmd(uint8_t cmd)
{
    lcd_byte(cmd, 0);
    if (cmd == 0x01 || cmd == 0x02) {
        __delay_ms(2);
    }
}

static void lcd_texto(const char *s)
{
    while (*s) {
        lcd_byte((uint8_t)*s++, 1);
    }
}

static void lcd_ir(uint8_t fila, uint8_t col)
{
    lcd_cmd((uint8_t)(((fila == 1) ? 0x80 : 0xC0) + col - 1));
}

static void lcd_iniciar(void)
{
    __delay_ms(50);
    pcf_escribir(LCD_LUZ);

    lcd_nibble(0x03, 0); __delay_ms(5);
    lcd_nibble(0x03, 0); __delay_us(150);
    lcd_nibble(0x03, 0); __delay_us(150);
    lcd_nibble(0x02, 0);

    lcd_cmd(0x28);     // 4 bits, 2 líneas
    lcd_cmd(0x0C);     // Pantalla encendida, sin cursor
    lcd_cmd(0x06);     // Avance a la derecha
    lcd_cmd(0x01);     // Borrar
}

// ===========================================================================
//  ADC (AN0, pin 2)
// ===========================================================================
static void adc_iniciar(void)
{
    TRISAbits.TRISA0 = 1;      // Pin 2 como entrada
    ADCON1 = 14;               // Solo AN0 analógico
    ADCON0bits.CHS = 0;        // Canal AN0
    ADCON2bits.ACQT = 2;       // 4 TAD
    ADCON2bits.ADCS = 2;       // Fosc/32 = 4 us
    ADCON2bits.ADFM = 1;       // Justificado a la derecha
    ADCON0bits.ADON = 1;
    __delay_us(20);
}

static uint16_t adc_promedio(void)
{
    uint32_t suma = 0;
    for (uint8_t i = 0; i < N_MUESTRAS; i++) {
        ADCON0bits.GO_nDONE = 1;
        while (ADCON0bits.GO_nDONE) {
            ;
        }
        suma += ((uint16_t)ADRESH << 8) | ADRESL;
        __delay_us(50);
    }
    return (uint16_t)(suma / N_MUESTRAS);
}

// ===========================================================================
//  Programa principal
// ===========================================================================
void main(void)
{
    char linea[17];
    uint16_t adc, mv;
    int16_t t_x100;

    TRISCbits.TRISC0 = 0;      // LED alarma
    TRISCbits.TRISC1 = 0;      // LED normal
    LATCbits.LATC0 = 0;
    LATCbits.LATC1 = 0;

    adc_iniciar();             // Primero el ADC: deja RB0 y RB1 digitales
    i2c_iniciar();
    lcd_iniciar();

    lcd_ir(1, 1); lcd_texto("   SENSORICX    ");
    lcd_ir(2, 1); lcd_texto(" LM35 20 a 70 C ");
    __delay_ms(1500);
    lcd_cmd(0x01);

    while (1) {
        adc    = adc_promedio();
        t_x100 = (int16_t)(2000 + ((uint32_t)adc * 5000UL + 511) / 1023);  // T x 100
        mv     = (uint16_t)(((uint32_t)adc * 5000UL + 511) / 1023);        // mV en el pin 2

        // Línea 1: temperatura
        lcd_ir(1, 1);
        if (adc <= ADC_LIM_BAJO) {
            lcd_texto("T:<=20.00");
        } else if (adc >= ADC_LIM_ALTO) {
            lcd_texto("T:>=70.00");
        } else {
            sprintf(linea, "T: %2d.%02d ", t_x100 / 100, t_x100 % 100);
            lcd_texto(linea);
        }
        lcd_byte(0xDF, 1);             // Símbolo de grado
        lcd_texto("C     ");

        // Línea 2: ADC y voltaje
        lcd_ir(2, 1);
        sprintf(linea, "ADC:%4u %u.%02uV ", adc, mv / 1000, (mv % 1000) / 10);
        lcd_texto(linea);

        // LEDs
        if (i2c_error) {
            LATCbits.LATC0 = !LATCbits.LATC0;   // Parpadea: el LCD no responde
            LATCbits.LATC1 = 0;
        } else if (t_x100 > T_ALARMA_X100) {
            LATCbits.LATC0 = 1;                 // Más de 40 °C
            LATCbits.LATC1 = 0;
        } else {
            LATCbits.LATC0 = 0;
            LATCbits.LATC1 = 1;                 // Normal
        }

        __delay_ms(250);
    }
}

 

Descarga el archivo. hex AQUI

Errores comunes y recomendaciones

  • Proteger el ADC por encima de 70 °C. Con +9 V de alimentación, la salida puede subir hasta unos 7 V si la temperatura pasa de 70 °C. Agrega una resistencia en serie de 1 kΩ y un diodo Schottky hacia +5 V (o un zener de 5.1 V) en la entrada del ADC.
  • Excursión de salida del LM358. Con 9 V, la salida máxima ronda los 7.5 V, suficiente para los 7 V de la primera etapa. No lo alimentes con 5 V: no alcanzaría los 7 V.
  • Impedancia del divisor de 2 V. El divisor R7/R8 tiene unos 600 Ω de resistencia equivalente, que se suman a R5 y causan un error de alrededor de 0.6 %. Para más precisión, pon un seguidor de tensión entre el divisor y R5, o usa una referencia de voltaje.
  • Resistencias de precisión. En el restador, cualquier diferencia entre R3–R6 introduce error. Usa resistencias de 1 % o mejores.
  • Calibración. Ajusta el trimmer R1 con el sensor a una temperatura conocida y verifica con un multímetro que la salida sea la esperada.

Preguntas frecuentes sobre el acondicionamiento del LM35

¿Qué es el acondicionamiento de señal de un sensor?

Es adaptar la señal eléctrica de un sensor (amplificarla, desplazarla, filtrarla) para que coincida con el rango de entrada del sistema que la lee, normalmente un ADC. Así se aprovecha toda su resolución.

¿Cuánto voltaje entrega el LM35 por grado?

10 mV por cada grado Celsius. A 25 °C entrega 0.25 V y a 100 °C entrega 1 V.

¿Por qué usar un amplificador no inversor y no uno inversor?

Porque mantiene la polaridad de la señal (no la vuelve negativa) y tiene una impedancia de entrada muy alta, así que no carga la salida del LM35.

¿Puedo usar otro amplificador operacional en lugar del LM358?

Sí. Cualquier opamp que funcione con alimentación simple y cuya salida llegue cerca de 0 V sirve, por ejemplo el LM324 o un opamp rail-to-rail como el MCP6002. Con un rail-to-rail incluso puedes alimentar todo con 5 V.

¿Qué resolución se obtiene con este circuito?

Con un ADC de 10 bits, el rango de 50 °C se reparte en 1024 niveles: unos 0.05 °C por paso, frente a unos 0.5 °C si se conecta el LM35 directo.

Conclusión

Con dos etapas de un LM358, un amplificador no inversor de ganancia 10 y un restador de 2 V, la señal del LM35 entre 20 °C y 70 °C ocupa todo el rango de 0 a 5 V del ADC. El método (encontrar la recta V_{out} = mV_{in} + b y construir cada término con un opamp) sirve para acondicionar cualquier sensor lineal.

 

Deja un comentario