En el núcleo de la arquitectura ARM Cortex-M4 existe un área de memoria altamente privilegiada llamada System Control Block (SCB). Es aquí donde residen los registros que controlan el alma misma del procesador: la unidad de protección de memoria (MPU), los manejadores de fallos (Fault Handlers) y, críticamente, el Vector Table Offset Register (VTOR).
El VTOR le dice al Controlador de Interrupciones (NVIC): "Cuando ocurra un evento de hardware (Timer, UART, GPIO), no busques la lista de respuestas en 0x08000000. Súmale este Offset (desplazamiento) y búscalo allá".
main()?Muchos tutoriales básicos te dirán que pongas SCB->VTOR = 0x08010000; como la primera línea de tu main(). Esto es una negligencia industrial. El compilador en C ejecuta tareas invisibles antes de llegar a la palabra main. Llama al código de ensamblador de arranque (startup_stm32.s) el cual inicializa la memoria estática y copia datos de Flash a RAM.
Si ocurre un fallo de bus, o un pulso espurio activa una interrupción en ese milisegundo anterior al main(), el procesador se colgará eternamente. La modificación del VTOR debe hacerse en la función pre-principal: SystemInit().
El Archivo Oculto: system_stm32f4xx.c. Este archivo, generado automáticamente por el entorno de ST, es responsable de configurar los relojes iniciales y el SCB. Normalmente, los desarrolladores no entran aquí por temor a romper el proyecto. Nosotros sí entraremos. Es aquí donde activaremos una macro predefinida diseñada específicamente para particionamiento de memoria en arquitecturas FOTA.
Ejercicio 1: Modificación del núcleo (Core modification). Abriremos el archivo system_stm32f4xx.c (Ubicado usualmente en la carpeta Core/Src/) y localizaremos la macro `USER_VECT_TAB_ADDRESS`. Descomentaremos esa línea y especificaremos el Offset (`0x10000`) correspondiente al Sector 4, donde inicia nuestra aplicación.
/**
* @file system_stm32f4xx.c
* @brief Modificación para FOTA: Habilitación de Relocación Vectorial Temprana
*/
#include "stm32f4xx.h"
// =========================================================================
// DESBLOQUEO DE LA MACRO DE USUARIO
// =========================================================================
/*
* Por defecto, STMicroelectronics comenta esta línea.
* Para habilitar el ajuste del VTOR, DEBEMOS descomentarla.
*/
#define USER_VECT_TAB_ADDRESS
#if defined(USER_VECT_TAB_ADDRESS)
/*!< Descomente la línea siguiente si necesita redireccionar la tabla
de vectores en la memoria SRAM (nosotros la dejamos en FLASH) */
/* #define VECT_TAB_SRAM */
/*!< Ingrese el Offset (Desplazamiento) de su aplicación.
Nuestro Linker Script empieza en 0x08010000.
Por lo tanto, el offset desde 0x08000000 es 0x10000.
ATENCIÓN: Este valor debe ser un múltiplo de la alineación estricta
requerida por el Cortex-M (usualmente múltiplo de 0x200). */
#define VECT_TAB_OFFSET 0x10000U
#endif /* USER_VECT_TAB_ADDRESS */
// ... (Omitimos variables globales de sistema) ...
// =========================================================================
// SYSTEM INIT: LA PRIMERA FUNCIÓN EN EJECUTARSE
// =========================================================================
/**
* @brief Setup the microcontroller system
* Initialize the FPU setting, vector table location and the PLL configuration.
* @note This function is called by the startup_stm32f401xe.s file BEFORE main()
*/
void SystemInit(void)
{
/* Habilitación del FPU (Floating Point Unit) si está activado */
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Configuración original de resets de hardware ... */
/* --------------------------------------------------------------------- */
/* LA MAGIA DEL VTOR SE EJECUTA AQUÍ AUTOMÁTICAMENTE */
/* --------------------------------------------------------------------- */
#if defined(USER_VECT_TAB_ADDRESS)
#if defined(VECT_TAB_SRAM)
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Reubicación en RAM (No lo usamos) */
#else
// Esta es la línea que salvará nuestra vida industrial:
// SCB->VTOR = 0x08000000 | 0x10000 = 0x08010000;
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
#endif
#endif /* USER_VECT_TAB_ADDRESS */
}
Verificación en Assembly: Para entender por qué esto es tan crítico, mira cómo arranca el procesador según el archivo de ensamblador startup_stm32f401xe.s:
/* Fragmento de startup_stm32f401xe.s */
Reset_Handler:
ldr sp, =_estack /* 1. Asigna Puntero de Pila (MSP) */
bl SystemInit /* 2. Llama a nuestra función modificada C */
bl __libc_init_array /* 3. Inicializa las variables estáticas C */
bl main /* 4. Finalmente, llama a tu main() */
Objetivo del día: Alineación del Hardware (NVIC) y Despliegue de Código Particionado.
Muchos tutoriales de YouTube sobre Bootloaders y OTA cometen el error cardinal de modificar el registro `SCB->VTOR` como primera instrucción de la función `main()`. En un banco de trabajo, a veces funciona. Pero en tu Despachador de Gas LP, los motores y relés generan pulsos parásitos (ruido) que pueden entrar por los pines de interrupción en los microsegundos previos al `main()`. Si un pin configurado previamente por el Bootloader detecta ruido e intenta saltar a su interrupción, pero la tabla antigua ya fue borrada, la máquina colapsará (HardFault).
Al adentrarte en las entrañas del Core del Cortex-M4 y habilitar `USER_VECT_TAB_ADDRESS` en `system_stm32f4xx.c`, has aplicado un nivel de disciplina que se exige en el desarrollo aeroespacial y automotriz. Tu tabla de interrupciones se reubica instantáneamente en el "Paso 2" del diagrama de arranque, mucho antes de que el compilador C siquiera termine de crear sus variables. Cuando el Kernel de FreeRTOS despierte en el `main()` y configure el SysTick (el reloj vital del sistema operativo), el procesador sabrá exactamente en qué dirección física buscar las instrucciones. La arquitectura de tu software ahora es completamente FOTA-Compliant.
0x00000000 (aliased a 0x08000000).SCB->VTOR (Vector Table Offset Register), proporcionando el desplazamiento exacto (ej. 0x10000).main(). Hacerlo deja una "Ventana de Vulnerabilidad" de microsegundos donde cualquier interrupción de hardware no deseada ocasionará un bloqueo severo del sistema (HardFault).SystemInit(). El entorno STM32Cube facilita esto proveyendo la macro comentada USER_VECT_TAB_ADDRESS y la constante VECT_TAB_OFFSET en el archivo system_stm32f4xx.c.0x08010000) garantiza naturalmente esta alineación de hardware.