DÍA 165 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Nacimiento del Procesador

Cuando energizas un procesador ARM Cortex-M4, su hardware interno (el núcleo de silicio) ejecuta una secuencia inamovible:

  1. Va a la dirección de memoria 0x08000000 y lee los primeros 4 bytes. Este valor lo carga en su registro MSP (Main Stack Pointer) para saber dónde está la RAM libre.
  2. Va a la dirección contigua 0x08000004 y lee los siguientes 4 bytes. Este valor es la dirección del Reset Handler (La primera función de tu código).
  3. Salta a esa función y empieza a ejecutar las instrucciones.

A esta lista de direcciones se le conoce como la Tabla de Vectores de Interrupción (Vector Table).

El Problema del Salto (Vector Table Relocation)

Si escribimos un programa Bootloader (que se guarda en 0x08000000) y nuestra verdadera Aplicación de Gasolinera la guardamos más adelante en 0x08004000, tenemos un problema. Cuando ocurra una interrupción del UART en la aplicación, el hardware ciegamente irá a buscar a la Tabla de Vectores del Bootloader en la dirección cero. ¡El sistema colapsará!

La magia del salto en Bare-Metal requiere el registro SCB->VTOR (Vector Table Offset Register). Justo antes de saltar a la aplicación, el Bootloader debe decirle al CPU: "A partir de este microsegundo, tu nueva tabla de vectores ya no está en el cero, está desplazada a 0x08004000". Luego, el Bootloader configura el Stack Pointer (MSP) de la aplicación, y finalmente realiza un salto ciego usando un puntero a función en C.

MAPA DE MEMORIA FLASH Y PROCESO DE SALTO (BOOTLOADER JUMP) Memoria Flash STM32 BOOTLOADER (16 KB) 0x0800 0000 (VTOR 1) Siempre se ejecuta primero ACTIVE APP (48 KB) 0x0800 4000 (VTOR 2) El Súper Bucle Industrial [0]: App Stack Pointer (RAM) [1]: App Reset_Handler OTA SLOT (64 KB) 0x0801 0000 Nuevos binarios (Día 147) Proceso de Salto C (Bootloader main) 1. ¿Hay actualización pendiente? Si sí: Borrar Sector 1-3, Mover código del Sec 4 al Sec 1. 2. Validar Existencia de Aplicación Activa if (((*(__IO uint32_t*)0x08004000) & 0x2FFE0000) == 0x20000000) 3. Asegurar Sistema y Apagar Interrupciones __disable_irq(); SysTick->CTRL = 0; 4. Reubicación Vectorial y Stack SCB->VTOR = 0x08004000; __set_MSP(app_sp); 5. Salto de Fe (Function Pointer Jump) app_entry(); // ¡Adios Bootloader, Hola Aplicación! El Program Counter (PC) entra al Sector 1

⚙️ Ejercicios Prácticos

Ejercicio 1: Escribiremos el código fuente exclusivo para el archivo main.c de nuestro proyecto "Bootloader". Este proyecto se compila y se quema de forma independiente a la Aplicación. Su única misión es verificar si la memoria en `0x08004000` tiene un programa válido (leyendo si su stack pointer apunta a la RAM válida 0x20000000). Si todo está correcto, paralizará sus propios periféricos y saltará hacia lo desconocido.


/**
 * @file bootloader_main.c
 * @brief Gestor de Arranque y Ejecutor de Salto (Vector Relocation)
 * @note  Este código se compila SOLO y se aloja en 0x08000000
 */

#include "stm32f401xe.h"
#include <stdint.h>

// Definimos la dirección donde empieza nuestra aplicación real
#define APPLICATION_ADDRESS     0x08004000

// Definimos un tipo de dato "Puntero a Función" que no recibe nada y no retorna nada.
// Representa la función `Reset_Handler()` de la aplicación de destino.
typedef void (*pFunction)(void);

/**
 * @brief Rutina crítica de transferencia de control.
 */
