En el STM32, el reloj del sistema (System Clock) viaja a través de un árbol jerárquico. Una rama alimenta al núcleo Cortex-M (el procesador que ejecuta tus sumas y bucles while), y otras ramas alimentan a los buses periféricos (AHB y APB) donde viven tus UARTs, DMAs y Timers.
En el modo Sleep, el hardware del STM32 cierra literalmente una compuerta eléctrica (Clock Gating) cortando el reloj exclusivamente del núcleo de la CPU. El procesador entra en un coma profundo y su consumo de energía cae drásticamente. Sin embargo, los relojes de los periféricos siguen latiendo. Tu UART sigue transmitiendo el ticket de la impresora, y tu DMA sigue descargando el paquete Modbus RS485. Todo mientras el cerebro duerme.
¿Cómo se queda dormido y cómo despierta? No es escribiendo un registro complejo. Es una instrucción ensambladora literal incrustada en la arquitectura ARM: WFI (Wait For Interrupt).
Cuando el procesador lee esa instrucción, detiene la ejecución del código en esa línea exacta y apaga su reloj. Solo un evento puede resucitarlo: Una interrupción de hardware en el NVIC.
En nuestra arquitectura, el CPU se despertará si el usuario presiona una tecla (EXTI), si la línea RS485 guarda silencio tras un paquete (UART IDLE), o, invariablemente, cada milisegundo por el "latido del corazón" de nuestro sistema (el SysTick Timer).
Ejercicio 1: Integraremos la instrucción intrínseca de CMSIS __WFI() al final de tu Súper Bucle infinito. A partir de hoy, en el momento en que tu CPU termine de procesar las banderas pendientes, ejecutará esta línea y se apagará. El `SysTick` de 1 milisegundo (Día 98) actuará como nuestro "marcapasos", despertando a la CPU mil veces por segundo, pero manteniéndola dormida durante las fracciones de tiempo muerto, reduciendo el consumo drásticamente.
/**
* @file super_loop_sleep.c
* @brief Implementación del Súper Bucle Orientado a Eventos con WFI (Modo Sleep)
*/
#include "stm32f401xe.h"
#include <stdbool.h>
// Dependencias lógicas de días anteriores
extern volatile bool bandera_trama_lista; // De UART DMA IDLE (Día 135)
extern volatile bool bandera_teclado_activado; // De EXTI Keypad (Día 128)
extern volatile bool bandera_pulso_sensor; // Del Caudalímetro
extern void procesar_tramas_pendientes(void);
extern char escanear_teclado_async(void);
extern void impresora_tick(void);
/**
* @brief Configuración opcional para mejorar la gestión de energía
*/
void configurar_politicas_de_energia(void) {
// Por defecto, el STM32 se despierta del Sleep en cuanto recibe una interrupción,
// ejecuta la rutina ISR, y vuelve al Súper Bucle (main).
// Si queremos que tras ejecutar el ISR vuelva a dormir INMEDIATAMENTE sin
// pasar por el Súper Bucle (útil en sistemas 100% basados en interrupciones puras),
// habilitaríamos el bit SLEEPONEXIT del registro SCB->SCR.
//
// Sin embargo, en nuestra arquitectura SÍ necesitamos volver al Súper Bucle
// para procesar las "Banderas" asíncronas (como el Parser Modbus o el Debounce).
// Por lo tanto, nos aseguramos de que SLEEPONEXIT esté APAGADO.
SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;
}
int main(void) {
// --- FASE DE ARRANQUE E INICIALIZACIÓN (BOOT) ---
// (Consumo pico: Todos los relojes y memorias trabajando a 84 MHz)
// 1. Relojes y Seguridad
// inicializar_relojes_84mhz();
// cargar_variables_desde_bkp();
// inicializar_defensa_tamper();
// 2. Periféricos de E/S
// SysTick_Init_1ms();
// inicializar_teclado_exti();
// inicializar_usart2_dma_circular(); // RS485
// inicializar_usart1_impresora();
configurar_politicas_de_energia();
// --- SUPER BUCLE ORIENTADO A EVENTOS (LOW POWER) ---
while(1) {
// ========================================================
// 1. ZONA ACTIVA DEL PROCESADOR (Cortex-M Despierto)
// ========================================================
// El CPU atiende rápido todo lo que el hardware dejó pendiente.
// Mantenimiento de la impresora (Non-blocking)
impresora_tick();
// Escaneo asíncrono del teclado solo si la alarma EXTI sonó
if (bandera_teclado_activado) {
char tecla = escanear_teclado_async();
if (tecla != 0) {
// procesar_comando_local(tecla);
}
}
// El DMA nos avisó mediante IDLE que llegó un paquete entero RS485
if (bandera_trama_lista) {
bandera_trama_lista = false;
procesar_tramas_pendientes(); // Parsea y responde
}
// El Caudalímetro emitió un pulso magnético
if (bandera_pulso_sensor) {
bandera_pulso_sensor = false;
// totalizador_historico_ml += 10;
// respaldar_totalizador_vivo();
}
// ========================================================
// 2. ZONA DE SUSPENSIÓN (Cortex-M Dormido)
// ========================================================
// Si el procesador ejecutó todo el código anterior y ya no hay
// banderas pendientes, no tiene sentido que repita el bucle "en el aire".
// Le ordenamos que apague su propio reloj maestro interno.
__WFI(); // Wait For Interrupt (Instrucción intrínseca de Ensamblador ARM)
// --- EL TIEMPO SE DETIENE AQUÍ PARA EL CPU ---
// (El consumo del chip cae drásticamente. El silicio se enfría).
// ... (Transcurre el tiempo físico, tal vez 0.5 milisegundos) ...
// ¡BAM! Una interrupción (SysTick, UART, EXTI) golpea el NVIC.
// El CPU reconecta su reloj, ejecuta el ISR de la interrupción, y
// retoma la ejecución EXACTAMENTE en la línea de código de abajo.
// Al llegar al final de las llaves, el 'while(1)' volverá a subir
// a procesar las banderas.
}
return 0;
}
Objetivo del día: Eficiencia Energética, Fiabilidad Térmica y UPS Prolongado.
El peor enemigo de los circuitos electrónicos en zonas calurosas o en el exterior (como las gasolineras) no es el software, es la Temperatura. Un STM32 girando infinitamente su CPU al 100% de carga a 84 MHz consume miliamperios críticos y disipa calor. A largo plazo, el estrés térmico envejece la placa base.
Aún más crítico: Los Despachadores de gas deben tener un sistema UPS (Baterías de respaldo central) que los mantenga encendidos durante los apagones para terminar las ventas en curso. Al integrar la directiva __WFI(), has transformado el perfil de consumo de tu máquina. Como el Despachador pasa horas enteras esperando a que llegue un cliente o un paquete Modbus, tu procesador ahora pasará el 99% de su vida útil en un estado de letargo profundo. Solo se despierta una fracción de microsegundo cada vez que el SysTick late, evalúa si hay trabajo, y vuelve a dormirse. El calentamiento térmico baja a cero absoluto, prolongando la vida del hardware industrial por años.
while(1) vacío o que solo evalúa condiciones falsas constantemente se denomina Busy-Waiting (Espera Activa), y es una pésima práctica en sistemas de grado industrial por su brutal desperdicio energético.