Un Bootloader es un mini-programa de software que vive en el Sector 0 de la memoria Flash de tu STM32. Es inmutable. Jamás se actualiza a sí mismo. Su único propósito en la vida es despertar al encender la máquina y hacerse una pregunta: "¿Hay una nueva actualización esperando ser instalada, o salto directo a ejecutar la Aplicación Principal?"
Dado que diseñaste tu hardware segregando el chip de red (ESP32) del chip de control de gas (STM32) en el Día 313, el STM32 no tiene idea de cómo conectarse al Wi-Fi. El flujo debe ser el siguiente:
.bin en su propia memoria externa (SPIFFS/SD).[CMD_REBOOT_TO_BOOTLOADER].Paso 1: Modificar el Script de Enlazado (Linker Script).
Por defecto, tu compilador asume que tu código vivirá en el principio de la memoria (0x0800_0000). Pero ahí ahora vivirá el Bootloader. Debes abrir el archivo STM32F401RETx_FLASH.ld de tu aplicación y cambiar el origen:
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 384K
Paso 2: Relocalizar la Tabla de Vectores (Vector Table Offset).
Cuando el hardware de la CPU sufre una interrupción (Ej. llega un pulso del caudalímetro), el procesador ARM busca instintivamente qué función ejecutar en la dirección 0x0800_0000. Si no le dices al procesador que la tabla se movió, saltará al Bootloader en lugar de a tu aplicación. Debes ir al archivo system_stm32f4xx.c y modificar:
#define VECT_TAB_OFFSET 0x20000U.
El Bootloader es un proyecto separado en C. Una vez que termina de descargar el firmware por UART y grabarlo en la Flash (usando la API HAL_FLASH_Program), debe "suicidarse" cediendo el control absoluto de la CPU al nuevo código parcheado. Esto requiere manipular registros a muy bajo nivel mediante punteros a funciones.
/**
* @file bootloader_core.c
* @brief Función crítica para ejecutar el Application Firmware desde un sector alto.
*/
#include "stm32f4xx_hal.h"
#include <stdio.h>
// Dirección donde el Linker Script de la Aplicación ubicó el código
#define MAIN_APP_START_ADDRESS (0x08020000U)
typedef void (*pFunction)(void);
// =========================================================================
// EL "SALTO DE FE" A LA APLICACIÓN PRINCIPAL
// =========================================================================
/**
* @brief Deshabilita interrupciones, configura el Stack Pointer y salta a la App.
*/
void Bootloader_JumpToApplication(void) {
printf("\r\n[BOOTLOADER] Actualización finalizada o no requerida.\r\n");
printf("[BOOTLOADER] Preparando Salto a 0x%08X...\r\n", MAIN_APP_START_ADDRESS);
// 1. Validar que exista código válido en esa dirección de memoria.
// El primer registro en la memoria debe ser un apuntador a la memoria RAM (Stack).
// La SRAM del STM32 siempre comienza en 0x20000000.
if (((*(__IO uint32_t*)MAIN_APP_START_ADDRESS) & 0x2FFE0000) == 0x20000000) {
printf("[BOOTLOADER] Imagen de aplicación válida encontrada.\r\n");
// 2. Deshabilitar todas las interrupciones globales antes de saltar.
// Si el bootloader recibe una interrupción de UART *mientras* está saltando,
// el sistema colapsará catastróficamente (HardFault).
__disable_irq();
// 3. Obtener la dirección del Reset Handler de la aplicación principal.
// El Reset Handler vive exactamente 4 bytes después de la dirección de inicio.
uint32_t jump_address = *(__IO uint32_t*)(MAIN_APP_START_ADDRESS + 4);
pFunction app_reset_handler = (pFunction)jump_address;
// 4. Mover el Main Stack Pointer (MSP) al Stack de la nueva aplicación.
// Esto le entrega a la aplicación su propia memoria RAM temporal.
__set_MSP(*(__IO uint32_t*)MAIN_APP_START_ADDRESS);
// 5. ¡Ejecutar la aplicación! (Esta función jamás retornará).
app_reset_handler();
} else {
printf("[BOOTLOADER] [ERROR] No hay aplicación válida. Pantalla de Muerte.\r\n");
// Aquí encendemos un LED Rojo y esperamos a que el técnico venga con un ST-Link.
while(1);
}
}
Objetivo del día: Mantenibilidad a Largo Plazo y Resiliencia en Despliegue Masivo (OTA).
El error que cometen las startups de hardware es asumir que el software que envían en la Versión 1.0 es definitivo. Cuando instalaste las 5 máquinas piloto ayer y descubriste que los botones rebotaban, tenías un problema: las máquinas estaban a 40 kilómetros de distancia y tu procesador STM32 estaba bloqueado criptográficamente (RDP 1). Sin un Bootloader, tu única opción habría sido desinstalar la tarjeta de las gaseras y traerlas al laboratorio, costándole a tu empresa cientos de dólares en soporte técnico.
Al diseñar este **Bootloader Personalizado**, has dotado a la arquitectura de tu máquina de "Inmortalidad Lógica". Tu chip secundario de red (ESP32) descargó silenciosamente el parche `v1.0.1` de AWS anoche. Al amanecer, la máquina se reinició sola, y el Bootloader, operando desde su búnker inmutable en el Sector 0, inyectó el nuevo código de filtros RC y la máquina de estados de botones en la Flash principal. Has actualizado el alma matemática e industrial de una máquina de alto riesgo desde la comodidad de tu escritorio. Esta infraestructura de actualización remota, separando el vector de red del vector de ejecución, es el sello definitivo de madurez de un producto de Hardware de Misión Crítica.
.ld) en la fase de compilación, indicándole al compilador C que los bytes de inicio de la memoria han cambiado (Ej. de 0x08000000 a 0x08020000).__disable_irq), sobreescribir el Main Stack Pointer (__set_MSP), y ejecutar un puntero a función directa hacia el Reset Handler de la nueva aplicación.