En el desarrollo de software para PC, tu programa se ejecuta, llega a un return 0; y le devuelve el control al Sistema Operativo (Windows, Linux). En el desarrollo Bare-Metal y RTOS, el STM32 es el universo entero. No existe ningún "afuera" al cual retornar.
Si la función main() termina, el Program Counter (PC) del microcontrolador (el registro que apunta a la siguiente instrucción de memoria a ejecutar) seguirá avanzando secuencialmente hacia direcciones de memoria no inicializadas o prohibidas. Esto provocará que el procesador intente ejecutar "basura" hexadecimal como si fuesen comandos válidos, disparando de inmediato una excepción de hardware irreversible llamada Hard Fault. Un sistema bloqueado en un Hard Fault con una válvula despachadora de gas abierta es una bomba de tiempo. El microcontrolador nunca, bajo ninguna circunstancia, debe salir de su programa.
Al introducir un Sistema Operativo en Tiempo Real (FreeRTOS) para gestionar la telemetría IoT y el despacho simultáneamente, el paradigma del ciclo infinito muta:
main() solo inicializa el reloj, los periféricos y llama a vTaskStartScheduler(). El ciclo infinito que se escribe al final del main() solo se ejecutará si el RTOS falla estrepitosamente al arrancar (usualmente por falta de memoria RAM / Heap insuficiente para crear el planificador). Es tu red de seguridad pasiva.for(;;). El RTOS se encarga de cambiar rápidamente entre ellas.El código no salvará a la placa si el hardware colapsa eléctricamente. En la etapa esquemática de KiCad para el cerebro de la gasera, debes asegurar el entorno de supervivencia del microcontrolador:
VDD y VDDA. Los relés de las electroválvulas generarán transitorios destructivos; sin estos capacitores, el STM32 sufrirá reinicios (*Brown-out resets*) aleatorios.BOOT0 debe estar firmemente atado a GND con una resistencia pull-down de 10kΩ. Esto evita que ruido electromagnético meta al chip en modo de programación interno, lo cual paralizaría la máquina indefinidamente.Ejercicio 1: Programa la arquitectura base del main.c. Incorpora la función Safe_Shutdown() que captura el control total del procesador si el inicio de la multitarea aborta.
/**
* @file main.c
* @brief Día 001 - Despachador de Gas LP (Fase 1).
* Estructura de ejecución base, inicialización y prevención de Hard Fault.
*/
#include "stm32f4xx.h" // Archivo de cabecera de registros ARM Cortex-M
#include "FreeRTOS.h"
#include "task.h"
#include <stdbool.h>
// --- Prototipos Privados (Encapsulamiento estricto) ---
static void Hardware_Init(void);
static void Safe_Shutdown(void);
static void vTask_MonitorSistema(void *pvParameters);
/**
* @brief Función principal y punto de entrada. No debe retornar jamás.
*/
int main(void) {
// 1. Inicialización Bare-Metal
// Configurar reloj, inicializar pines de válvulas en estado cerrado (LOW).
Hardware_Init();
// 2. Despliegue de Tareas del RTOS
BaseType_t estado_tarea;
estado_tarea = xTaskCreate(
vTask_MonitorSistema,
"MonitorSalud",
configMINIMAL_STACK_SIZE,
NULL,
configMAX_PRIORITIES - 1, // Tarea de altísima prioridad
NULL
);
if (estado_tarea != pdPASS) {
// La creación falló porque el Heap del sistema está agotado.
Safe_Shutdown();
}
// 3. Ceder control al Scheduler
// Si la memoria es suficiente, el RTOS secuestra el Program Counter aquí.
vTaskStartScheduler();
// ==========================================
// 4. EL FALLBACK CRÍTICO (Red de Seguridad)
// ==========================================
// Si el flujo de código avanza a esta línea, vTaskStartScheduler falló
// y devolvió el control. El sistema está degradado.
while(1) {
Safe_Shutdown();
// En un escenario real, aquí disparamos un parpadeo de LED de error
// a través de un retardo básico por software, y refrescamos el Watchdog
// para que no reinicie la placa infinitamente, sino que se mantenga
// en este estado seguro apagado.
}
return 0; // Convención sintáctica. Físicamente inalcanzable.
}
/* --- Implementación de Rutinas --- */
static void Hardware_Init(void) {
// Código de inicialización de registros periféricos
// Lo desarrollaremos a fondo en el Día 002 y Día 004.
}
static void Safe_Shutdown(void) {
// BOTÓN DE PÁNICO EN SOFTWARE:
// 1. Escribir ceros (0) lógicos directos en los registros ODR
// de los pines GPIO de las electroválvulas.
// 2. Apagar señal PWM de las bombas.
// 3. Forzar el display a mostrar "ERROR CRITICO 01".
}
static void vTask_MonitorSistema(void *pvParameters) {
// Todo hilo de FreeRTOS debe contener su propio ciclo infinito
for(;;) {
// Lógica de validación de hardware y sensores de gas
// Bloquear tarea por 1000 Ticks, liberando CPU al resto del equipo
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
Hito Logrado: Control determinista de la ejecución y arquitectura Fail-safe.
Al implementar este bloque, le has dado al despachador un protocolo inquebrantable para "morir con dignidad" en caso de una falla catastrófica de memoria durante el encendido. La función Safe_Shutdown() representa la máxima de la automatización industrial: asumes anticipadamente que el hardware o el RTOS pueden fallar, y configuras una respuesta absoluta para que las electroválvulas bloqueen el flujo de combustible al instante. Tu placa ahora es inherentemente segura desde el instante en que recibe voltaje.
while(1)) detiene el avance destructivo del Contador de Programa (PC) hacia memoria inválida.main() queda relegado a actuar como una trampa de errores graves, delegando la ejecución real a los ciclos for(;;) individuales de cada Tarea.1. ¿Qué suceso físico ocurre dentro del silicio del STM32 si omites el ciclo `while(1)` al final del `main()` y el FreeRTOS no logra arrancar?
Respuesta: El Program Counter (PC) incrementa su dirección, pidiendo a la memoria Flash la siguiente instrucción. Como no hay código tuyo allí, el bus lee datos aleatorios (basura estática). El decodificador de instrucciones de la CPU intentará ejecutar esta basura, generando una excepción de instrucción indefinida que dispara una interrupción no enmascarable (NMI) hacia el HardFault_Handler. El chip queda vegetal, y si una válvula estaba en ALTO (abierta) durante la inicialización, jamás será apagada.