Módulo 1

Memoria del ESP32: SRAM, Flash, PSRAM

Gestión y Arquitectura de Memoria en Sistemas Embebidos

ESP32 Memoria Mecatrónica UNAM

Introducción a la Arquitectura de Memoria

El ESP32 es un SoC (System on a Chip) diseñado para aplicaciones de Internet de las Cosas (IoT) y sistemas embebidos. Como evolución del ESP8266, incrementa significativamente la capacidad de procesamiento y añade conectividad Bluetooth. Su arquitectura de memoria está cuidadosamente diseñada para optimizar el rendimiento en aplicaciones mecatrónicas.

Tipos de Memoria en el ESP32

La gestión eficiente de memoria es fundamental en mecatrónica e IoT, donde los recursos suelen ser limitados. El ESP32 incorpora tres tipos principales de memoria, cada una con características específicas para diferentes aplicaciones:

Tipos de Memoria del ESP32

SRAM

Static Random-Access Memory - Volátil y rápida

Capacidad: 520 KB
Tipo: Volátil
Uso: Variables runtime
Velocidad: Muy rápida

Flash

Memoria no volátil para código y datos persistentes

Capacidad: 4-16 MB
Tipo: No volátil
Uso: Código + datos
Velocidad: Moderada

PSRAM

Pseudo-Static RAM para expansión de memoria

Capacidad: Hasta 16 MB
Tipo: Volátil
Uso: Datos grandes
Velocidad: Rápida

Mapas de Memoria y Especificaciones

El ESP32 utiliza un mapa de memoria complejo que permite optimizar el acceso a diferentes tipos de memoria según las necesidades de cada aplicación mecatrónica.

Distribución de Memoria

  • DRAM (Data RAM) 328 KB
  • IRAM (Instruction RAM) 192 KB
  • ROM 448 KB
  • RTC Fast Memory 8 KB
  • RTC Slow Memory 8 KB
  • Flash SPI 4-16 MB
  • PSRAM SPI 0-16 MB
  • Cache 32 KB I/D

Gestión de Memoria con Atributos

El ESP32 permite usar directivas especiales para controlar dónde se almacenan las variables y funciones. Esto es crucial para optimizar el rendimiento en aplicaciones mecatrónicas.

C++ - Arduino IDE
// Demostración de gestión de memoria en ESP32
#include 
#include 
#include 

// Variables en diferentes tipos de memoria
DRAM_ATTR int contador_dram = 0;           // Variable en DRAM
IRAM_ATTR int contador_iram = 0;           // Variable en IRAM
RTC_DATA_ATTR int contador_rtc = 0;        // Variable en RTC (persiste en deep sleep)

// Arrays grandes en PSRAM (si está disponible)
MALLOC_CAP_SPIRAM int* array_psram = nullptr;

// Función crítica en IRAM (para interrupciones)
IRAM_ATTR void funcion_critica() {
    contador_iram++;
    // Esta función se ejecuta desde IRAM para máximo rendimiento
    digitalWrite(2, !digitalRead(2));
}

// Buffer estático en DRAM
DRAM_ATTR static uint8_t buffer_sensor[1024];

void setup() {
    Serial.begin(115200);
    delay(1000);
    
    Serial.println("\n=== ESP32 Memory Management Demo ===");
    
    // Mostrar información de memoria disponible
    mostrarInfoMemoria();
    
    // Intentar asignar memoria en PSRAM
    if (esp_spiram_is_initialized()) {
        Serial.println("PSRAM detectada - Asignando memoria...");
        array_psram = (int*)heap_caps_malloc(10000 * sizeof(int), MALLOC_CAP_SPIRAM);
        
        if (array_psram != nullptr) {
            Serial.println("Memoria PSRAM asignada exitosamente");
            // Llenar array con datos de prueba
            for (int i = 0; i < 10000; i++) {
                array_psram[i] = i * 2;
            }
        } else {
            Serial.println("Error: No se pudo asignar memoria PSRAM");
        }
    } else {
        Serial.println("PSRAM no disponible en este ESP32");
    }
    
    // Configurar pin 2 como salida para la función crítica
    pinMode(2, OUTPUT);
    
    // Configurar timer para función crítica cada 1ms
    hw_timer_t* timer = timerBegin(0, 80, true); // 80MHz/80 = 1MHz
    timerAttachInterrupt(timer, &funcion_critica, true);
    timerAlarmWrite(timer, 1000, true); // 1000 us = 1ms
    timerAlarmEnable(timer);
    
    Serial.println("Sistema inicializado - Monitor de memoria activo");
}

