Tutorial · Principiante · 30 min

Lectura de tensiones analógicas: referencias ADC y divisores

analogRead devuelve una proporción, no una tensión. Elige una referencia que no se hunda, dimensiona un divisor y construye un monitor de batería fiable.

Introducción

analogRead() es la función más amable de un Arduino y una de las más silenciosamente engañosas. Devuelve un número entre 0 y 1023, todo el mundo trata ese número como una tensión, y la mayoría de las veces nada sale mal.

Sale mal en un robot, porque un robot tiene motores, y los motores mueven justo aquello contra lo que se mide ese número.

analogRead devuelve una proporción

El ADC no mide voltios. Compara su entrada con una referencia e informa la proporción:

reading = 1023 × V_in / V_ref

En un Arduino Uno V_ref por defecto es AVCC —el riel de 5 V de la placa. Así que analogRead() en realidad responde «¿qué fracción del riel de 5 V es esto?», y convertirlo a voltios multiplicando por 5/1023 solo es correcto mientras el riel esté exactamente a 5 V.

Ya sabes que no lo está, siempre que los motores están en marcha.

La referencia es el error

Cablea una batería a través de un divisor a A0 y mira el número mientras arrancan los motores.

Dos gráficas apiladas. La superior muestra cuentas del ADC frente a tensión del pack de 6 a 8,4 voltios para dos diseños: una referencia de riel de 5 voltios con divisor 20k/10k que sube de unas 409 a 573 cuentas, y una referencia interna de 1,1 voltios con divisor 68k/10k que sube de unas 715 a 1002. La gráfica inferior muestra la tensión de batería que informa cada diseño para un pack estable de 7,4 voltios mientras el riel real de 5 voltios varía de 4,2 a 5,2 voltios: el diseño con referencia al riel cae de 8,8 voltios a 7,1 voltios mientras el diseño con referencia interna se mantiene plano en 7,4.
Abajo: el pack nunca se mueve. Mientras el riel se hunde de 5,0 V a 4,2 V bajo carga de los motores, el código que confía en una referencia de 5 V informa que la batería sube de 7,4 V a 8,8 V. La referencia bandgap informa 7,40 V en todo momento. Arriba: los mismos 10 bits repartidos en 286 cuentas en lugar de 164, porque el divisor se dimensionó para llenar la referencia. Descargar SVG

Un corte por batería baja construido sobre el primer diseño hace lo contrario de su trabajo: cuanto más trabaja el robot, más sana parece la batería. Y el fallo es silencioso, porque cada lectura individual es una proporción perfectamente válida.

La solución es una referencia que no se mueva. El ATmega328P tiene una referencia bandgap de 1,1 V integrada:

analogReference(INTERNAL);   // 1.1 V on an Uno; INTERNAL1V1 on a Mega
analogRead(A0);              // discard — the reference needs a moment to settle

Desecha siempre la primera lectura tras cambiar de referencia. La referencia interna y el condensador de entrada necesitan tiempo, y la primera muestra se toma antes de que lo hayan tenido.

Dimensionar el divisor

La referencia ahora es de 1,1 V y tu pack es de 8,4 V, así que el divisor tiene que dejar el tope del rango justo por debajo de la referencia:

V_adc = V_pack × R2 / (R1 + R2)

Con R1 = 68 kΩ y R2 = 10 kΩ la proporción es 0,128, así que un pack lleno de 8,4 V presenta 1,077 V —justo por debajo de la referencia, usando casi toda la escala. Tres números que conviene comprobar en cualquier divisor que diseñes:

Comprobación Este divisor Por qué
Tope del rango frente a referencia 1,077 V frente a 1,1 V Por encima de la referencia y recorta en plano
Corriente en reposo 108 µA Consume 2,6 mAh al día —despreciable frente a 2000 mAh
Impedancia de salida (R1∥R2) 8,7 kΩ Mantén por debajo de ~10 kΩ, ver abajo

Compáralo con la versión ingenua —un divisor 20 kΩ/10 kΩ leído contra el riel de 5 V. Pone un pack lleno en 2,80 V, que son 573 cuentas de 1023 posibles, y usa 164 cuentas en todo el rango 6,0–8,4 V del pack frente a las 286 del mejor diseño. Casi el doble de resolución, por las mismas dos resistencias y una línea de código.

