Antes de escribir una sola línea de código, debes entender quién fabricó el chip que tienes en tu escritorio. El STM32 no fue diseñado por una sola empresa; es un híbrido.
ARM (Advanced RISC Machines) es una empresa inglesa que diseña exclusivamente el "Cerebro" (la Unidad Aritmético Lógica, el bus de memoria y el controlador de interrupciones NVIC). ARM licencia este diseño como Propiedad Intelectual. Luego, empresas como STMicroelectronics compran ese cerebro, lo pegan en una pastilla de silicio y le añaden "Periféricos" a su alrededor (Puertos GPIO, UART, Convertidores ADC, Timers).
Por lo tanto, programar Bare-Metal significa que usarás los manuales de ARM para hablar con el núcleo, y los manuales de ST para encender y apagar pines.
Cuando aplicas 3.3V al pin VDD de tu tarjeta, el núcleo Cortex-M no busca una función llamada main(). El hardware físico está soldado para buscar ciegamente en la dirección de memoria 0x00000000. Allí debe encontrar dos cosas críticas:
Reset_Handler.Ejercicio 1: En los IDEs comerciales (como STM32CubeIDE), todo este proceso está oculto en archivos ensambladores incomprensibles generados automáticamente. Como ingeniero Bare-Metal, vamos a escribir la Tabla de Vectores y el `Reset_Handler` puro en lenguaje C. Así desmitificaremos el arranque.
#include <stdint.h>
// --- 1. SÍMBOLOS DEFINIDOS POR EL LINKER SCRIPT ---
// El Linker (enlazador) nos dirá matemáticamente dónde termina la RAM
extern uint32_t _estack;
// Prototipos de funciones
void Reset_Handler(void);
int main(void);
// --- 2. LA TABLA DE VECTORES DE INTERRUPCIÓN (VECTOR TABLE) ---
// Forzamos al compilador a colocar este arreglo de punteros exactamente
// al principio de la memoria Flash usando un atributo de sección.
__attribute__((section(".isr_vector")))
void (* const vector_table[])(void) = {
(void (*)(void))(&_estack), // 0x0000: Initial Stack Pointer
Reset_Handler, // 0x0004: Reset Vector (Punto de entrada)
0, // NMI Handler
0, // HardFault Handler
// ... aquí irían el resto de interrupciones de ARM
};
// --- 3. EL PUNTO CERO DE EJECUCIÓN (RESET HANDLER) ---
// Esta es la verdadera primera instrucción que el CPU ejecuta al encender
void Reset_Handler(void) {
// Aquí (en un proyecto real) copiaríamos la sección .data de la Flash a la RAM
// y llenaríamos la sección .bss con ceros.
// Configuramos el árbol de relojes al máximo (Ej. 84 MHz para el STM32F401)
// SystemInit();
// Finalmente, cedemos el control a tu lógica de aplicación
main();
// Si main() llega a retornar por error, atrapamos al CPU en un bucle infinito
while(1) { }
}
// --- 4. TU CÓDIGO METROLÓGICO ---
int main(void) {
// 1. Configurar Hardware
// 2. Encender Relés
while(1) {
// Despachar gas
}
return 0;
}
Objetivo del día: Comprensión absoluta del mapa de memoria de tu procesador.
Cuando soldados tu STM32 en la placa física, no le estás entregando un software de Windows. Le estás entregando una matriz de memoria plana. El código C que acabamos de escribir construye la Tabla de Vectores, que es literalmente el mapa de carreteras del procesador. Sin esta tabla colocada explícitamente en el inicio de la Flash (0x08000000), el STM32 entrará en estado de HardFault antes siquiera de poder parpadear un LED.
Comprender que main() no es magia, sino simplemente una función que es llamada por el Reset_Handler después de preparar la memoria RAM, te da el poder para modificar los tiempos de arranque. En tu Despachador, si ocurre un micro-corte de energía de 5 milisegundos, tu Reset_Handler desnudo reiniciará el hardware en microsegundos, permitiéndote recuperar la telemetría del flujo de gas casi instantáneamente sin que el usuario note el reinicio del sistema.
main().__attribute__((section(".isr_vector"))) para inyectar nuestra matriz de punteros exactamente donde el hardware físico exige que esté.