← Índice del curso

Circuitos Secuenciales y Memoria · Tema 29

EEPROM: datos que permanecen

La EPROM del tema anterior obligaba a sacar el chip, iluminarlo con luz ultravioleta y reprogramarlo entero. La EEPROM (Electrically Erasable PROM) elimina ese ritual: se borra y se escribe con señales eléctricas, un byte por vez y sin sacarla del circuito. Es la memoria donde un equipo guarda lo que debe sobrevivir a un apagón: la calibración de un sensor, el número de serie, la configuración del usuario, la última lectura. Tiene una limitación que todo programador debe aprender a respetar: cada celda aguanta una cantidad finita de escrituras.

01 · Punto de partida

De la EPROM a la EEPROM

Una EPROM se programaba bit a bit con tensión alta (solo de 1 a 0) y se borraba toda a la vez con luz. Cada uno de esos pasos era incómodo: un programador externo, una lámpara UV, un chip con ventana. La EEPROM resuelve los tres:

Qué cambia de la EPROM a la EEPROM.
AspectoEPROMEEPROM
BorradoLuz ultravioleta, todo el chipEléctrico, un byte (o una página)
ProgramaciónProgramador externo con VPPCon el chip en el circuito, con la alimentación normal
Tensión altaLa suministra el programadorLa genera el propio chip (bomba de carga)
EncapsuladoCon ventana de cuarzoPlástico común
Reescribir un byteImposible sin borrar todoPosible, en unos milisegundos
Densidad y costoMayor densidad, más barataCelda más grande, más cara por bit

Con esa comodidad, la EEPROM cambió el uso de las memorias no volátiles: dejó de ser solo el lugar del programa y pasó a ser un almacén de datos que cambian poco durante la vida del equipo. El programa mismo suele estar en Flash (tema 30), de la que la EEPROM es una pariente cercana.

02 · La celda

La celda de compuerta flotante

Como en la EPROM, el bit se guarda como carga eléctrica atrapada en una compuerta flotante (tema 28): una isla de silicio totalmente rodeada de óxido, que ni los electrones ni el tiempo pueden atravesar fácilmente. Con carga atrapada, el transistor necesita más tensión de compuerta para conducir; sin carga, menos. Al leer, se aplica una tensión intermedia y se mira si el transistor conduce o no.

La diferencia está en cómo se mueve la carga. En la EEPROM, una zona del óxido es muy delgada (del orden de 10 nm) y permite el efecto túnel (Fowler–Nordheim): con un campo eléctrico intenso, los electrones atraviesan el óxido en ambos sentidos. Aplicando la tensión en un sentido se inyectan electrones en la compuerta (se programa); en el sentido contrario se extraen (se borra). No hace falta luz ni un programador: bastan unos 12 a 20 V, que el chip fabrica a partir de los 5 V o 3,3 V de alimentación con una bomba de carga interna.

  • Un transistor de selección por celda. Cada celda de una EEPROM tiene, además del transistor de compuerta flotante, otro de selección que la aísla durante el borrado de un solo byte. Por eso la celda es más grande que la de EPROM o Flash, y la capacidad por chip es menor.
  • El óxido se gasta. Cada vez que los electrones lo atraviesan, una pequeña cantidad queda atrapada en el óxido y degrada el aislante. Con miles de ciclos, la diferencia entre «cargado» y «descargado» se acorta hasta no poder distinguirse. Este es el origen del límite de ciclos.
  • El valor borrado. Una EEPROM borrada lee normalmente 0xFF (como la EPROM); escribir pone ceros donde corresponde. Pero como se borra la palabra antes de escribir, no hay restricción de «solo bajar bits».

03 · Escritura

Escribir: bytes, páginas y espera

Leer una EEPROM es igual que leer una ROM (tema 28): dirección, CE̅, OE̅ y el dato aparece. Escribir es otra historia, mucho más lenta que la lectura. Un ciclo de escritura de un byte incluye, internamente, borrar el byte y luego programarlo, y cada paso requiere milisegundos. El tiempo total, tWC, es de unos 3 a 10 ms por escritura (contra decenas de nanosegundos de la lectura).

