A nivel de ensamblador, la función HAL_Delay(1000) del ecosistema STM32 atrapa a la CPU en un bucle ciego: "Anota la hora actual, y gira en círculos sin hacer absolutamente nada hasta que el reloj avance 1000 milisegundos". Durante ese tiempo, la CPU que corre a 84 MHz desperdicia 84 millones de ciclos de reloj. Si un sensor I2C exige un retardo de 50ms antes de poder recibir su primer comando, usar HAL_Delay(50) es un crimen contra la concurrencia.
El silicio del procesador STM32 (Master) arranca casi instantáneamente (< 2 milisegundos). Los cuellos de botella son los periféricos (Esclavos) que tienen sus propios microcontroladores internos lentos. La estrategia de Boot Paralelo consiste en que el STM32 envíe la señal de encendido eléctrico (Enable Pin) a todos los periféricos simultáneamente en el milisegundo cero, y luego dedique su tiempo a leer la EEPROM, arrancar los relojes y calcular criptografía. Solo interrogará a los periféricos cuando hayan transcurrido sus respectivos tiempos de gracia biológicos.
Paso 1: Pines de "Enable" de Hardware.
Para controlar el arranque, tu STM32 debe ser el maestro absoluto de la energía. En el diseño de tu placa (KiCad), los periféricos grandes (ESP32, Backlight del LCD) no deben estar conectados permanentemente a la línea de 3.3V. Deben alimentarse a través de un Transistor MOSFET canal P (Load Switch), cuya compuerta esté conectada a un pin GPIO del STM32. Así, el STM32 decide exactamente en qué milisegundo reciben voltaje.
Paso 2: Borrar todos los `HAL_Delay()` del código.
Busca en todo tu proyecto (Ctrl+Shift+F) la palabra clave `HAL_Delay`. Deberás reemplazarlos con temporizadores de software basados en el reloj maestro HAL_GetTick(), transformando tu inicialización lineal en una Máquina de Estados (State Machine).
En lugar de bloquear la CPU esperando al Wi-Fi, dispararemos el temporizador y el sistema principal seguirá girando en el while(1). Cuando el temporizador marque que han pasado 2000 milisegundos, el código pasará al siguiente estado y enviará los comandos AT.
/**
* @file system_boot_optimizer.c
* @brief Máquina de Estados de Arranque No Bloqueante (Asynchronous Boot FSM)
*/
#include "stm32f4xx_hal.h"
#include <stdbool.h>
#include <stdio.h>
// =========================================================================
// DEFINICIÓN DE ESTADOS Y TIMEOUTS
// =========================================================================
typedef enum {
BOOT_STATE_INIT_HARDWARE = 0,
BOOT_STATE_WAIT_LCD,
BOOT_STATE_WAIT_WIFI,
BOOT_STATE_ALL_SYSTEMS_GO
} BootState_t;
BootState_t Current_Boot_State = BOOT_STATE_INIT_HARDWARE;
// Tiempos de calentamiento requeridos por el datasheet de los periféricos
#define LCD_WARMUP_TIME_MS 100
#define WIFI_WARMUP_TIME_MS 2000
uint32_t Boot_Start_Tick = 0;
// Variables lógicas del sistema
bool is_lcd_ready = false;
bool is_wifi_ready = false;
// =========================================================================
// LA MÁQUINA DE ESTADOS DEL ARRANQUE (Se llama en el while(1))
// =========================================================================
void System_Async_Boot_Manager(void) {
uint32_t current_tick = HAL_GetTick();
switch (Current_Boot_State) {
case BOOT_STATE_INIT_HARDWARE:
printf("[BOOT] Energizando periféricos simultáneamente...\r\n");
// 1. Dar poder eléctrico físico a los periféricos al mismo tiempo
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); // LCD Enable Pin
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET); // ESP32 Enable Pin
// 2. Registrar la marca de tiempo de inicio
Boot_Start_Tick = current_tick;
// 3. Avanzar al estado de espera no bloqueante
Current_Boot_State = BOOT_STATE_WAIT_LCD;
break;
case BOOT_STATE_WAIT_LCD:
// Comprobar si ya pasaron los 100ms requeridos por la LCD
if ((current_tick - Boot_Start_Tick) >= LCD_WARMUP_TIME_MS) {
// Ahora sí es seguro enviar comandos I2C/SPI
printf("[BOOT] LCD Caliente. Inicializando controlador...\r\n");
LCD_Send_Init_Sequence();
is_lcd_ready = true;
// Avanzar a esperar al módulo más lento
Current_Boot_State = BOOT_STATE_WAIT_WIFI;
}
// Mientras tanto, la CPU retorna inmediatamente y puede ejecutar otras tareas
break;
case BOOT_STATE_WAIT_WIFI:
// La pantalla ya está lista, mostramos el logo mientras esperamos al Wi-Fi
if (is_lcd_ready) {
LCD_Print_Loading_Animation();
}
// Comprobar si ya pasaron los 2000ms requeridos por el ESP32
if ((current_tick - Boot_Start_Tick) >= WIFI_WARMUP_TIME_MS) {
printf("[BOOT] ESP32 Caliente. Enviando comando AT de Sincronización...\r\n");
ESP32_Send_Command("AT+RST\r\n");
is_wifi_ready = true;
Current_Boot_State = BOOT_STATE_ALL_SYSTEMS_GO;
}
break;
case BOOT_STATE_ALL_SYSTEMS_GO:
// El proceso de arranque finalizó. El sistema cede el control a la FSM principal de ventas.
break;
}
}
// =========================================================================
// EJEMPLO DEL BUCLE PRINCIPAL (MAIN)
// =========================================================================
int main(void) {
HAL_Init();
SystemClock_Config();
// Configuraciones nativas (SRAM) que toman microsegundos se hacen aquí
Metrology_Variables_Init();
while (1) {
// En lugar de detenerse 2 segundos, el bucle gira miles de veces por segundo.
// El Boot_Manager avanza de estado solo cuando los relojes biológicos coinciden.
if (Current_Boot_State != BOOT_STATE_ALL_SYSTEMS_GO) {
System_Async_Boot_Manager();
} else {
// Lógica principal de venta de Gas LP
Process_Gas_Dispenser_Sales();
}
// Otras tareas críticas que no se pueden detener nunca (ej. Anti-Fraude)
Security_Fraud_Detection_Task();
}
}
Objetivo del día: Disponibilidad Extrema y Reducción del Tiempo de Recuperación (MTTR).
En el Día 325 implementamos el sistema de "Dying Breath" para sobrevivir a los apagones guardando la venta de gas en la memoria de respaldo de la batería. Pero cuando la energía de CFE regresa, el operador y el cliente están parados frente a la máquina con gran ansiedad. Si tu firmware usa retardos bloqueantes (`HAL_Delay`), la pantalla de la máquina permanecerá en negro total durante casi 3 segundos mientras espera al Wi-Fi. El cliente asumirá que su dinero se perdió, y el operador presionará botones desesperadamente, arriesgándose a cancelar la venta rescatada.
Al implementar el **Arranque Asíncrono Paralelo (Boot State Machine)**, has logrado que tu Despachador de Gas cobre vida instantáneamente. El microcontrolador despierta, energiza a los periféricos esclavos, recupera la venta de la memoria BKP y dibuja la interfaz en la pantalla LCD en menos de **120 milisegundos** (la suma del boot del ARM + calentamiento del LCD). El cliente ve su dinero en la pantalla casi en el instante en que regresa la luz. Y mientras el cliente se relaja y el operador levanta la pistola de la manguera, el módulo Wi-Fi ESP32 termina silenciosamente sus lentos 2 segundos de calentamiento en el fondo (Background) sin que nadie lo note. Esta es la diferencia entre un código que simplemente "funciona" y una experiencia de usuario industrial impecable.
HAL_Delay() (Busy-Wait Loop) es el anti-patrón número uno en sistemas embebidos de tiempo real. Congela la CPU, desperdiciando millones de ciclos de reloj que podrían utilizarse para ejecutar cálculos metrológicos o auditorías de seguridad.HAL_GetTick(). La función entra, evalúa si ha pasado el tiempo objetivo (Timeout) mediante la resta de marcas de tiempo, y sale inmediatamente (Non-Blocking) si el hardware aún no está listo.switch/case se vuelve inmanejable ("Spaghetti Code"). Este dolor arquitectónico es la prueba definitiva de que nuestro hardware está clamando por un Sistema Operativo.