void loop() {
    contador_dram++;
    contador_rtc++; // Se mantiene en deep sleep
    
    // Mostrar estadísticas cada 5 segundos
    if (contador_dram % 50 == 0) {
        Serial.println("\n--- Estadísticas de Memoria ---");
        Serial.printf("Contador DRAM: %d\n", contador_dram);
        Serial.printf("Contador IRAM: %d\n", contador_iram);
        Serial.printf("Contador RTC: %d\n", contador_rtc);
        
        mostrarInfoMemoria();
        
        // Test de velocidad de acceso
        testVelocidadMemoria();
    }
    
    // Simular procesamiento de sensor
    procesarDatosSensor();
    
    delay(100);
}

void mostrarInfoMemoria() {
    Serial.println("\n--- Información de Memoria ---");
    Serial.printf("Free Heap: %d bytes\n", esp_get_free_heap_size());
    Serial.printf("Min Free Heap: %d bytes\n", esp_get_minimum_free_heap_size());
    Serial.printf("Free Internal: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
    Serial.printf("Free SPIRAM: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
    Serial.printf("Free DMA: %d bytes\n", heap_caps_get_free_size(MALLOC_CAP_DMA));
}

void testVelocidadMemoria() {
    const int TEST_SIZE = 1000;
    unsigned long tiempo_inicio, tiempo_fin;
    
    // Test DRAM
    tiempo_inicio = micros();
    for (int i = 0; i < TEST_SIZE; i++) {
        buffer_sensor[i] = i % 256;
    }
    tiempo_fin = micros();
    Serial.printf("Velocidad DRAM: %lu us para %d bytes\n", 
                  tiempo_fin - tiempo_inicio, TEST_SIZE);
    
    // Test PSRAM (si está disponible)
    if (array_psram != nullptr) {
        tiempo_inicio = micros();
        for (int i = 0; i < TEST_SIZE; i++) {
            array_psram[i] = i;
        }
        tiempo_fin = micros();
        Serial.printf("Velocidad PSRAM: %lu us para %d enteros\n", 
                      tiempo_fin - tiempo_inicio, TEST_SIZE);
    }
}

void procesarDatosSensor() {
    // Simular lectura de múltiples sensores
    static uint16_t indice_buffer = 0;
    
    // Almacenar datos en buffer DRAM
    buffer_sensor[indice_buffer] = analogRead(A0) >> 4; // 8 bits
    indice_buffer = (indice_buffer + 1) % sizeof(buffer_sensor);
    
    // Si tenemos PSRAM, almacenar histórico
    if (array_psram != nullptr) {
        static int indice_historico = 0;
        array_psram[indice_historico] = millis();
        indice_historico = (indice_historico + 1) % 10000;
    }
}

Ejercicios Prácticos

1

Monitor de Memoria SRAM

Principiante 20 min

Objetivo: Crear un monitor que muestre el uso de SRAM en tiempo real.

Materiales:

  • ESP32 DevKit
  • Monitor serie
  • Arduino IDE

Conceptos: Heap memory, stack, fragmentación

2

Almacenamiento en Flash SPIFFS

Intermedio 35 min

Objetivo: Implementar sistema de almacenamiento persistente usando SPIFFS.

Aplicación: Guardar configuraciones de sensores y logs de eventos

Conceptos: SPIFFS, NVS, particiones de memoria Flash

3

Optimización con PSRAM

Avanzado 50 min

Objetivo: Utilizar PSRAM para procesamiento de grandes volúmenes de datos de sensores.

Aplicación: Buffer circular para datos de acelerómetro con FFT

Análisis: Comparativa de rendimiento SRAM vs PSRAM

Aplicaciones de Gestión de Memoria en Mecatrónica

Estrategias de Memoria para Sistemas Mecatrónicos

La gestión inteligente de memoria en el ESP32 permite implementar sistemas mecatrónicos complejos y eficientes:

SRAM (Runtime):
  • Variables de control PID
  • Buffers de comunicación
  • Estados de máquina en tiempo real
Flash (Persistente):
  • Configuraciones de sistema
  • Logs de eventos y calibraciones
  • Firmware y aplicaciones
Casos de Uso Específicos
Control Industrial
SRAM para control, Flash para recetas de producción
Robótica Móvil
PSRAM para mapas, SRAM para navegación
Adquisición de Datos
Buffers grandes en PSRAM para muestreo continuo

Referencias y Recursos Adicionales