Lectura frente a escritura en una EEPROM (valores típicos).
OperaciónTiempoDesgasta
Lectura de un bytedecenas a cientos de ns (paralela); 22 µs por byte a 400 kHz en I²CNo
Escritura de un byte3 a 10 msSí, un ciclo de esa celda
Escritura de una página3 a 10 ms (el mismo, para toda la página)Sí, un ciclo de cada celda escrita

El procedimiento y la espera

  1. El procesador entrega dirección y dato con una señal de escritura (o la trama equivalente en las memorias serie).
  2. La EEPROM latchea la dirección y el dato y arranca un temporizador interno: de ahí en más no necesita nada del bus.
  3. Durante tWC la memoria está ocupada: ignora o rechaza los accesos. Escribir de nuevo antes de que termine es un error.
  4. El procesador detecta el fin de dos formas: esperando el tiempo máximo del datasheet, o preguntando (polling).

El polling es más eficiente porque muchas escrituras terminan antes del máximo. En las EEPROM paralelas se lee la posición escrita: durante la escritura, el bit 7 del dato está complementado (DATA polling) o el bit 6 conmuta en cada lectura (toggle bit); cuando el dato leído vuelve a ser el escrito, terminó. Algunas tienen un pin READY/BUSY. En las EEPROM I²C se envía la dirección del dispositivo repetidamente: no contesta (NACK) hasta que termina la escritura y recién entonces confirma (ACK) — es el ACK polling.

Escritura por páginas

Como el tiempo de escritura es el mismo para un byte que para varios, las EEPROM aceptan una página de bytes (típicamente 8 a 128) en una sola operación: se envían todos seguidos, la memoria los guarda en un buffer, y un único ciclo interno los escribe a la vez. Escribir 64 bytes por página cuesta lo mismo que escribir 1, una ganancia enorme.

Reglas de la escritura por páginas.
ReglaConsecuencia
Una página es un bloque alineado (por ejemplo, 64 bytes desde una dirección múltiplo de 64)Los límites de página están fijos por el chip, no por el programa
Los bytes de una página deben llegar seguidos, sin demoras largasSi el procesador tarda demasiado entre bytes, la memoria cierra el buffer y empieza a escribir
Si se envían más bytes que los que quedan en la página, dan la vuelta dentro de la misma páginaSe pisan los primeros bytes de la página; la página siguiente no se toca

La última regla es una de las trampas más comunes: al guardar una estructura de 20 bytes que empieza en la posición 58 de una página de 64, los primeros 6 bytes caen al final de la página y los otros 14 vuelven al comienzo, pisando datos válidos. El código debe partir las escrituras en tramos que no crucen un límite de página (el paso 1 del código muestra cómo).

Protección contra escrituras accidentales

Una escritura es peligrosa si ocurre por error (ruido en el bus, un programa descontrolado, la caída de la tensión). Las EEPROM tienen defensas: un pin WP (write protect) que bloquea toda escritura, una secuencia de desbloqueo de varios bytes especiales que hay que enviar antes de escribir (protección por software) y, en algunas, bloques que se pueden proteger de forma permanente.

04 · EEPROM serie

EEPROM serie (I²C y SPI)

La mayoría de las EEPROM que usan hoy los microcontroladores son seriales: pocos pines (8), poco espacio de placa y un bus compartido. Las dos familias más comunes:

Familias de EEPROM serie.
FamiliaBusEjemplosPines de datos y reloj
24xxI²C (dos hilos)24C02 (256 bytes), 24C64 (8 KiB), 24LC256 (32 KiB)SDA (datos) y SCL (reloj), más 3 pines A0–A2 de dirección y WP
25xxSPI (cuatro hilos)25LC256 (32 KiB)MOSI, MISO, SCK y CS̅, más WP y HOLD
93xxMicrowire (tres hilos)93C46 (128 bytes)DI, DO, CLK y CS

En una 24xx típica, la dirección del dispositivo es 1010 A2 A1 A0 más un bit de lectura o escritura: cambiando los tres pines de dirección se conectan hasta 8 chips en el mismo bus. Una escritura es una secuencia de bytes: dirección del dispositivo, dirección de memoria (uno o dos bytes según el tamaño), datos (uno o toda una página) y una condición de parada. La parada es la que dispara el ciclo interno de escritura de unos 5 ms.

