DÍA 257 DE 365

Fase 5: Diseño Industrial y Proyecto Final

📖 Teoría a Estudiar

El Secreto del Bit Cero (El Estado Thumb)

Los procesadores de la familia ARM Cortex-M (como tu STM32F401) no entienden el antiguo conjunto de instrucciones ARM de 32 bits. Fueron diseñados para ser ultra eficientes, por lo que utilizan exclusivamente el conjunto de instrucciones comprimidas llamado Thumb-2.

Cuando el procesador realiza un salto (Jump/Branch) a una dirección de memoria, examina el Bit Menos Significativo (LSB - Bit 0) de esa dirección:

Afortunadamente, el Linker de C (el programa que compila tu código) es inteligente. Cuando compila el archivo principal de tu Despachador (la Aplicación), busca la dirección real del Reset_Handler() y, antes de guardarla en la tabla de vectores en 0x08010004, le suma 1 automáticamente.

Por lo tanto, tu Bootloader no debe modificar ese valor. Simplemente debe leer los 4 bytes almacenados en 0x08010004, castearlos ciegamente a un puntero de función y ejecutarlos.

Desarmando la Bomba: Teardown del Bootloader

Antes de ceder el control, el Bootloader debe restaurar el hardware a su estado virgen de fábrica:

  1. Deshabilitar Interrupciones Globales: Para que ninguna interrupción residual salte durante la transición.
  2. Desactivar el SysTick: El "corazón" que late cada milisegundo debe detenerse. Si sigue latiendo, forzará a la Aplicación a ejecutar un vector de interrupción antes de que la Aplicación haya tenido tiempo de configurar su propia tabla.
  3. De-Inicializar Periféricos: Apagar GPIOs, UARTs o SPIs usados por el Bootloader.
ANATOMÍA DEL SALTO: VECTOR TABLE Y EL ESTADO THUMB TABLA DE VECTORES DE LA APLICACIÓN 0x08010000 0x20018000 Main Stack Pointer (MSP) 0x08010004 0x08010145 Reset_Handler de la App 0x08010008 NMI_Handler 0x0801000C HardFault_Handler ZOOM AL VALOR EXTRAÍDO 0x08010145 ... 0100 0101 Bit 0 (LSB) = 1 uint32_t app_addr = 0x08010000; uint32_t vector_reset = *(__IO uint32_t*)(app_addr + 4); void (*jump_func)(void); jump_func = (void (*)(void))vector_reset; jump_func(); // El procesador asume Thumb State

⚙️ Ejercicios Prácticos

Desarmar el Sistema: La biblioteca HAL de ST proporciona la función HAL_DeInit(), la cual intenta devolver todos los registros de los periféricos a su valor de reset. Además, debemos apagar el SysTick usando la API interna del Cortex-M.

Ejercicio 1: El Algoritmo C del Salto (Jump). Uniremos la validación de memoria del día anterior con la rutina de limpieza total. Este código debe residir exclusivamente en el proyecto del Bootloader.


/**
 * @file bootloader_jump.c
 * @brief Algoritmo definitivo de Teardown y Salto a la Aplicación (MISRA 11.1 Dev)
 */

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

// =========================================================================
// DEFINICIONES DE DIRECCIONES DE MEMORIA
// =========================================================================
#define APP_BASE_ADDRESS        (0x08010000U)
#define RAM_START_ADDRESS       (0x20000000U)
// SRAM1 del STM32F401RE tiene 96KB (0x18000)
#define RAM_END_ADDRESS         (0x20018000U) 

// =========================================================================
// PUNTERO DE FUNCIÓN (TIPADO STRICTO)
// =========================================================================
typedef void (*pFunction_JumpApp)(void);

