Un error de diseño común en la programación bare-metal es permitir que el Súper Bucle gire al 100% de la capacidad de la CPU cuando no hay tareas útiles que realizar (por ejemplo, cuando no se está despachando gas y el sistema está inactivo). Esto se traduce en ciclos de reloj desperdiciados, disipación de calor innecesaria y consumo de energía crónico.
El estándar en la industria para solucionar esto es el uso de Modos de Bajo Consumo (Sleep Modes), controlados por Banderas de Suspensión (Sleep Flags). En lugar de iterar vacíamente, el código evalúa una bandera de estado. Si ninguna subtarea requiere atención, el software invoca una instrucción en ensamblador que detiene el núcleo del microcontrolador hasta que un evento de hardware (interrupción) lo despierte.
En el ecosistema ARM Cortex-M, la transición al modo de bajo consumo se logra típicamente mediante la instrucción en ensamblador WFI (Wait For Interrupt). Al ejecutarla, la CPU detiene su reloj interno inmediatamente, pero mantiene activos los periféricos seleccionados y el controlador de interrupciones (NVIC). En C sin dependencias de librerías pesadas, organizamos el flujo de la siguiente forma:
volatile bool. volatile es crítico para evitar que el compilador optimice y borre las banderas que cambian desde rutinas ISR.while(1) evalúa si hay "trabajo por hacer". Si no hay tareas pendientes, pone la bandera de suspensión en verdadero.__WFI().Ejercicio 1: Implementa la lógica fundamental de manejo de energía mediante banderas de estado en C puro. Simula la estructura de decisión que unifica tu rutina operativa con tu rutina de suspensión.
#include <stdint.h>
#include <stdbool.h>
// --- 1. DEFINICIÓN DE BANDERAS GLOBALES ---
// 'volatile' indica al compilador de C que estas variables pueden
// cambiar en cualquier momento debido a una interrupción (hardware).
volatile bool despachador_activo = false;
volatile bool permitir_suspension = false;
// Macro simulando la instrucción intrínseca de CMSIS para ARM
// En un código real de STM32 incluirías <core_cm4.h> o similar.
#define __WFI() /* asm("wfi") */
// --- 2. GESTOR DE ENERGÍA ---
void gestionar_modo_bajo_consumo(void) {
if (permitir_suspension) {
// Paso previo opcional: Apagar periféricos que gastan energía
// apagar_retroiluminacion_lcd();
// --- DETENER RELOJ DE LA CPU AQUÍ ---
__WFI();
// ------------------------------------
// La CPU se congela en la línea anterior.
// Solo avanzará a esta línea cuando una interrupción la despierte.
permitir_suspension = false; // Reset de seguridad
// encender_retroiluminacion_lcd();
}
}
// --- 3. SÚPER BUCLE PRINCIPAL ---
int main(void) {
// Inicialización del hardware, pines y puertos...
while(1) {
if (despachador_activo) {
// Lógica crítica: Leer presión, calcular flujo, actualizar válvulas
// procesar_sensores_gas();
// Si el despacho termina por límite o corte manual:
// despachador_activo = false;
}
else {
// Si el sistema no está operando activamente, habilitamos la bandera
permitir_suspension = true;
}
// Evaluar suspensión al final del ciclo
gestionar_modo_bajo_consumo();
}
return 0;
}
// --- 4. RUTINAS DE SERVICIO DE INTERRUPCIÓN (ISR) ---
// Ejemplo: Botón "START" de la máquina conectado a un pin de interrupción.
void EXTI0_IRQHandler(void) {
// El cliente presiona el botón, lo que levanta una señal eléctrica.
// Esto despierta inmediatamente al STM32 de la instrucción WFI.
despachador_activo = true; // Solicitar inicio del proceso
permitir_suspension = false; // Denegar explícitamente el sleep
// Limpiar bandera de interrupción de hardware (ej. EXTI->PR)
}
Objetivo del día: Estrategia de Gestión Energética Industrial.
Al desarrollar un Despachador de Gas LP de grado industrial, la eficiencia térmica y energética es fundamental. A lo largo de un día, la máquina pasará la gran mayoría de sus horas en estado de reposo esperando clientes. Mantener el procesador STM32 procesando cálculos nulos repetitivamente eleva la temperatura del encapsulado y drena la energía de la estación (especialmente crítico si la estación opera con bancos de baterías UPS durante cortes de red eléctrica).
Al programar tus banderas de suspensión, le enseñas al firmware a "relajarse". La CPU detendrá sus operaciones lógicas, manteniendo el consumo al mínimo, y solo "despertará" en fracciones de microsegundo cuando el teclado matricial emita una interrupción o un lector de tarjetas envíe datos por el bus UART. Esto demuestra madurez en tu diseño de firmware, separándote de los prototipos amateur que ignoran los estados de reposo.
main() y las Interrupciones deben ser declaradas como volatile para evitar corrupciones de datos por optimizaciones del compilador.WFI (Wait For Interrupt) pausa el núcleo del microcontrolador hasta que un pulso eléctrico externo (hardware) o periférico (timer/UART) lo obliga a reanudar el código.1. ¿Qué pasaría si omites la palabra clave `volatile` en tu bandera `despachador_activo`?
Respuesta: El compilador GCC optimizará tu código. Al analizar el Súper Bucle, verá que despachador_activo nunca parece cambiar dentro de la función main(). Como el compilador no "entiende" que una interrupción paralela puede cambiarla, la tratará como una constante y el despachador jamás se activará o quedará atrapado en un bucle infinito, sin importar cuántas veces presiones el botón físico.
2. Si usas FreeRTOS más adelante, ¿cómo cambia esta lógica de WFI puro?
Respuesta: Aunque el principio del silicio es idéntico, al utilizar un RTOS, ya no gestionas el WFI manualmente en tu main(). El sistema operativo proporciona una Tarea Inactiva (Idle Task) o un Idle Hook. Cuando todas tus tareas de control de gas están bloqueadas esperando un evento (como un delay o un semáforo), el Scheduler de FreeRTOS cede el control a la Idle Task, la cual es la encargada de ejecutar de forma segura la instrucción de bajo consumo subyacente.