05 · Retención y ciclos

Retención y ciclos de escritura

Dos números describen la vida de una EEPROM y están en la primera página de su hoja de datos:

Especificaciones de duración (valores típicos; consultá el datasheet del chip).
ParámetroQué diceValor típico
Retención de datosCuánto tiempo conserva un dato escrito, sin alimentación10 a 100 años (disminuye con la temperatura y con el número de ciclos acumulados)
Resistencia (endurance)Cuántas veces se puede escribir cada celda100 000 a 1 000 000 de ciclos por byte

La retención es una propiedad de la carga atrapada, que se escapa muy lentamente; el calor la acelera. La resistencia es una propiedad del óxido, que se degrada con cada ciclo. Son números que nada tienen que ver con las lecturas: leer no desgasta, se puede leer indefinidamente.

Cien mil ciclos parecen muchos, pero se agotan rápido si se escribe siempre en el mismo lugar:

Vida útil de un byte de 100 000 ciclos según con qué frecuencia se reescribe.
Frecuencia de reescrituraVida útil
10 veces por segundo2,8 horas
Una vez por segundo27,8 horas
Una vez por minuto69 días
Una vez por hora11,4 años

Es decir: un registro de eventos que escribe cada segundo en la misma posición dura poco más de un día. Hay que diseñar con ese límite a la vista, como se hace a continuación.

06 · Diseño

Diseñar con una memoria que se desgasta

1. Escribir lo mínimo

  • Escribir solo si cambió: leer el valor guardado y escribir únicamente si es distinto. Leer es gratis.
  • Guardar en RAM lo que cambia seguido: mantener el contador en una variable y copiarlo a la EEPROM solo cada tanto, ante un evento importante o al detectar que se apaga (caída de tensión).
  • Agrupar campos: una escritura de página en vez de muchos bytes sueltos.

2. Repartir el desgaste (wear leveling)

Si un dato debe escribirse a menudo, se lo guarda en una ronda de N posiciones en lugar de una: cada escritura va a la siguiente, y la vida útil se multiplica por N. Para leerlo hay que saber cuál fue la última (un número de secuencia, o buscar el valor más reciente).

Vida útil de un dato escrito una vez por segundo, repartido entre N posiciones (100 000 ciclos por celda).
PosicionesVida útil
127,8 horas
89 días
6474 días
512593 días
409613 años

3. Sobrevivir a un corte de energía durante la escritura

Si la energía se corta a mitad de una escritura, la celda queda con un valor indefinido (ni el viejo ni el nuevo) y, en una estructura de varios bytes, una mezcla de datos viejos y nuevos. La técnica estándar:

  • Dos copias alternadas: el dato se guarda en dos ranuras; cada escritura va a la más vieja, nunca a la última válida.
  • Número de secuencia y CRC: cada ranura lleva un contador creciente y un código de verificación (tema 28). Al arrancar se leen las dos y se toma la más nueva con CRC correcto.
  • Resultado: si el corte ocurre durante la escritura, la ranura a medio escribir tiene CRC inválido y se descarta; la otra, completa, sigue ahí. Se pierde, como mucho, la última actualización, pero nunca se lee basura.
  • Detección de caída de tensión (brown-out): un circuito que avisa cuando la alimentación baja de un umbral permite terminar o no empezar una escritura. Un condensador de reserva da los milisegundos necesarios.
  • Verificar por lectura: después de escribir, leer y comparar. Detecta celdas gastadas o escrituras fallidas.

07 · Comparación

EEPROM frente a otras memorias

Memorias que guardan datos con y sin energía.
MemoriaNo volátilBorradoEscrituraCiclosUso típico
SRAMNo—Ns, byteIlimitadosDatos de trabajo
EPROMSíUV, todo el chipProgramador, byte (solo 1→0)~100Firmware en desarrollo
EEPROMSíEléctrico, por bytems, byte o página105 a 106Configuración, calibración, contadores
FlashSíEléctrico, por bloqueµs por byte, ms por bloque104 a 105Programa y datos grandes (tema 30)
FRAMSíNo hace faltaNs, byte1012 o másRegistro frecuente de datos