La resolución no es la precisión

Diez bits contra una referencia de 5 V son 4,88 mV por paso; contra la referencia de 1,1 V son 1,07 mV. Ninguno de los dos números es tu precisión.

Lo que realmente te limita:

  • Tolerancia de la referencia. El bandgap es nominalmente 1,1 V y varía unos pocos puntos porcentuales entre chips. Eso es un error fijo de escala, así que mídelo una vez por placa: aplica una tensión conocida, lee las cuentas y despeja la referencia real. Guarda esa constante. Esto es un paso de calibración, no un refinamiento opcional.
  • Ruido. La alimentación de un robot es ruidosa, y promediar es la respuesta barata. Promediar N muestras reduce el ruido aleatorio en √N —16 muestras son 4× más silenciosas, un bit extra de resolución utilizable. No hace nada contra un error de escala.
  • Impedancia de fuente. El ADC carga un pequeño condensador de muestreo y retención a través de lo que tenga conectado. Una fuente de alta impedancia no puede llenarlo en el tiempo permitido, y el residuo del canal leído antes sigue ahí dentro.

Eso último produce un error distintivo que merece nombre.

Canales que leen como sus vecinos

Lee A0 y A1 por turnos con fuentes de alta impedancia y cada lectura es arrastrada hacia la anterior. Barre un potenciómetro en A0 y mira cómo A1 se mueve en simpatía. Nada está mal cableado.

Dos soluciones, y puedes usar ambas:

int readSettled(uint8_t pin) {
  analogRead(pin);        // charge the sample-and-hold on this channel
  return analogRead(pin); // then take the one you keep
}

Leer dos veces y descartar la primera da al condensador una segunda ventana de muestreo para alcanzar la nueva tensión. La otra solución es bajar la impedancia de fuente —por eso el divisor de arriba usa 68 kΩ/10 kΩ en lugar de los 100 kΩ/100 kΩ que parecen más ordenados y dan una impedancia de salida de 50 kΩ.

Dónde aparece esto en un robot

  • Monitorización de batería, como arriba —y es lo que hace un corte por baja tensión lo bastante fiable para proteger un pack de Li-ion.
  • Sensores de línea analógicos. Un array de reflectancia IR da un valor continuo por sensor, que es lo que permite a un seguidor de línea calcular una posición suave en lugar de una bandera izquierda/derecha. Esos sensores se leen contra el mismo riel que los motores están hundiendo, que es una razón por la que la calibración de un seguidor de línea deriva a medida que se vacía la batería.
  • Medición de corriente, una resistencia shunt o sensor Hall cuya salida es una tensión.
  • Potenciómetros como mandos de ajuste, que es la forma más amable de ajustar una ganancia PID sin reprogramar.
  • No para el sensor de 5 V de una placa de 3,3 V. Un ESP32 o una Raspberry Pi necesita el divisor por protección, no solo por rango.

Cuando algo sale mal

Síntoma Normalmente
La lectura de batería sube cuando arrancan los motores Referenciada al riel que se hunde; usa INTERNAL
Lectura clavada en 1023 Entrada por encima de la referencia —proporción del divisor demasiado alta
Un canal repite el eco del leído antes Impedancia de fuente demasiado alta; lee dos veces y descarta
Las lecturas saltan decenas de cuentas Ruido de alimentación; promedia, y revisa los rieles de alimentación
Tensión siempre desviada unos pocos puntos porcentuales Referencia interna sin calibrar —mídela una vez
Primera lectura tras analogReference() errónea Esperado; descártala
La calibración del sensor de línea deriva al vaciarse la batería Lecturas referenciadas al riel en un riel que cae
Solo se usa el 15 % del rango Divisor dimensionado para 5 V leyendo contra 1,1 V

La regla que evita la mayor parte de esto: decide cuál es tu referencia antes de decidir nada más. Cada número que te da el ADC es relativo a ella, así que una referencia que se mueve hace que cada lectura cuente dos cosas a la vez.

Explora el grafo

Forma parte de estos montajes

Proyectos y rutas de aprendizaje que incluyen este tutorial.

Lecturas adicionales

Referencias