3.12. Lectura de señales analógicas con el ADC

Hasta ahora la cámara ha estado leyendo señales digitales: un pin está en 0 o en 1, un interruptor está abierto o cerrado. La mayoría de las señales que provienen de sensores del mundo real son analógicas: una tensión continua que varía de forma suave a lo largo de algún rango. Una fotorresistencia recorre todas las tensiones entre los raíles a medida que cambia el brillo ambiental. La salida de un sensor de temperatura se desplaza unos pocos milivoltios a medida que una habitación se calienta. La salida de un micrófono sube y baja con el sonido que lo rodea.

Un convertidor analógico-digital (ADC) es el puente. Muestrea la tensión en un pin y devuelve un entero que Python puede leer como cualquier otro valor.

3.12.1. Cuantización

Un valor digital no puede representar una tensión continua de forma exacta. La tarea del ADC es cuantizar: ajustar cada muestra al más cercano de un conjunto fijo de niveles. Un ADC de N bits tiene 2^N niveles; un convertidor de 12 bits tiene 4096 de ellos repartidos por su rango de entrada.

Una curva analógica suave representada frente al tiempo, superpuesta con una aproximación digital escalonada. Líneas horizontales discontinuas marcan los niveles de cuantización; la curva escalonada se ajusta a cualquiera que sea el nivel más cercano a la señal analógica en cada punto de muestreo.

Cuantización: cada muestra de la señal analógica (línea continua) se redondea a uno de un conjunto finito de niveles digitales (línea discontinua escalonada).

La tensión entre dos niveles adyacentes es el tamaño de paso del ADC; cualquier cosa más pequeña que eso desaparece en el redondeo. Un ADC de 12 bits sobre un rango de 3,3 V tiene un tamaño de paso de unos 3.3 / 4096 0.8 mV: lo suficientemente fino como para que la mayoría de las señales parezcan efectivamente continuas en software.

3.12.2. La clase machine.ADC

machine.ADC envuelve un canal de entrada analógica. Constrúyelo con el pin que quieras leer y luego llama a read_u16():

from machine import ADC

adc = ADC("P6")
value = adc.read_u16()
print(value)

read_u16() siempre devuelve un entero sin signo de 16 bits entre 0 y 65535. La resolución nativa del ADC varía según la placa (12 bits en STM32, específica del puerto en otros casos); el resultado se alinea a la izquierda en 16 bits para que el detalle del hardware no se filtre en Python: un valor de 65535 es fondo de escala independientemente del chip.

La tensión de referencia (la entrada que corresponde al fondo de escala) depende de la placa. Consulta la Placas OpenMV para conocer el valor en tu cámara. Cualquier cosa por encima de la referencia se lee como fondo de escala (y puede dañar el pin si supera la tensión de entrada máxima absoluta).

3.12.2.1. Conversión de cuentas a tensión

La correspondencia de cuentas a tensión es lineal, con las cuentas de fondo de escala asignadas exactamente a Vref:

voltage = counts × Vref / 65535

En código:

VREF = 3.3  # cam-dependent; see the quickref
counts = adc.read_u16()
voltage = counts * VREF / 65535
print(voltage, "V")

3.12.3. Divisores de tensión

Dos resistencias en serie entre un raíl de tensión y tierra forman un divisor de tensión. El nodo entre ellas se sitúa a una tensión determinada por la relación entre las dos resistencias:

Un divisor de tensión. Vin en la parte superior se conecta a través de R1 a un nodo derivado como V_out, que luego se conecta a través de R2 a tierra.

Un divisor de tensión: R1 y R2 en serie reducen Vin hasta V_out.

V_out = Vin × R2 / (R1 + R2)

Resistencias iguales dan la mitad de la tensión del raíl; R2 mucho menor que R1 sitúa la derivación cerca de tierra; R2 mucho mayor la sitúa cerca del raíl.

La fórmula asume que nada más extrae una corriente apreciable de V_out. Un pin de ADC es de alta impedancia (megaohmios, nanoamperios) y cumple eso fácilmente, de modo que un divisor que alimenta un ADC se comporta como predice la fórmula.

3.12.4. Potenciómetros

Un potenciómetro es un único componente físico que es exactamente un divisor de tensión, con un cursor deslizante que mueve la derivación entre los dos extremos. Girar la perilla cambia R1 y R2 a la vez manteniendo constante su suma (la resistencia total del potenciómetro).

Un potenciómetro conectado entre 3,3 V y tierra. El cursor se deriva a un pin de ADC.

Un potenciómetro conectado como fuente de tensión manual para el ADC: 3,3 V en un extremo, tierra en el otro, y el cursor al pin.

Un potenciómetro es el dispositivo de entrada canónico para probar el ADC. Conecta un extremo a 3.3 V, el otro a tierra y el cursor a un pin con capacidad de ADC; girar la perilla hace que el cursor recorra todas las tensiones entre los raíles.

import time
from machine import ADC

pot = ADC("P6")
VREF = 3.3

while True:
    counts = pot.read_u16()
    voltage = counts * VREF / 65535
    print(voltage, "V")
    time.sleep_ms(100)

3.12.5. Lectura de tensiones más altas con un divisor

Una tensión por encima de Vref fijará el ADC en el fondo de escala y puede dañar la entrada si supera el valor máximo absoluto. Para leer una fuente más alta (una batería, la salida de un sensor cuyo rango supera Vref) redúcela con un divisor de tensión fijo antes de que llegue al pin:

Un divisor de tensión que reduce un V_in alto hasta un pin de ADC. R1 va desde V_in hasta una unión, que se deriva horizontalmente al pin del ADC; R2 continúa desde la unión hasta tierra.

Escalado de una fuente de alta tensión para que se ajuste al ADC: R1 y R2 forman un divisor de tensión fijo cuya derivación alimenta el pin del ADC.

Elige R1 y R2 de modo que la tensión dividida permanezca dentro del rango del ADC a la tensión de entrada más alta que esperes:

V_adc = V_in × R2 / (R1 + R2)

Para un V_in = 12 V máximo y una referencia de 3,3 V, la relación R2 / (R1 + R2) debe ser como máximo 3.3 / 12 0.275. Una elección común con un pequeño margen es R1 = 33 , R2 = 10 . La relación es 10 / 43 0.233, de modo que V_adc llega como máximo a unos 12 × 0.233 2.79 V: con seguridad por debajo de Vref.

Para recuperar el V_in original a partir de una lectura del ADC, invierte la fórmula del divisor:

V_in = V_adc × (R1 + R2) / R2

En código:

from machine import ADC

R1 = 33_000
R2 = 10_000
VREF = 3.3

adc = ADC("P6")

counts = adc.read_u16()
v_adc = counts * VREF / 65535
v_in = v_adc * (R1 + R2) / R2
print(v_in, "V")

Algunas notas prácticas:

  • El divisor consume V_in / (R1 + R2) de forma continua. Con R1 + R2 = 43 y V_in = 12 V, eso son unos 280 µA: normalmente insignificante, pero si la fuente funciona con batería considera resistencias más grandes (100 kΩ a 1 MΩ) para reducir el consumo en reposo.

  • La tolerancia de las resistencias (normalmente ±1 % o ±5 %) repercute directamente en la precisión de la medición. Dos resistencias del ±5 % pueden dar al V_in recuperado un error en el peor caso de aproximadamente ±10 %.

  • La impedancia de fuente del divisor se combina con cualquier capacidad parásita para filtrar la entrada con un paso bajo. Para señales que cambian rápidamente eso importa; para una comprobación de la tensión de una batería no.