// =========================================================================
// FUNCIÓN PRINCIPAL DE SALTO (BOOTLOADER KERNEL)
// =========================================================================
void Ejecutar_Salto_A_Aplicacion(void) {
    
    // 1. Extraer el valor almacenado en la primera palabra (MSP)
    // Usamos el modificador __IO (volatile) para asegurar lectura directa de Flash
    uint32_t msp_app_value = *(__IO uint32_t*)APP_BASE_ADDRESS;
    
    // 2. VALIDACIÓN ESTRICTA DE MEMORIA (Sanity Check)
    // El puntero de pila DEBE estar dentro del rango físico de la RAM del microcontrolador.
    // Si no lo está, la Flash está vacía (0xFFFFFFFF) o corrupta.
    if ((msp_app_value >= RAM_START_ADDRESS) && (msp_app_value <= RAM_END_ADDRESS)) {
        
        // --- 3. EXTRACCIÓN DEL VECTOR DE RESET ---
        // El Reset Handler está en Base_Address + 4 bytes
        uint32_t jump_address = *(__IO uint32_t*)(APP_BASE_ADDRESS + 4U);
        
        // (DESVIACIÓN MISRA-C Regla 11.1)
        // Transformamos el valor hexadecimal a un puntero ejecutable.
        // El compilador sabrá que el bit 0 es 1 (Thumb) pero ejecutará en direcciones pares.
        pFunction_JumpApp App_Salto_Handler = (pFunction_JumpApp)jump_address;
        
        // --- 4. TEARDOWN: DESTRUIR EL ESTADO DEL BOOTLOADER ---
        
        // A) Deshabilitar todas las interrupciones en el Core del Cortex-M4
        __disable_irq();
        
        // B) Detener el latido del sistema (SysTick Timer)
        // Si el SysTick queda encendido, lanzará un HardFault en la App.
        SysTick->CTRL = 0;
        SysTick->LOAD = 0;
        SysTick->VAL  = 0;
        
        // C) De-inicializar periféricos (HAL)
        HAL_DeInit();
        
        // D) Limpiar posibles interrupciones pendientes en el NVIC
        for (uint8_t i = 0U; i < 8U; i++) {
            NVIC->ICER[i] = 0xFFFFFFFFU;
            NVIC->ICPR[i] = 0xFFFFFFFFU;
        }
        
        // --- 5. ASIGNACIÓN DEL NUEVO PUNTERO DE PILA ---
        // Le decimos al procesador que olvide la RAM usada por el Bootloader
        // y asuma la RAM que la Aplicación necesita.
        __set_MSP(msp_app_value);
        
        // --- 6. EL SALTO AL VACÍO ---
        // Esta función NO tiene "return". El procesador entra en el Reset_Handler
        // de la nueva aplicación y la vida comienza de cero.
        App_Salto_Handler();
        
    } else {
        // ERROR CRÍTICO: No hay aplicación válida.
        // Modo Emergencia: Encender LED Rojo y esperar comandos por UART.
        // Entrar_Modo_Recuperacion_Bootloader();
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Infraestructura FOTA (Firmware Over-The-Air) y Control de Ciclo de Vida.

En un entorno industrial y corporativo, el costo de hardware es secundario; el verdadero costo oculto de un proyecto es el OPEX (Gastos de Operación Mantenimiento). Cada vez que detectas un "bug" crítico o necesitas modificar la Tabla Termodinámica del Gas LP por regulaciones estatales, actualizar físicamente cientos de estaciones a nivel nacional es logísticamente inviable.

Al dominar la escritura de un **Bootloader Bare-Metal** y el enrutamiento de la Tabla de Vectores, has independizado al hardware de la fragilidad del software. El procesador de tu Despachador de Gas LP ya no es un esclavo de un único programa grabado de fábrica. Ahora posee un mecanismo de supervivencia primitivo (El Bootloader en `0x08000000`) que es capaz de auditar, limpiar la memoria y ceder el trono al código operativo real. Has creado una máquina que puede mutar, evolucionar y recibir cirugías cerebrales a control remoto sin jamás arriesgarse a quedar inoperativa ("Bricked"). Tu diseño es ahora, oficialmente, IoT de Grado Industrial.

📝 Resumen del Día