01 · Punto de partida
Tres memorias, tres papeles
En el curso anterior comparaste RAM, ROM, EEPROM y Flash como componentes sueltos, con sus buses y sus señales de control. Un microcontrolador las integra y las usa a las tres, cada una para algo distinto. En el tema 3 las vimos en el diagrama de bloques; ahora las estudiamos con la profundidad que necesita quien programa.
Para un programador de escritorio, la memoria es una sola cosa: un espacio donde reservar variables. Aquí, dónde se guarda un dato determina cuánto dura, cuánto tarda en escribirse y cuántas veces se puede cambiar. Un error de elección no siempre da un fallo inmediato: a veces el programa funciona semanas y después se degrada.
Retomamos el curso de circuitos secuenciales y memoria (EEPROM y Flash), el mapa de memoria del tema 3 y la salida de avr-size del tema 5.
02 · Panorama
Comparación general
| Característica | Flash | SRAM | EEPROM |
|---|---|---|---|
| Tamaño | 32 KB | 2 KB | 1 KB |
| ¿Se conserva sin energía? | Sí | No | Sí |
| Lectura | A la velocidad de la CPU | 2 ciclos (LD/ST) | Rápida, con registros de control |
| Escritura | Por páginas de 128 bytes, con borrado previo | Inmediata, byte a byte | Por byte, unos milisegundos |
| Vida útil | 10 000 ciclos de borrado/escritura | Ilimitada | 100 000 ciclos por celda |
| Retención de datos | 20 años a 85 °C | Mientras haya alimentación | 20 años a 85 °C |
| Qué guarda | Programa y constantes | Variables, pila, heap | Configuración y datos persistentes |
La regla de oro: el programa y los datos que no cambian van en Flash; los datos que cambian todo el tiempo van en SRAM; los datos que cambian de vez en cuando y deben sobrevivir al apagado van en EEPROM.
03 · El programa
La memoria Flash
La Flash guarda las instrucciones que ejecuta la CPU. Está organizada en páginas de 128 bytes (64 palabras) y tiene una restricción física fundamental: borrar pone todos los bits de una página en 1, y escribir solo puede pasar bits de 1 a 0. Para cambiar un byte de 0 a 1 hay que borrar la página completa y reescribirla.
De ahí salen varias consecuencias:
- La Flash tolera unos 10 000 ciclos de borrado y escritura. Alcanza de sobra para grabar el programa muchas veces, pero no para guardar un dato que cambia continuamente.
- Se lee a la velocidad de la CPU, porque de allí salen las instrucciones. En un AVR, leer una constante de la Flash exige instrucciones especiales (
LPM), porque su espacio de direcciones es distinto del de la SRAM. - El programa puede escribir su propia Flash mediante la instrucción
SPM, pero solo desde la sección de arranque (boot section). Es el mecanismo del cargador de arranque del tema 5.
El tamaño de la Flash se reparte entre el cargador de arranque (opcional, de 0,5 a 4 KB según se configure), el código de inicio, tu programa, las rutinas de biblioteca y las constantes. Hay un costo oculto: una variable global inicializada ocupa Flash y SRAM. El valor inicial se guarda en la Flash y el código de inicio lo copia a la SRAM antes de llamar a main().
04 · Los datos vivos
La SRAM: variables, heap y pila
La SRAM es memoria de acceso rápido y escritura libre, pero volátil: se pierde al cortar la energía. En el ATmega328P ocupa las direcciones 0x0100 a 0x08FF y se reparte, de abajo hacia arriba, en cuatro regiones:
.data: variables globales y estáticas con valor inicial (uint8_t contador = 5;). Su valor inicial viaja en la Flash..bss: globales sin valor inicial; el código de inicio las pone en cero.- Heap: memoria dinámica (
malloc,new). Crece hacia direcciones mayores. - Pila: direcciones de retorno, variables locales y registros guardados. Empieza en
RAMEND(0x08FF) y crece hacia abajo.
El enlazador sabe cuánto ocupan .data y .bss (es lo que informa avr-size como «Data»). El heap y la pila, en cambio, dependen de lo que ocurra al ejecutar: el compilador no puede decir cuánta pila usará un programa con interrupciones y llamadas anidadas. Por eso se diseña con margen.
05 · Los datos que sobreviven
La EEPROM
La EEPROM guarda pocos datos que deben persistir: calibraciones, configuración elegida por el usuario, un contador de horas de uso, el último estado antes de un apagado. Se lee con rapidez y se escribe byte por byte, pero una escritura tarda unos milisegundos (alrededor de 3,4 ms según la hoja de datos) y gasta vida útil de esa celda: 100 000 ciclos.
Cien mil escrituras parecen muchas, pero un programa descuidado las gasta rápido. Si un contador se guarda una vez por segundo en el mismo byte, la celda llega al límite en 100 000 s, es decir, unas 28 horas. Las estrategias para alargar la vida útil son tres:
- Escribir solo cuando el valor cambia. La función
eeprom_update_byte()lee primero y solo escribe si el dato es distinto. - Escribir menos seguido. Guardar al apagar (con un detector de falla de alimentación) en lugar de cada vuelta del bucle.
- Repartir el desgaste (wear leveling): rotar la posición de escritura entre varias celdas, de modo que cada una recibe una fracción de las escrituras.
Si se corta la energía durante una escritura
Una escritura de unos milisegundos puede quedar a medias si la tensión cae. Un byte corrupto es el peor tipo de fallo porque parece un dato válido. Dos defensas habituales: guardar un código de verificación (suma de comprobación) junto con el dato, y descartar los datos cuya verificación falla; y mantener dos copias alternadas, de modo que siempre exista una anterior íntegra. El detector de caída de tensión (brown-out), que veremos con el reset, evita que el chip siga operando con una tensión insuficiente.
06 · Decidir
Dónde va cada dato
| Dato | Memoria | Motivo |
|---|---|---|
| El código del programa | Flash | No cambia durante la ejecución y debe sobrevivir al apagado |
| Tabla de constantes (senos, colores, caracteres de una pantalla) | Flash, con PROGMEM | No cambia; en SRAM gastaría memoria escasa |
| Textos de mensajes | Flash | Pueden ocupar cientos de bytes que la SRAM no tiene |
| Contadores, estados, buffers de lectura | SRAM | Cambian todo el tiempo; no se guardan al apagar |
| Variables locales y retornos de función | SRAM (pila) | Existen mientras la función se ejecuta |
| Calibración del sensor, configuración del usuario | EEPROM | Cambia rara vez y debe sobrevivir al apagado |
| Registro histórico de muchos datos | EEPROM o memoria externa | Si el volumen o la frecuencia superan lo permitido, se agrega un chip de memoria |
Una pregunta práctica sirve para decidir: ¿cuánto cambia y cuánto debe durar? Si cambia siempre y puede perderse, SRAM. Si no cambia nunca, Flash. Si cambia a veces y no puede perderse, EEPROM.
07 · En el código
Declarar cada memoria en C
Con avr-libc se indica en la declaración dónde vive cada dato:
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
const uint8_t seno[] PROGMEM = {0, 25, 49, 71, 90, 106, 117, 124, 127}; // tabla en Flash
uint8_t EEMEM calibracion; // variable en EEPROM
uint8_t contador = 5; // .data: ocupa SRAM y también Flash (valor inicial)
uint8_t lecturas[64]; // .bss: SRAM, inicia en cero
int main(void) {
uint8_t v = pgm_read_byte(&seno[3]); // leer una constante de la Flash
eeprom_update_byte(&calibracion, v); // escribir en EEPROM solo si cambió
uint8_t c = eeprom_read_byte(&calibracion); // leer de la EEPROM
while (1) { lecturas[0] = c + contador; }
}
PROGMEMcoloca la tabla en la Flash. Para leerla se usapgm_read_byte()(opgm_read_word()), que genera la instrucciónLPM. Si leés una variable declarada conPROGMEMcomo si fuera normal, obtenés basura.EEMEMasigna una posición de la EEPROM. Se accede con funciones, no directamente.- Una constante declarada solo con
constno se mueve a la Flash en un AVR: sigue copiándose a la SRAM. Es una particularidad de esta arquitectura Harvard; en la mayoría de los ARM,constalcanza.
El efecto en el presupuesto es grande. Una tabla de 512 bytes en SRAM consume la cuarta parte de los 2 KB; en Flash cuesta lo mismo que en SRAM, pero de una memoria 16 veces más grande.
08 · Analogía con programación
Modelar las reglas físicas de la Flash y la EEPROM
Dos clases de Python reproducen las reglas esenciales. La primera modela la Flash: programar es un AND, y solo el borrado de página devuelve los bits a 1. La segunda cuenta el desgaste de la EEPROM y compara escribir con actualizar, que es lo que hace eeprom_update_byte().
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
class Flash:
"""Memoria Flash: borrar pone todo en 1; programar solo puede pasar bits de 1 a 0."""
PAGINA = 128
def __init__(self, paginas=2):
self.mem = bytearray([0xFF] * (paginas * self.PAGINA))
self.borrados = 0
def borrar_pagina(self, pagina):
inicio = pagina * self.PAGINA
self.mem[inicio:inicio + self.PAGINA] = bytes([0xFF] * self.PAGINA)
self.borrados += 1
def programar(self, direccion, valor):
self.mem[direccion] &= valor # un AND: solo se apagan bits
flash = Flash()
flash.programar(0, 0xA5)
print(f"Escribo 0xA5 sobre 0xFF -> {flash.mem[0]:#04x}")
flash.programar(0, 0x5A) # sin borrar antes
print(f"Escribo 0x5A sin borrar -> {flash.mem[0]:#04x} (0xA5 AND 0x5A)")
flash.borrar_pagina(0)
flash.programar(0, 0x5A)
print(f"Borro la página y escribo -> {flash.mem[0]:#04x} (borrados: {flash.borrados})")
class Eeprom:
"""EEPROM: cada escritura real gasta ciclos de vida de esa celda."""
def __init__(self, tam=8):
self.mem = bytearray([0xFF] * tam)
self.desgaste = [0] * tam
def escribir(self, direccion, valor):
self.mem[direccion] = valor
self.desgaste[direccion] += 1
def actualizar(self, direccion, valor): # como eeprom_update_byte()
if self.mem[direccion] != valor:
self.escribir(direccion, valor)
a, b = Eeprom(), Eeprom()
for hora in range(1000):
temperatura = 21 + (hora // 100) # solo cambia 9 veces
a.escribir(0, temperatura)
b.actualizar(0, temperatura)
print(f"1000 lecturas -> write: {a.desgaste[0]} ciclos gastados, update: {b.desgaste[0]}")
print(f"Vida restante (de 100 000): write {100_000 - a.desgaste[0]}, update {100_000 - b.desgaste[0]}")
Resultado: escribir 0x5A sobre 0xA5 sin borrar da 0x00, no 0x5A; con borrado previo, sí. Y con 1000 lecturas de las que solo cambian 10, escribir gasta 1000 ciclos y actualizar solo 10. Probá hacer que la temperatura cambie en todas las lecturas y observá cómo desaparece la ventaja.
El modelo omite que en la Flash real se borra y escribe por páginas completas de 128 bytes, con un buffer intermedio, y que cada operación tarda milisegundos. Pero basta para entender por qué un contador no puede vivir en Flash: cada cambio exigiría borrar una página.
09 · Exploración interactiva
Desgaste y mapa de la SRAM
La primera herramienta calcula cuánto dura una celda que se reescribe periódicamente, con o sin update y repartiendo el desgaste. La segunda dibuja la SRAM del ATmega328P con los tamaños de cada región.
Banco de memoria
1. ¿Cuánto dura la memoria?
2. Mapa de la SRAM (2048 bytes)
El cálculo de vida útil supone que las escrituras se reparten por igual entre las posiciones y que la especificación de ciclos es un mínimo garantizado por celda, como indica el fabricante. No modela la corrupción por cortes de energía ni la temperatura.
Recorrido guiado
- Con los valores iniciales (EEPROM, una vez por segundo, una posición), la vida es de unas 28 horas. Cambiá la memoria a Flash: bajan a menos de 3 horas.
- Subí a 100 posiciones en rotación: la vida se multiplica por 100 (unos 116 días).
- Pasá a «cada 10 minutos» con una posición: unos 694 días. Con 10 posiciones, 19 años.
- Con 1 posición y un guardado por hora, bajá la proporción de cambios a 10 %: la vida pasa a más de un siglo, porque update casi nunca escribe.
- En el mapa de SRAM subí la pila a 1200 bytes: las regiones no caben y aparece el desborde.
Desafío: un registro de 1 Hz que dure 10 años
Querés guardar un dato nuevo cada segundo durante 10 años. Son unos 315 millones de escrituras. Con 100 000 ciclos por celda harían falta más de 3 000 posiciones en rotación, y la EEPROM de 1 KB solo ofrece 1024. Hay que cambiar de estrategia: guardar en SRAM y volcar a la EEPROM de a bloques, reducir la frecuencia, o usar una memoria externa (una Flash serie o una FRAM, que tolera muchísimos más ciclos).
10 · Método de trabajo
Presupuesto de memoria
- Listá los datos del proyecto y clasificalos con la pregunta de la sección 6: ¿cuánto cambia y cuánto debe durar?
- Sumá Flash y SRAM con la salida de
avr-size: Program contra 32 256 o 32 768 bytes; Data contra 2048. - Reservá la pila. Estimá la profundidad máxima de llamadas más las interrupciones y sumá un margen. Evitá recursión.
- Decidí qué mover a Flash. Tablas y textos con
PROGMEM. A menudo es la mayor ganancia de SRAM. - Calculá el desgaste de la EEPROM para cada dato que persiste y usá
updatey rotación cuando haga falta. - Medí la SRAM libre en ejecución. Una técnica conocida es llamar a una función que calcula la distancia entre el final del heap y la cima de la pila. Se puede hacer en distintos puntos del programa.
Con el multímetro y el osciloscopio no se ve la memoria, pero sí sus efectos: un reinicio inexplicable cada tanto o valores que se corrompen suelen indicar que la pila invadió el heap. Y una tensión de alimentación inestable durante una escritura en EEPROM explica datos persistentes dañados.
11 · Comprensión
Confusiones frecuentes
«Si la Flash guarda el programa, puedo guardar ahí mis variables»
Puede escribirse desde el programa solo en condiciones muy particulares y con una vida de 10 000 ciclos. Las variables se reservan en SRAM; lo que va a Flash son constantes y el código.
«const alcanza para ahorrar SRAM en un AVR»
No: en AVR el compilador copia las constantes a la SRAM al arrancar. Hay que usar PROGMEM y leer con las funciones pgm_read_*.
«La EEPROM se puede escribir en cada vuelta del bucle»
Cada escritura gasta vida útil y bloquea unos milisegundos. A 100 escrituras por segundo, una celda dura unos 17 minutos. Se escribe solo cuando el dato cambia y con la mínima frecuencia posible.
«Si compila y entra, la memoria está bien»
El compilador no sabe cuánta pila usará el programa ni cuánto crecerá el heap. Un programa puede compilar sin errores y fallar en ejecución por falta de pila.
«La SRAM conserva su contenido tras un reset»
No hay garantía. Aunque a veces los valores sobreviven a un reinicio breve, no se puede confiar en ello. Todo lo que debe persistir va a EEPROM.
12 · Práctica guiada
Predecí antes de comprobar
1. ¿Qué memoria?
Elegí Flash, SRAM o EEPROM para: (a) el número de serie del producto grabado en fábrica, (b) la temperatura actual, (c) una tabla de 256 valores de un seno, (d) la última configuración elegida por el usuario.
Ver solución razonada
(a) Flash (o EEPROM si lo escribe el programa de fabricación): nunca cambia. (b) SRAM: cambia constantemente. (c) Flash con PROGMEM: es constante y de 256 bytes. (d) EEPROM: cambia rara vez y debe sobrevivir al apagado.
2. Vida de la EEPROM
Un dispositivo guarda su estado una vez cada 10 minutos en una sola posición. ¿Cuánto dura la celda? ¿Y si rota entre 10 posiciones?
Ver solución
144 escrituras por día. Con una posición: 100 000 / 144 ≈ 694 días (unos 1,9 años). Con 10 posiciones: 6 940 días, casi 19 años.
3. Presupuesto de SRAM
Un programa tiene .data = 150, .bss = 600, sin heap, y una pila máxima estimada de 200 bytes. ¿Cuánta SRAM queda libre?
Ver solución
2048 − (150 + 600 + 0 + 200) = 1098 bytes libres (53,6 %). Es una situación cómoda.
4. Mover una tabla a Flash
Un programa tiene una tabla de 512 bytes en SRAM. ¿Qué porcentaje de la SRAM libera moverla a Flash con PROGMEM, y qué cambio hay que hacer en el código que la lee?
Ver solución
512 / 2048 = 25 % de la SRAM. Hay que declararla con PROGMEM y reemplazar cada acceso tabla[i] por pgm_read_byte(&tabla[i]).
5. Corte de energía
Un dispositivo guarda un contador de 16 bits en EEPROM. Se corta la alimentación después de escribir el primer byte y antes del segundo. ¿Qué problema aparece y cómo se evita?
Ver solución razonada
El contador queda con un byte nuevo y uno viejo: un valor que nunca existió. Se evita guardando junto con el dato una suma de verificación (y descartando datos con suma incorrecta) o alternando dos copias, de modo que siempre quede una íntegra. También ayuda detectar la caída de tensión antes de que sea irreversible.
13 · Síntesis
Ideas para recordar
- El ATmega328P tiene 32 KB de Flash, 2 KB de SRAM y 1 KB de EEPROM, cada una con un papel distinto.
- La Flash guarda programa y constantes; se borra por páginas y tolera unos 10 000 ciclos.
- En Flash, borrar pone bits en 1 y escribir solo los pasa a 0.
- La SRAM guarda .data, .bss, heap y pila; heap y pila crecen una hacia la otra y no hay aviso si chocan.
- Una global inicializada ocupa Flash y SRAM; una constante grande conviene en Flash con
PROGMEM. - La EEPROM guarda datos persistentes, con unos 100 000 ciclos por celda y escrituras de milisegundos.
- Para cuidar la EEPROM: escribir solo si cambia, escribir poco y rotar posiciones; proteger los datos con verificación.
¿Listo para el tema 9?
Antes de avanzar, decí en qué memoria pondrías una tabla de constantes, un contador de lecturas y una calibración; calculá la vida de una celda que se escribe una vez por minuto; y dibujá de memoria el mapa de la SRAM. Si podés justificar las tres respuestas, ya sabés dónde viven los datos.
En el próximo tema vamos a ver los registros y el mapa de memoria con más detalle: cómo se organizan los registros de los periféricos y cómo se leen en la hoja de datos.