01 · Punto de partida
Del número a la tensión
Un pin de salida solo ofrece 0 V o Vcc. Para entregar 1,7 V a un circuito, regular el contraste de una pantalla, fijar el umbral de un comparador o reproducir un sonido, hace falta una salida analógica: una tensión que el programa pueda fijar en cualquier valor de un conjunto fino de niveles.
Muchos microcontroladores traen un DAC (digital-to-analog converter) integrado, pero el ATmega328P del Arduino Uno no. Lo que sí tiene son temporizadores con PWM y buses SPI e I2C. De allí salen las tres soluciones que veremos: una red de resistencias que suma bits (R-2R), la PWM de los temas 25 y 26 con un filtro, y un DAC externo que se maneja por bus.
La relación con el tema anterior es simétrica: allí discretizábamos tiempo y amplitud para entrar al mundo digital; acá los volvemos a unir. Los conceptos de resolución, LSB y frecuencia de actualización son los mismos que los de muestreo y cuantización.
02 · Qué entrega
Qué hace un DAC
Un DAC de n bits recibe un código entero entre 0 y 2n − 1 y entrega una tensión proporcional a una referencia:
El código máximo da Vref − 1 LSB: igual que en el ADC, nunca se alcanza del todo la referencia. Con 8 bits y 5 V el paso es de 19,5 mV; con 12 bits, 1,22 mV. La tensión entre dos códigos consecutivos no existe: la salida avanza a saltos.
| Parámetro | Qué significa |
|---|---|
| Resolución | Número de bits: define el paso ideal (LSB) |
| Monotonía | Si el código sube, la tensión nunca baja. Importa en lazos de control |
| DNL (no linealidad diferencial) | Cuánto difiere cada paso real de 1 LSB. Si DNL < −1 LSB, el DAC deja de ser monótono |
| INL (no linealidad integral) | Cuánto se aparta la salida de la recta ideal |
| Tiempo de asentamiento | Lo que tarda la salida en llegar a ½ LSB del valor final tras un cambio de código |
| Glitch | Pico transitorio al cambiar de código, mayor en las transiciones donde muchos bits cambian a la vez (0111…1 → 1000…0) |
| Impedancia de salida | Determina cuánto cae la tensión al conectar una carga |
Una diferencia con el ADC: el DAC es una función, no una búsqueda; la salida se mantiene constante entre actualizaciones (retenedor de orden cero) y su precisión depende tanto de la referencia como de la red interna.
03 · Resistencias que suman bits
La escalera R-2R
Con solo dos valores de resistencia, R y 2R, una escalera convierte un código binario en una tensión. Cada bit del código gobierna un conmutador que conecta su pata de 2R a Vref (bit en 1) o a masa (bit en 0). En cada nodo, la resistencia que se ve hacia la derecha es siempre 2R, de modo que la tensión se divide a la mitad en cada tramo. El bit más pesado llega al nodo de salida con peso 1/2, el siguiente con 1/4, y así hasta el bit menos pesado con 1/2n. El resultado es exactamente la fórmula del DAC ideal.
Es la forma natural de construir un DAC con pines de salida digitales: 8 pines del puerto D con 16 resistencias dan un DAC de 8 bits. Las salidas del microcontrolador hacen de conmutador, con una limitación: sus transistores tienen una resistencia de salida de decenas de ohmios (tema 16), que se suma a cada 2R. Se compensa con resistencias de valores altos (10 kΩ y 20 kΩ).
El punto delicado es la exactitud. Solo importa la razón entre resistencias, no su valor absoluto, pero cada una tiene tolerancia. El error de la pata del bit más pesado pesa el doble que el de cualquier otro: un 1 % de error en esa resistencia, en un DAC de 8 bits, mueve la transición central 0,64 LSB y puede dejar el paso de 127 a 128 con un valor negativo. El DAC deja de ser monótono. Para 8 bits se exigen resistencias de 0,1 % o menos en las posiciones más altas, o una red integrada con resistencias apareadas.
La impedancia de salida de la escalera es R, y por eso la salida no admite carga sin un seguidor (sección 6). Y como en el ADC, la tensión de referencia pasa directamente a la salida: las variaciones de Vref son variaciones de la salida.
04 · Una sola resistencia
PWM filtrada con RC
La alternativa más económica usa un único pin y dos componentes. Una PWM de ciclo de trabajo D tiene valor medio Vcc · D. Un filtro pasa-bajos RC deja pasar ese valor medio y atenúa la portadora (tema 25). La salida es:
| Magnitud | Fórmula | Ejemplo: R = 10 kΩ, C = 1 µF, 7812,5 Hz, D = 50 % |
|---|---|---|
| Frecuencia de corte | fc = 1 / (2π R C) | 15,9 Hz |
| Ondulación pico a pico | Vcc · D · (1 − D) / (R C fPWM) | 16 mV (0,8 LSB de 8 bits) |
| Asentamiento a ½ LSB de 8 bits | R C · ln 512 ≈ 6,24 R C | 62 ms |
| Efecto de una carga RL | Vsal · RL / (R + RL) | con RL = 10 kΩ, a la mitad |
Los números muestran el compromiso central de este DAC. La ondulación baja si aumenta R C f, pero el asentamiento crece con R C. Para una ondulación por debajo de 1 LSB hace falta que R C fPWM > 0,25 · 2n en el peor caso (D = 50 %). Subir la frecuencia de la PWM ayuda a las dos cosas, pero el temporizador de 8 bits ya trabaja a 62,5 kHz como máximo con 8 bits de resolución (tema 25): más rápido implica menos bits. Dos filtros RC en cascada (segundo orden) atenúan la portadora mucho más con un retardo moderado.
Se usa para tensiones que cambian despacio: una referencia ajustable, el contraste de una pantalla, el nivel de un umbral. No es adecuado para ondas rápidas, porque el filtro que quita la portadora también elimina cualquier señal por encima de unas decenas de hercios. Para audio de baja calidad se usa una portadora mucho más alta que la banda de audio (62,5 kHz frente a 4 kHz) y un filtro de corte intermedio.
05 · Hardware dedicado
DAC integrados y externos
Un DAC verdadero es más preciso y rápido que las dos alternativas anteriores y no consume el temporizador. Hay dos caminos:
| Dispositivo | Resolución | Interfaz | Observaciones |
|---|---|---|---|
| ESP32 (interno) | 8 bits, 2 canales | pines GPIO25 y GPIO26 | Sin buffer potente; ruido y no linealidad moderados |
| Arduino Due (SAM3X, interno) | 12 bits, 2 canales | DAC0 y DAC1 | Salida entre 1/6 y 5/6 de 3,3 V |
| Arduino Zero / MKR (SAMD21, interno) | 10 bits, 1 canal | pin A0 | También se usa para audio |
| STM32F4 (interno) | 12 bits, 2 canales | DAC_OUT1 y DAC_OUT2 | Con DMA y temporizador, genera ondas sin CPU |
| MCP4725 (externo) | 12 bits, 1 canal | I2C (tema 33) | Memoria EEPROM para recordar el valor al encender |
| MCP4921 (externo) | 12 bits, 1 canal | SPI (tema 34) | Más rápido que el I2C; referencia propia en un pin |
| RP2040, ATmega328P, ESP32-C3 | Sin DAC: PWM con filtro o integrado externo | ||
Un DAC por bus tiene ventajas claras: la resolución es de 12 bits o más, la linealidad está garantizada, la salida es estable e incluye un buffer. Su desventaja es el tiempo de escritura: una palabra de 16 bits por SPI a 4 MHz tarda 4 µs, y por I2C a 400 kHz más de 100 µs, de modo que la tasa de actualización la limita el bus y no el DAC.
06 · Entregar la tensión
Etapa de salida y carga
Una salida analógica casi nunca se conecta directa a la carga. Tres problemas típicos:
- La impedancia. Una R-2R de R = 10 kΩ o un filtro RC de 10 kΩ ve su tensión rebajada por cualquier carga comparable. Un amplificador operacional como seguidor de tensión (ganancia 1) aísla la red de la carga y entrega la corriente necesaria.
- El rango. La salida llega hasta Vref pero muchos amplificadores no entregan tensiones cercanas a la alimentación. Un op-amp «rail-to-rail» llega a unos milivolts de los extremos; uno común, como el LM358, se queda a 1,5 V del positivo. Con ganancia mayor que 1, un amplificador no inversor extiende el rango (por ejemplo, 0 a 10 V desde 0 a 5 V).
- El offset y la señal bipolar. Un DAC unipolar no produce tensiones negativas. Para una señal de audio centrada en 0 V, se suma un nivel de continua de Vref/2 y se bloquea con un capacitor de acoplamiento a la salida.
La corriente que puede entregar un pin del microcontrolador (20 mA, tema 16) no sirve para actuadores: la salida analógica es una señal de control, y la potencia se agrega con un transistor o un amplificador (tema 38).
07 · Señales que cambian
Generar formas de onda
Con un DAC, una onda es un arreglo en memoria que se recorre a ritmo constante: una tabla de onda de N puntos, un temporizador que dispara una actualización a fs y un índice que avanza. La frecuencia resultante es:
Para cambiar la frecuencia sin cambiar la tabla se usa un acumulador de fase (síntesis digital directa, DDS). Una variable de 16 bits suma un paso en cada actualización; los 8 bits altos indexan una tabla de 256 puntos. Con un paso p y una tasa fs:
El acumulador se desborda solo y da la vuelta al ciclo, y la resolución en frecuencia es fs/65 536 (0,12 Hz con 8 kHz). La tabla se calcula una vez (o se guarda en Flash con PROGMEM, tema 8).
Hay una trampa que ya conocés del tema 27. Como la salida se mantiene constante entre actualizaciones, el espectro tiene «imágenes» de la señal alrededor de múltiplos de fs: una senoidal de 250 Hz actualizada a 8 kHz trae componentes a 7750 Hz y 8250 Hz. Un filtro de reconstrucción pasa-bajos por debajo de fs/2 las elimina. El retenedor de orden cero, además, atenúa 3,9 dB la frecuencia de Nyquist, de modo que las frecuencias altas salen más bajas que las bajas.
Con pocos puntos por ciclo la onda se ve escalonada y la distorsión crece; con más puntos, el filtro puede ser sencillo. Para una senoidal razonable, 16 a 32 puntos por ciclo con un filtro RC de segundo orden.
08 · Primer programa
Código: PWM y acumulador de fase
Este programa configura el temporizador 0 como PWM rápida de 8 bits sin divisor (62,5 kHz) en OC0A (PD6), y el temporizador 2 en modo CTC a 8 kHz para actualizar la salida con una senoidal de la tabla.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
static const uint8_t SENO[256] PROGMEM = { /* tabla de 256 puntos: ver el programa de Python */ };
static volatile uint16_t fase;
static volatile uint16_t paso = 1024; // 1024 * 8000 / 65536 = 125 Hz
static void dac_pwm_init(void) {
DDRD |= (1 << PD6); // OC0A como salida
TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); // Fast PWM de 8 bits
TCCR0B = (1 << CS00); // sin prescaler: 62,5 kHz
OCR0A = 0;
}
static inline void dac_write(uint8_t valor) { OCR0A = valor; }
static void tick_8khz_init(void) {
TCCR2A = (1 << WGM21); // CTC
TCCR2B = (1 << CS21); // 16 MHz / 8 = 2 MHz
OCR2A = 249; // 2 MHz / 250 = 8 kHz
TIMSK2 = (1 << OCIE2A);
}
ISR(TIMER2_COMPA_vect) {
fase += paso; // el desborde cierra el ciclo
dac_write(pgm_read_byte(&SENO[fase >> 8]));
}
int main(void) {
dac_pwm_init();
tick_8khz_init();
sei();
for (;;) { /* aquí va el resto del programa */ }
}
Las dos tareas de temporización son independientes: el temporizador 0 sostiene la portadora y el 2 marca el ritmo de actualización. La ISR es corta (suma de 16 bits, un acceso a Flash y una escritura): del orden de 50 ciclos, es decir unos 3 µs cada 125 µs (2,5 % de la CPU). Cambiar paso cambia la frecuencia; como es de 16 bits, hay que protegerlo si se modifica fuera de la ISR (tema 21).
09 · DAC externo
Código: DAC externo por SPI
El MCP4921 recibe una palabra de 16 bits: cuatro bits de configuración y 12 de dato. Escribirla es enviar dos bytes con el chip seleccionado:
static void spi_init(void) {
DDRB |= (1 << PB3) | (1 << PB5) | (1 << PB2); // MOSI, SCK, CS como salidas
PORTB |= (1 << PB2); // CS inactivo (alto)
SPCR = (1 << SPE) | (1 << MSTR); // maestro, reloj / 4 = 4 MHz
}
static void mcp4921_write(uint16_t valor12) {
uint16_t palabra = 0x3000 | (valor12 & 0x0FFF); // DAC A, ganancia 1x, activo
PORTB &= ~(1 << PB2); // CS bajo
SPDR = palabra >> 8; while (!(SPSR & (1 << SPIF))) { }
SPDR = palabra & 0xFF; while (!(SPSR & (1 << SPIF))) { }
PORTB |= (1 << PB2); // CS alto: la salida se actualiza
}
La palabra 0x3000 activa el DAC (bit 12), fija la ganancia en 1× (bit 13) y no usa el buffer de referencia (bit 14). Con el pin LDAC a masa, la salida se actualiza cuando CS sube. Los bits de configuración conviene verificarlos con el datasheet de tu dispositivo. El bus SPI se estudia en el tema 34; por ahora importa que escribir un valor cuesta unos 5 µs con un reloj de 4 MHz.
10 · Analogía con programación
Simular la escalera, el filtro y la tabla
Este programa calcula la salida de una escalera R-2R con una resistencia fuera de tolerancia (por equivalentes de Thevenin) y muestra la pérdida de monotonía, compara la ondulación de una PWM con filtro RC entre la simulación paso a paso y la fórmula, y calcula frecuencias del acumulador de fase.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
import math
def escalera(codigo, bits, vref=5.0, tol=0.0, msb_extra=0.0):
# R-2R de n bits: se resuelve de LSB a MSB con equivalentes de Thevenin
vt, rt = 0.0, 2.0 # terminacion de 2R
for k in range(bits):
if k > 0:
rt += 1.0 # resistencia R entre nodos
pata = 2.0 * (1 + (msb_extra if k == bits - 1 else 0.0))
vk = vref if (codigo >> k) & 1 else 0.0
g1, g2 = 1 / rt, 1 / pata
vt = (vt * g1 + vk * g2) / (g1 + g2)
rt = 1 / (g1 + g2)
return vt
# 1) ideal: salida = vref * codigo / 2^n
err = max(abs(escalera(c, 8) - c * 5 / 256) for c in range(256))
print("R-2R ideal de 8 bits, error maximo:", f"{err:.1e}", "V")
# 2) un error en la pata del MSB rompe la transicion mayor
lsb = 5 / 256
for tol in (0.0, 0.005, 0.01, 0.02):
paso = (escalera(128, 8, msb_extra=tol) - escalera(127, 8, msb_extra=tol)) / lsb
print(f"pata MSB {tol*100:4.1f} % -> paso 127->128 = {paso:6.2f} LSB", "(no monotono)" if paso <= 0 else "")
# 3) PWM + RC: ondulacion simulada frente a la formula
def ondulacion(f, duty, r, c, vcc=5.0, pasos=200, periodos=400):
dt = 1 / (f * pasos)
v = vcc * duty
hist = []
for p in range(periodos):
for i in range(pasos):
fuente = vcc if i < duty * pasos else 0.0
v += (fuente - v) * dt / (r * c)
if p > periodos - 6:
hist.append(v)
return max(hist) - min(hist)
print()
print(" f (Hz) R C simulada formula")
for f, r, c in ((62500, 10e3, 1e-6), (7812.5, 10e3, 1e-6), (976.56, 10e3, 1e-6), (976.56, 10e3, 10e-6)):
sim = ondulacion(f, 0.5, r, c, pasos=100, periodos=int(8 * r * c * f) + 40)
form = 5 * 0.5 * 0.5 / (r * c * f)
print(f"{f:9.1f} {r/1e3:4.0f}k {c*1e6:4.0f}uF {sim*1000:7.2f} mV {form*1000:7.2f} mV")
print("tiempo de asentamiento a 1/2 LSB de 8 bits con RC = 10 ms:", f"{math.log(512)*10:.1f} ms")
# 4) DDS: acumulador de fase de 16 bits y tabla de 256 puntos
fs = 8000.0
print()
print("paso frecuencia de salida (fs = 8 kHz)")
for paso in (256, 512, 1024, 3277):
print(f"{paso:5d} {paso * fs / 65536:8.2f} Hz")
tabla = [round(127.5 + 127.5 * math.sin(2 * math.pi * i / 256)) for i in range(256)]
print("primeros valores de la tabla:", tabla[:8], "... maximo", max(tabla), "minimo", min(tabla))
Resultado: con resistencias ideales la salida es exacta. Un error de 0,5 % en la pata del bit más pesado deja el paso 127 a 128 en 0,36 LSB, y con 1 % pasa a −0,27 LSB: ya no es monótono. La ondulación simulada coincide con la fórmula (2 mV a 62,5 kHz y 128 mV a 976,6 Hz con 10 kΩ y 1 µF). Y un paso de 2048 da una senoidal de 250 Hz con una tasa de 8 kHz.
El modelo de la R-2R solo contempla una resistencia fuera de tolerancia y la salida sin carga; en un circuito real hay varias a la vez y la resistencia de los pines digitales se suma.
11 · Exploración interactiva
Escalera, filtro y tablas
La escalera permite aplicar tolerancias a las resistencias y ver cómo se desvía cada bit y cuánto vale el paso crítico. El filtro calcula las tres cantidades de la sección 4 para tu PWM. El generador arma una tabla de onda lista para pegar en C.
Banco de salidas analógicas
1. Escalera R-2R con tolerancia
2. PWM con filtro RC
3. Tabla de onda
Recorrido guiado
- En la escalera de 8 bits con 1 % de error en la pata del MSB, el paso central vale −0,27 LSB: no es monótona. Bajá el error a 0,5 %: queda en 0,36 LSB, ya positivo pero lejos de 1.
- Cambiá a «Todas las resistencias por igual» con 10 %: la salida no se mueve. Solo importan las razones.
- Subí a 12 bits con 0,1 % en la pata del MSB: el paso central pasa a −1,05 LSB y la escalera deja de ser monótona. Cuantos más bits, más precisión exige la red.
- En la PWM, 62 500 Hz con 10 kΩ y 1 µF da 2 mV de ondulación, 0,1 LSB. Bajá la frecuencia a 976,56 Hz: sube a 128 mV (6,6 LSB) y la nota avisa.
- Con 976,56 Hz y C = 10 µF la ondulación vuelve a 12,8 mV, pero el asentamiento se alarga a 624 ms.
- Conectá una carga de 10 kΩ: la tensión se reduce a la mitad.
- En la tabla de onda, 32 puntos a 8000 por segundo dan 250 Hz. Probá 16 puntos con 8000 por segundo y 500 Hz, y observá cómo el filtro ya casi no tiene espacio.
Desafío: audio de 1 kHz con la PWM del Timer0
Se quiere una senoidal de 1 kHz a partir de la PWM de 62,5 kHz con una tabla de 32 puntos. La tasa de actualización es 32 × 1000 = 32 kHz, lo que deja menos de 2 periodos de PWM por muestra. El filtro tiene que cortar bien por encima de 3 kHz (la tercera armónica) y por debajo de 31 kHz; dos etapas RC de 5 kΩ y 10 nF (corte de 3,2 kHz cada una) funcionan, con una ondulación residual de la portadora pequeña pero visible. Comprobalo con la herramienta 2: el filtro de primer orden solo atenúa 20 dB por década.
12 · Comprensión
Confusiones frecuentes
«analogWrite() genera una tensión analógica»
No: genera una PWM digital. Sin un filtro, el pin solo conmuta entre 0 y Vcc a 490 o 980 Hz; un multímetro lee el promedio, pero un osciloscopio muestra pulsos.
«Un DAC de 12 bits tiene 12 bits de exactitud»
Tiene 12 bits de resolución. La exactitud real depende de la referencia, el offset, la ganancia y la no linealidad: a menudo son 10 u 11 bits efectivos.
«Para una R-2R necesito resistencias exactas de 10 kΩ y 20 kΩ»
Lo que importa es la razón: 2 a 1 entre las patas y los tramos. Si todas varían igual, la salida no cambia; si no están apareadas, aparece el error de linealidad.
«Basta subir C para bajar la ondulación»
También se alarga el tiempo de asentamiento (6,24 R C) y se retrasan los cambios. Subir la frecuencia de la PWM mejora las dos cosas, pero baja la resolución.
«Conecto el DAC directo a un motor o un parlante»
La corriente de un pin o de un DAC es de unos pocos miliamperes y su impedancia es alta. Hace falta un amplificador o un transistor entre medio.
13 · Práctica guiada
Predecí antes de comprobar
1. Salida de una R-2R
Una escalera de 8 bits con Vref = 5 V recibe el código 0xA5. ¿Qué tensión entrega sin carga?
Ver solución
0xA5 = 165. V = 5 × 165 / 256 = 3,223 V.
2. PWM como DAC
Con Fast PWM de 8 bits, Vcc = 5 V y OCR0A = 63, ¿qué tensión media sale después del filtro?
Ver solución
D = (63 + 1) / 256 = 25 %. V = 1,25 V.
3. Ondulación
Con R = 10 kΩ, C = 1 µF, PWM de 7812,5 Hz y D = 50 %, ¿cuánto vale la ondulación y cuántos LSB de 8 bits son?
Ver solución
5 × 0,25 / (10 000 × 10−6 × 7812,5) = 16 mV. El LSB es 5 / 256 = 19,5 mV: 0,82 LSB.
4. Frecuencia de una tabla
Un acumulador de fase de 16 bits se actualiza a 8 kHz. ¿Qué frecuencia sale con un paso de 2048, y qué paso hace falta para 440 Hz?
Ver solución
2048 × 8000 / 65 536 = 250 Hz. Para 440 Hz: p = 440 × 65 536 / 8000 = 3604 (da 439,94 Hz).
5. PWM de 10 bits
Se quiere un DAC por PWM de 10 bits con el Timer1 a 16 MHz sin divisor. ¿Qué frecuencia de portadora resulta?
Ver solución
Fast PWM de 10 bits: 16 MHz / 1024 = 15 625 Hz. La portadora es cuatro veces más lenta que la de 8 bits, lo que obliga a un filtro con una constante de tiempo cuatro veces mayor para la misma ondulación en LSB (el LSB también es cuatro veces menor, por lo que en la práctica se necesita unas 16 veces más R·C).
14 · Síntesis
Ideas para recordar
- Un DAC convierte un código en una tensión: Vsal = Vref · código / 2n, con saltos de 1 LSB.
- El ATmega328P no tiene DAC. Las alternativas son la escalera R-2R, la PWM con filtro RC y un DAC externo por SPI o I2C.
- En la R-2R importa la razón entre resistencias; el bit más pesado exige la mayor precisión, y un error grande rompe la monotonía.
- En una PWM filtrada, la ondulación baja con R·C·f, pero el asentamiento crece con R·C: es un DAC lento para tensiones casi continuas.
- Las salidas necesitan un seguidor o amplificador: la impedancia de salida y la corriente de un DAC son limitadas.
- Las formas de onda salen de una tabla y un temporizador (f = fs/N) o de un acumulador de fase; hace falta un filtro de reconstrucción por debajo de fs/2.
¿Listo para el tema 30?
Antes de avanzar, calculá la tensión de una R-2R para un código dado, dimensioná un filtro RC para una PWM y explicá por qué un DAC de 12 bits no es necesariamente exacto a 12 bits.
El próximo tema es un taller que junta ADC y conversión a unidades: potenciómetro, LM35 y termistor, del código binario a volts, grados y unidades físicas.