Módulo 1

Arquitectura del ESP32: Procesador Xtensa dual core

Introducción al ESP32 y su Ecosistema

ESP32 Mecatrónica IoT UNAM

Arquitectura del Procesador Xtensa LX6

El ESP32 incorpora un procesador dual-core Xtensa LX6 de 32 bits, desarrollado por Tensilica (ahora parte de Cadence). Esta arquitectura es especialmente diseñada para aplicaciones de bajo consumo energético y alta performance, convirtiéndolo en la elección ideal para sistemas mecatrónicos e IoT.

Núcleo PRO_CPU (Protocolo)
  • Núcleo principal de aplicaciones
  • Maneja tareas complejas
  • Frecuencia hasta 240 MHz
  • Cache de instrucciones y datos
Núcleo APP_CPU (Aplicación)
  • Núcleo secundario para tareas
  • Procesamiento en paralelo
  • Optimización de rendimiento
  • Multitasking con FreeRTOS

Especificaciones del Procesador

La arquitectura Xtensa LX6 proporciona un rendimiento excepcional para aplicaciones embebidas, con características avanzadas que permiten ejecutar múltiples tareas de forma eficiente.

Características del Procesador

  • Arquitectura 32-bit RISC
  • Núcleos Dual-core
  • Freq. Máxima 240 MHz
  • Cache L1 32 KB I/D
  • SRAM 520 KB
  • ROM 448 KB
  • RTC Fast Memory 8 KB
  • RTC Slow Memory 8 KB

Programación Dual-Core

Una de las ventajas principales del ESP32 es su capacidad de procesamiento paralelo. A continuación, un ejemplo que demuestra cómo utilizar ambos núcleos de manera eficiente:

C++ - Arduino IDE
// Demostración de programación dual-core en ESP32
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// Variables globales para comunicación entre núcleos
volatile int contador_core0 = 0;
volatile int contador_core1 = 0;
const int LED_PIN = 2;

// Tarea para el núcleo 0 (PRO_CPU)
void tarea_core0(void *parametro) {
  Serial.println("Tarea iniciada en Core 0 (PRO_CPU)");
  
  while(1) {
    contador_core0++;
    
    // Mostrar información cada 1000 iteraciones
    if (contador_core0 % 1000 == 0) {
      Serial.printf("Core 0: %d iteraciones\n", contador_core0);
      Serial.printf("Frecuencia CPU: %d MHz\n", getCpuFrequencyMhz());
    }
    
    // Control del LED
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    
    vTaskDelay(500 / portTICK_PERIOD_MS); // 500ms delay
  }
}

// Tarea para el núcleo 1 (APP_CPU)
void tarea_core1(void *parametro) {
  Serial.println("Tarea iniciada en Core 1 (APP_CPU)");
  
  while(1) {
    contador_core1++;
    
    // Procesamiento matemático intensivo
    float resultado = 0;
    for(int i = 0; i < 10000; i++) {
      resultado += sin(i) * cos(i);
    }
    
    // Mostrar información cada 100 iteraciones
    if (contador_core1 % 100 == 0) {
      Serial.printf("Core 1: %d cálculos completados\n", contador_core1);
      Serial.printf("Resultado matemático: %.2f\n", resultado);
    }
    
    vTaskDelay(1000 / portTICK_PERIOD_MS); // 1000ms delay
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  
  delay(2000);
  Serial.println("\n=== ESP32 Dual-Core Demo ===");
  Serial.printf("Chip: %s\n", ESP.getChipModel());
  Serial.printf("Núcleos disponibles: %d\n", ESP.getChipCores());
  Serial.printf("Frecuencia actual: %d MHz\n", getCpuFrequencyMhz());
  
  // Crear tarea en Core 0 (núcleo 0)
  xTaskCreatePinnedToCore(
    tarea_core0,    // Función de la tarea
    "TareaCore0",   // Nombre de la tarea
    10000,          // Tamaño del stack
    NULL,           // Parámetros
    1,              // Prioridad
    NULL,           // Handle de la tarea
    0               // Núcleo asignado (0)
  );
  
  // Crear tarea en Core 1 (núcleo 1)
  xTaskCreatePinnedToCore(
    tarea_core1,    // Función de la tarea
    "TareaCore1",   // Nombre de la tarea
    10000,          // Tamaño del stack
    NULL,           // Parámetros
    1,              // Prioridad
    NULL,           // Handle de la tarea
    1               // Núcleo asignado (1)
  );
  
  Serial.println("Tareas creadas en ambos núcleos");
}

void loop() {
  // El loop principal puede estar vacío cuando usamos tareas FreeRTOS
  // o realizar tareas de monitoreo
  Serial.printf("Sistema funcionando - Core 0: %d, Core 1: %d\n", 
                contador_core0, contador_core1);
  delay(5000);
}

Ejercicios Prácticos

1

Medición de Frecuencia del Procesador

Principiante 15 min

Objetivo: Obtener información detallada sobre el procesador ESP32 y sus características.

Materiales:

  • ESP32 DevKit
  • Cable USB
  • PC con Arduino IDE

Resultados esperados: Mostrar información del chip, frecuencia, memoria disponible.

2

Tareas en Dual-Core

Intermedio 30 min

Objetivo: Implementar tareas separadas en cada núcleo del ESP32 usando FreeRTOS.

Conceptos: Multitasking, asignación de núcleos, comunicación entre tareas

Aplicación: Una tarea controla LEDs, otra procesa sensores

3

Benchmark de Rendimiento

Avanzado 45 min

Objetivo: Comparar el rendimiento entre procesamiento single-core vs dual-core.

Métrica: Operaciones matemáticas por segundo, tiempo de respuesta

Análisis: Gráficas de performance y optimización de código

Aplicaciones del Dual-Core en Mecatrónica

Ventajas del Procesamiento Paralelo

En sistemas mecatrónicos complejos, el dual-core del ESP32 permite separar tareas críticas y optimizar el rendimiento:

Núcleo 0 (PRO_CPU):
  • Comunicaciones WiFi/Bluetooth
  • Gestión de datos y memoria
  • Tareas del sistema operativo
Núcleo 1 (APP_CPU):
  • Control de actuadores
  • Procesamiento de sensores
  • Tareas de tiempo real
Casos de Uso Prácticos
Robot Autónomo
Un core para navegación, otro para sensores
Control Industrial
Monitoreo y control simultáneo
Sistemas Vehiculares
Telemetría y control de motor

Referencias y Recursos Adicionales