La EEPROM ocupa un nicho claro: pocos datos (cientos de bytes a decenas de KiB) que se modifican byte a byte con poca frecuencia. Cuando los datos cambian muy seguido, la FRAM o una SRAM con batería son mejores; cuando son muchos, la Flash. Muchos microcontroladores traen una pequeña EEPROM interna precisamente para configuración y calibración.

08 · La EEPROM en código

Páginas, desgaste, guardado seguro y tiempos

Los bloques reproducen la vuelta de página, calculan la vida útil con y sin rotación, implementan el guardado con dos ranuras y CRC y simulan cortes de energía, y comparan escritura por bytes y por páginas en un bus I²C.

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

Paso 1: la vuelta de página

class EEPROM:
    """EEPROM con páginas: una escritura de página que cruza el límite da la vuelta DENTRO de la página."""

    def __init__(self, tamano=256, pagina=64):
        self.datos = [0xFF] * tamano
        self.pagina = pagina

    def escribir_byte(self, a, v):
        self.datos[a] = v & 0xFF

    def escribir_pagina(self, a, bytes_):
        base = (a // self.pagina) * self.pagina
        off = a % self.pagina
        for i, b in enumerate(bytes_):
            self.datos[base + (off + i) % self.pagina] = b & 0xFF


m = EEPROM()
for i in range(64):
    m.datos[i] = i                                        # contenido previo conocido en la primera página
print("antes: direcciones 60..63 =", m.datos[60:64], "  direcciones 0..5 =", m.datos[0:6])
m.escribir_pagina(60, [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6])      # 7 bytes desde la dirección 60
print("escribo 7 bytes desde la 60 (solo caben 4 en esta página):")
print("  direcciones 60..63 =", [hex(x) for x in m.datos[60:64]])
print("  direcciones 0..2   =", [hex(x) for x in m.datos[0:3]], "  <- los 3 bytes sobrantes PISARON el comienzo de la página")
print("  dirección 64       =", hex(m.datos[64]), "  (la página siguiente no se tocó)")

m2 = EEPROM()
def escribir_seguro(mem, a, bytes_):
    """Parte la escritura en tramos que no cruzan el límite de página."""
    while bytes_:
        libres = mem.pagina - (a % mem.pagina)
        tramo, bytes_ = bytes_[:libres], bytes_[libres:]
        mem.escribir_pagina(a, tramo)
        a += len(tramo)


escribir_seguro(m2, 60, [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6])
print()
print("partiendo la escritura en tramos de página:")
print("  60..63 =", [hex(x) for x in m2.datos[60:64]], " 64..66 =", [hex(x) for x in m2.datos[64:67]])

Escribiendo 7 bytes desde la dirección 60 en páginas de 64, los primeros 4 quedan en 60 a 63; los 3 restantes no pasan a la página siguiente sino que dan la vuelta y pisan las direcciones 0, 1 y 2 (que valían 0, 1 y 2). La dirección 64 sigue en 0xFF. Partiendo la escritura en tramos que respetan el límite, los 3 bytes sobrantes llegan a 64, 65 y 66, que es lo que se quería.

Paso 2: la vida útil y la rotación

endurance = 100_000                          # ciclos de escritura por byte (valor típico de EEPROM serie)


def duracion(segundos):
    if segundos < 3600 * 48:
        return f"{segundos / 3600:.1f} horas"
    if segundos < 86400 * 365 * 2:
        return f"{segundos / 86400:.0f} días"
    return f"{segundos / 86400 / 365:.1f} años"


print(f"endurance por byte: {endurance:,} ciclos\n")
print("vida útil si se reescribe SIEMPRE el mismo byte:")
for texto, periodo_s in (("10 veces por segundo", 0.1), ("una vez por segundo", 1), ("una vez por minuto", 60), ("una vez por hora", 3600)):
    print(f"  {texto:22s}: {duracion(endurance * periodo_s)}")

print()
print("con rotación de direcciones (wear leveling) entre N posiciones, a 1 escritura por segundo:")
for N in (1, 8, 64, 512, 4096):
    print(f"  N = {N:4d} posiciones: {duracion(endurance * N * 1)}")

print()
dias = 10 * 365
print("escrituras por día que se pueden permitir si la memoria debe durar 10 años:")
print(f"  un solo byte: {endurance / dias:.1f} escrituras por día")
print(f"  32 768 bytes rotando: {endurance * 32768 / dias:,.0f} escrituras por día")

Con 100 000 ciclos, un byte reescrito una vez por segundo dura 27,8 horas, y diez veces por segundo, menos de tres. Repartiendo la escritura entre 8 posiciones se llega a 9 días; entre 4096, a 13 años. Para que un solo byte dure 10 años, sólo se pueden permitir unas 27 escrituras por día; rotando por los 32 768 bytes de una 24LC256, casi 900 000 por día. Cambiá endurance a 1 000 000 para ver el efecto.

Paso 3: guardado con dos ranuras y CRC

import random
from binascii import crc_hqx


def crc16(b):
    return crc_hqx(bytes(b), 0xFFFF)


class Almacen:
    """Dos ranuras de 8 bytes: [número de secuencia (2), dato (4), CRC (2)]. Siempre se escribe en la ranura MÁS VIEJA."""

    def __init__(self):
        self.ranuras = [bytearray(8), bytearray(8)]

    def empaquetar(self, seq, dato):
        cuerpo = seq.to_bytes(2, "big") + dato.to_bytes(4, "big")
        return bytearray(cuerpo + crc16(cuerpo).to_bytes(2, "big"))

    def leer_ranura(self, i):
        r = self.ranuras[i]
        if crc16(r[:6]) == int.from_bytes(r[6:], "big") and any(r[:6]):
            return int.from_bytes(r[:2], "big"), int.from_bytes(r[2:6], "big")
        return None

    def guardar(self, dato, cortar_a_la_mitad=False, rnd=None):
        validas = [self.leer_ranura(i) for i in (0, 1)]
        seqs = [v[0] if v else -1 for v in validas]
        destino = 0 if seqs[0] <= seqs[1] else 1                         # la más vieja (o inválida)
        nueva_seq = max(max(seqs), 0) + 1
        pack = self.empaquetar(nueva_seq, dato)
        if cortar_a_la_mitad:                                           # se corta la energía durante la escritura
            for k in range(rnd.randint(1, 7)):
                self.ranuras[destino][k] = pack[k]
            self.ranuras[destino][rnd.randint(0, 7)] ^= 0x5A
        else:
            self.ranuras[destino] = pack

    def cargar(self):
        validas = [v for v in (self.leer_ranura(0), self.leer_ranura(1)) if v]
        return max(validas)[1] if validas else None


rnd = random.Random(29)
a = Almacen()
for valor in (100, 200, 300):
    a.guardar(valor)
print("guardo 100, 200, 300 -> cargo:", a.cargar())
a.guardar(400, cortar_a_la_mitad=True, rnd=rnd)
print("corte de energía al guardar 400  -> cargo:", a.cargar(), "(se conserva el último valor completo)")
a.guardar(500)
print("guardo 500 -> cargo:", a.cargar())

# estadística: 1000 cortes en momentos al azar
buenos = 0
for _ in range(1000):
    s = Almacen()
    for v in range(1, rnd.randint(2, 6)):
        s.guardar(v * 10)
    previo = s.cargar()
    s.guardar(999, cortar_a_la_mitad=True, rnd=rnd)
    c = s.cargar()
    buenos += c in (previo, 999)
print(f"1000 cortes en medio de una escritura: {buenos} dejaron un valor completo (el anterior o el nuevo)")

# comparación con una sola copia sin protección: se escriben k de 6 bytes y se corta
mezclas = 0
for _ in range(1000):
    viejo, nuevo = bytes(rnd.randrange(256) for _ in range(6)), bytes(rnd.randrange(256) for _ in range(6))
    k = rnd.randint(0, 6)                                             # bytes que alcanzaron a escribirse
    quedo = nuevo[:k] + viejo[k:]
    mezclas += quedo not in (viejo, nuevo)
print(f"una sola copia sin CRC: {mezclas} de 1000 cortes dejan una mezcla ilegible de dato viejo y nuevo")

Con tres guardados completos, se carga el último (300). Cuando se corta la energía al guardar 400, la ranura a medio escribir tiene un CRC inválido y se descarta, así que se sigue cargando 300; después de guardar 500 todo vuelve a la normalidad. En 1000 cortes aleatorios, siempre queda un valor completo (el anterior o el nuevo). En cambio, con una sola copia sin CRC, unos 7 de cada 10 cortes dejan una mezcla de dato viejo y nuevo que parece válida y no lo es.

Paso 4: bytes frente a páginas en I²C

# Escribir 1 KiB en una EEPROM serie I2C a 400 kHz, con tiempo interno de escritura de 5 ms.
f_i2c = 400_000
t_bit = 1 / f_i2c
t_byte_bus = 9 * t_bit                                  # 8 bits + ACK
t_wr = 5e-3                                             # tiempo de ciclo de escritura interno
tamano = 1024
pagina = 64

# byte a byte: cada byte necesita dirección de dispositivo (1 byte) + dirección (2 bytes) + dato (1 byte) + espera de escritura
t_byte = (1 + 2 + 1) * t_byte_bus + t_wr
total_byte = tamano * t_byte
# por páginas: una transacción por página con 3 bytes de encabezado + 64 de datos + una sola espera
t_pag = (3 + pagina) * t_byte_bus + t_wr
total_pag = (tamano // pagina) * t_pag
print(f"I2C a {f_i2c / 1000:.0f} kHz: un byte en el bus tarda {t_byte_bus * 1e6:.1f} us")
print(f"escritura byte a byte de {tamano} bytes: {total_byte:.2f} s  ({t_byte * 1e3:.2f} ms por byte, dominada por la espera de {t_wr * 1e3:.0f} ms)")
print(f"escritura por páginas de {pagina} bytes:     {total_pag * 1e3:.0f} ms  ({t_pag * 1e3:.2f} ms por página)")
print(f"la escritura por páginas es {total_byte / total_pag:.0f} veces más rápida")
print()
print(f"lectura secuencial de {tamano} bytes: {(3 + tamano) * t_byte_bus * 1e3:.1f} ms (sin espera de escritura)")
print("-> leer es unas", round(total_byte / ((3 + tamano) * t_byte_bus)), "veces más rápido que escribir byte a byte")

Escribir 1 KiB byte a byte por I²C tarda 5,2 segundos, casi todo esperando los 5 ms internos de cada byte (5,09 ms por byte). Por páginas de 64 bytes son 16 transacciones de 6,5 ms: 104 ms en total, 50 veces menos. Leer el mismo kilobyte lleva 23 ms: unas 226 veces más rápido que escribirlo byte a byte. Por eso la lectura es gratis en comparación y la escritura se cuida.

Los cuatro bloques muestran las tres actitudes del programador con una EEPROM: escribir por páginas respetando sus límites, contar cuántas veces se escribe y suponer que la energía puede cortarse en el peor momento.

09 · Exploración interactiva

El laboratorio: celdas que se gastan

El simulador es una EEPROM de 16 bytes con páginas de 4 y una resistencia reducida (entre 5 y 60 ciclos por celda, en lugar de cien mil) para que el desgaste se vea. Cada celda tiene una barra de desgaste; al llegar al límite se «gasta» y deja de aceptar cambios. Podés escribir bytes y páginas (incluyendo la vuelta), simular un corte de energía durante una escritura y comparar un contador que siempre escribe en la dirección 0 con uno que rota entre las direcciones 8 a F.

EEPROM de 16 bytes, páginas de 4

Celdas de la EEPROM simulada con su desgaste Dieciséis celdas en dos filas. Cada una muestra su dirección, su contenido en hexadecimal y una barra de desgaste con los ciclos usados sobre el límite. Una celda gastada se marca con borde naranja.
Tiempo acumulado de escritura0 ms
Escrituras físicas0
Escrituras fallidas (celda gastada)0
Mayor desgaste—
Valor real del contador0
Contador ingenuo (dirección 0)—
Contador con rotación (última ranura)—

EEPROM nueva de 16 bytes con 20 ciclos de resistencia por celda. Escribí un byte o probá los contadores.

Últimas 8 operaciones desde el inicio de la simulación.
N.ºOperaciónDirecciónDatoResultado
Sin operaciones.

Recorrido guiado

  1. Con la dirección 0 y el dato A5, presioná «Escribir byte»: la celda toma 0xA5, su barra de desgaste sube 1/20 y el tiempo acumulado es 5 ms. «Leer byte» no cambia el desgaste: leer no gasta.
  2. Ponés la dirección 2 y los datos A0 A1 A2 A3 A4, y presioná «Escribir página»: la página 0 (direcciones 0 a 3) recibe A0 en la 2, A1 en la 3, y los dos bytes sobrantes (A2 y A3) dan la vuelta y caen en las direcciones 0 y 1, y A4 pisa de nuevo la 2. Termina con la 2 en A4, no en A0.
  3. Escribí una página de 4 bytes en la dirección 0 y mirá el tiempo: son 5 ms para los 4 bytes, no 20. Pero el desgaste se cuenta por cada celda escrita.
  4. Presioná «Escribir con corte de energía»: la celda queda con un valor al azar, ni el viejo ni el nuevo. Es el riesgo que se evita con dos copias y CRC.
  5. Reiniciá la simulación y presioná tres veces «Contador ingenuo ×10» (30 incrementos): la celda 0 llega a su límite de 20 ciclos y se marca como gastada. El contador real sigue subiendo, pero el valor en la dirección 0 se queda en 20.
  6. Reiniciá otra vez y presioná «Contador con rotación ×10» varias veces: cada incremento va a una ranura distinta (8 a F) y el desgaste se reparte. Resiste 160 incrementos (8 × 20) antes de gastarse alguna ranura.
  7. Subí la resistencia con el control deslizante a 60: el contador ingenuo ahora resiste 60 incrementos y el de rotación, 480. La rotación multiplica la vida por el número de ranuras.
  8. Presioná «Poner contenido en 0xFF»: los datos se borran pero el desgaste queda. Una EEPROM gastada no se recupera borrándola.
Desafío: el contador que solo escribe cuando cambia

Un sensor entrega una lectura cada segundo, pero casi siempre es la misma que la anterior. Si el programa lee el valor guardado y escribe solo cuando difiere, el desgaste depende de cuántas veces cambia el dato, no de cuántas veces se lee el sensor. Si el dato cambia, en promedio, una vez cada 10 minutos, una celda de 100 000 ciclos dura casi 2 años en lugar de 28 horas. Leer es gratis: conviene siempre.

10 · Comprensión

Confusiones frecuentes

«La EEPROM se puede escribir tantas veces como la RAM»

Cada celda soporta unos 105 a 106 escrituras. La RAM no tiene límite. Un dato que se actualiza muy seguido pertenece a la RAM.

«Leer una EEPROM también la desgasta»

No: leer no mueve carga. Solo las escrituras (borrar y programar) cuentan para el límite.

«Una escritura de página termina cuando se envía el último byte»

Termina cuando acaba el ciclo interno, unos milisegundos después de la parada o del último byte. Hay que esperar o sondear antes del próximo acceso.

«Si escribo más bytes que los de la página, pasan a la siguiente»

Dan la vuelta dentro de la misma página y pisan sus primeros bytes. El programa debe partir la escritura en tramos.

«El dato queda a salvo aunque se corte la energía en la escritura»

Esa celda (o varias, si es una página) queda en un valor indefinido. Se evita con dos copias alternadas y un CRC.

«Borrar la EEPROM le devuelve la vida útil»

El desgaste del óxido es acumulativo e irreversible. Borrar es otra escritura y gasta un ciclo más.

«Los 100 años de retención valen siempre»

Disminuyen con la temperatura y con la cantidad de ciclos acumulados. Una EEPROM muy usada y caliente retiene menos que una nueva a temperatura ambiente.

«Basta con esperar el máximo del datasheet para todas las escrituras»

Funciona, pero desperdicia tiempo; el polling termina apenas la memoria está lista. Lo que no funciona es esperar menos del mínimo especificado.

11 · Práctica guiada

Predecí antes de simular

1. Vida útil

Una EEPROM de 1 000 000 de ciclos guarda un contador que se reescribe cada 10 segundos en la misma posición. ¿Cuánto dura la celda?

Ver solución

1 000 000 · 10 s = 10 000 000 s ≈ 115,7 días (casi 4 meses). Un contador así necesita rotación o escribir menos seguido.

2. Con rotación

Con 100 000 ciclos por celda, un dato se escribe una vez por minuto, repartido entre 32 posiciones. ¿Cuánto dura la memoria?

Ver solución

100 000 · 32 = 3 200 000 escrituras · 60 s = 192 000 000 s ≈ 6,1 años. Sin rotación serían solo 69 días.

3. Vuelta de página

Una EEPROM tiene páginas de 32 bytes. Se envían 5 bytes a partir de la dirección 0x1E. ¿En qué direcciones quedan?

Ver solución

La página va de 0x00 a 0x1F. Desde 0x1E caben 2 bytes (0x1E y 0x1F); los 3 restantes dan la vuelta: 0x1E, 0x1F, 0x00, 0x01, 0x02. Los tres últimos pisan el comienzo de la misma página.

4. Bytes o páginas

Se deben escribir 256 bytes en una EEPROM con tWC = 5 ms. ¿Cuánto tiempo de espera hay escribiendo byte a byte y con páginas de 32 bytes (sin contar el tiempo del bus)?

Ver solución

Byte a byte: 256 · 5 ms = 1,28 s. Con páginas de 32: 256 / 32 = 8 páginas · 5 ms = 40 ms. Es 32 veces más rápido.

5. Dos copias

Las dos ranuras de un almacén tienen números de secuencia 7 y 8, ambas con CRC correcto. Se empieza a guardar un dato nuevo y se corta la energía a mitad de la escritura. ¿En qué ranura se escribía y qué valor se recupera?

Ver solución razonada

Se escribe en la ranura más vieja (secuencia 7), que pasaría a tener la 9. Con el corte, esa ranura queda con un CRC inválido y se descarta. Se recupera el dato de la ranura con secuencia 8, la última completa. Se pierde la actualización interrumpida, pero no se lee basura.

6. Diagnóstico

Un termómetro guarda la temperatura máxima en una EEPROM cada vez que lee el sensor (cada segundo), y a los dos días empieza a mostrar valores erróneos. ¿Qué pasó y cómo se corrige?

Ver solución razonada

Con 86 400 lecturas por día y 100 000 ciclos, la celda se gasta en poco más de un día. Se corrige guardando solo cuando el máximo cambia (se compara con el valor guardado, que se lee gratis), manteniendo el valor de trabajo en RAM y copiándolo a la EEPROM cada tanto o al detectar la caída de tensión, y, si hiciera falta, rotando entre varias posiciones.

12 · Síntesis

Ideas para recordar

  • La EEPROM es una memoria no volátil que se borra y escribe eléctricamente, byte a byte y en el circuito, con una celda de compuerta flotante y efecto túnel; el chip genera la tensión alta con una bomba de carga.
  • Leer es rápido y no desgasta; escribir toma milisegundos (borrar y programar) y consume un ciclo de la celda.
  • La escritura por páginas escribe varios bytes en un solo ciclo interno; los bytes sobrantes dan la vuelta dentro de la página y pisan su comienzo.
  • Hay que esperar el fin de la escritura, con un tiempo fijo o con polling (DATA polling, toggle bit o ACK polling en I²C).
  • Retención típica: 10 a 100 años; resistencia típica: 105 a 106 ciclos por byte. Escribir siempre en el mismo byte lo agota en horas o días.
  • Se alarga la vida escribiendo solo si el dato cambió, guardando en RAM lo que cambia seguido y repartiendo las escrituras entre muchas posiciones (wear leveling).
  • Para sobrevivir a un corte de energía: dos copias alternadas con número de secuencia y CRC, detección de caída de tensión y verificación por lectura.
  • La EEPROM sirve para pocos datos que cambian poco (configuración, calibración); para mucho volumen, la Flash; para escrituras muy frecuentes, FRAM o RAM con batería.

¿Listo para el tema 30?

Antes de avanzar, explicá de memoria qué hace distinta a la EEPROM de la EPROM, qué ocurre si una escritura de página cruza su límite y cómo se hace un guardado seguro frente a cortes de energía. Si podés justificar las tres respuestas, ya podés pasar a la memoria que usan casi todos los equipos para guardar su programa.

En el próximo tema veremos la memoria Flash: páginas, sectores y borrado por bloques, el desgaste y cómo se reparte entre bloques, y por qué escribir en ella exige estar seguro de lo que ya contiene.