void Bootloader_JumpToApp(void) {
    
    // 1. OBTENER VECTORES DE LA APLICACIÓN
    // Leemos los primeros 4 bytes de la aplicación (El valor del Stack Pointer en RAM)
    uint32_t app_stack_pointer = *(__IO uint32_t*)APPLICATION_ADDRESS;
    
    // Leemos los siguientes 4 bytes (La dirección de memoria del Reset_Handler de la App)
    uint32_t app_reset_handler_addr = *(__IO uint32_t*)(APPLICATION_ADDRESS + 4);
    
    // 2. VALIDACIÓN DE INTEGRIDAD
    // En la familia STM32F4, la SRAM empieza en 0x20000000.
    // Usamos una máscara de bits para comprobar si el SP leído realmente apunta a la RAM.
    // Si la memoria Flash estuviera vacía (borrada), leería 0xFFFFFFFF, y no saltará.
    if ((app_stack_pointer & 0x2FFE0000) == 0x20000000) {
        
        // 3. AISLAMIENTO DEL SISTEMA (Desarme absoluto)
        // Deshabilitamos interrupciones globales. Si el Systick del Bootloader
        // interrumpe a la aplicación mientras arranca, generará un HardFault.
        __disable_irq();
        
        // Apagamos explícitamente el SysTick Timer
        SysTick->CTRL = 0;
        
        // 4. REUBICACIÓN DE TABLA DE VECTORES
        // Le indicamos al núcleo Cortex-M4 que las interrupciones ya no viven
        // en 0x00000000, sino en la dirección de la Aplicación.
        SCB->VTOR = APPLICATION_ADDRESS;
        
        // 5. INICIALIZAR EL PUNTERO DE PILA (STACK POINTER)
        // Forzamos al CPU a usar la memoria RAM que requiere la Aplicación
        __set_MSP(app_stack_pointer);
        
        // 6. EL SALTO DE FE
        // Convertimos la dirección leída en un puntero a función ejecutable en C
        pFunction app_entry = (pFunction)app_reset_handler_addr;
        
        // Llamamos a la función. A partir de esta línea, el Bootloader "muere"
        // y el microcontrolador le pertenece 100% a la Aplicación de Gasolinera.
        app_entry(); 
    }
    else {
        // La validación falló. No hay aplicación instalada o está corrupta.
        // Opcional: Encender LED rojo permanente o esperar actualización serial.
        while(1) {
            // Error fatal.
        }
    }
}

int main(void) {
    
    // 1. Inicialización ultra-rápida (Reloj básico)
    // El Bootloader no necesita la FPU ni arreglos complejos.
    
    // 2. ¿HAY UNA ACTUALIZACIÓN PENDIENTE (OTA)?
    // Aquí leeríamos el RTC Backup Register (BKP). Si dice "UPDATE_READY",
    // significa que la App en su ejecución normal descargó el archivo en el Sector 4.
    // if (RTC->BKP0R == 0xUPDATE) {
    //      Flash_Unlock();
    //      Flash_Erase_Sector(1); Flash_Erase_Sector(2); Flash_Erase_Sector(3);
    //      Flash_Copy_From_Sector4_To_Sector1();
    //      RTC->BKP0R = 0; // Limpiar bandera
    // }
    
    // 3. Ejecutar salto ciego
    Bootloader_JumpToApp();
    
    // Si la función anterior tiene éxito, el procesador JAMÁS llegará a esta línea.
    while(1) {}
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Inmortalidad Evolutiva (Zero-Touch Maintenance).

El mayor terror de un fabricante industrial es un "Brick". Si vendes 2,000 máquinas despachadoras en 2,000 gasolineras a nivel nacional, y descubres un bug en el cálculo del precio, necesitas actualizar el firmware. Si durante la actualización remota (OTA) hay un apagón eléctrico, la memoria queda borrada a la mitad. La máquina amanecería muerta (brickeada), obligándote a enviar 2,000 técnicos en camionetas por todo el país solo para reconectar el programador USB ST-LINK.

Al dotar a tu placa de un **Bootloader Bare-Metal Aislado**, eliminas el riesgo. El Bootloader vive en el inamovible Sector 0. Nunca se borra, nunca se sobreescribe. Si hay un apagón mientras actualizas la "App Activa", al regresar la luz el procesador entra al Bootloader, valida la integridad, descubre que la aplicación está corrupta, y se niega a saltar, entrando en modo de rescate seguro. Has convertido a tu placa de circuito en un servidor tolerante a fallas que puede evolucionar por décadas sin intervención humana física.

📝 Resumen del Día