Un error grave de diseño es usar una instrucción if (modelo == PREMIUM) en tu Súper Bucle. Esta instrucción se evalúa en el Tiempo de Ejecución (Run-Time). Significa que el código de la pantalla Premium y la pantalla Básica fueron introducidos por el compilador en la memoria Flash (desperdiciando KB de memoria), y además, el procesador gasta ciclos de reloj cada milisegundo preguntándose qué modelo es.
El lenguaje C tiene una etapa anterior al Compilador llamada Preprocesador. Es un manipulador de texto crudo. Las directivas que empiezan con `#` (como #define, #ifdef, #endif) le dicen al Preprocesador: "Antes de compilar, revisa estas reglas. Si la regla no se cumple, BORRA FÍSICAMENTE el texto de ese código".
Cuando usas Compilación Condicional, el código ignorado desaparece. No ocupa un solo byte en la memoria Flash, y no cuesta un solo ciclo de reloj. Esto te permite tener un solo archivo main.c que contenga toda la lógica de los 3 modelos de Despachador. Simplemente cambias una "Bandera de Compilación" (Flag) y el archivo .bin resultante contendrá estrictamente lo que necesita esa máquina.
Ejercicio 1: Crearemos un archivo system_config.h. Este será el panel de control de tu fábrica. Luego, crearemos un logger.h extremadamente elegante: Si definimos DEBUG_ENABLE, la función LOG_PRINT enviará texto por el UART para que depuremos. Si lo comentamos (modo Producción), el preprocesador destruirá todas las llamadas a LOG_PRINT en el proyecto, dejando el código ligero y ciego a espías.
system_config.h (El Selector de Fábrica)
/**
* @file system_config.h
* @brief Panel de Control de Construcción (Build Flags)
*/
#ifndef SYSTEM_CONFIG_H
#define SYSTEM_CONFIG_H
// =======================================================
// INTERRUPTORES DE COMPILACIÓN
// (Comenta o descomenta para alterar el hardware de destino)
// =======================================================
// Define el modelo de la máquina
#define DISPENSER_MODEL_PREMIUM
// #define DISPENSER_MODEL_BASIC
// Habilita mensajes de depuración en la terminal UART
#define DEBUG_ENABLE
// Define la región (Cambia reglas fiscales)
#define REGION_MEXICO
// #define REGION_EUROPE
#endif
logger.h (El Macro Inteligente)
/**
* @file logger.h
* @brief Sistema de registro Zero-Overhead basado en preprocesador
*/
#ifndef LOGGER_H
#define LOGGER_H
#include "system_config.h"
// Si el modo Debug está activo en la configuración...
#ifdef DEBUG_ENABLE
// Declaración externa de la función real de hardware
extern void UART_Enviar_Cadena(const char* msg);
// Reemplazamos la macro por la función real
#define LOG_PRINT(mensaje) UART_Enviar_Cadena(mensaje)
#else
// MODO PRODUCCIÓN COMERCIAL
// Si no está definido, reemplazamos la macro con una operación nula (do while 0)
// El compilador optimizará esto borrándolo por completo del ensamblador final.
#define LOG_PRINT(mensaje) do {} while(0)
#endif
#endif
main.c (El Código Único)
/**
* @file main.c
* @brief Súper Bucle Universal (Single Source of Truth)
*/
#include "stm32f401xe.h"
#include "system_config.h"
#include "logger.h"
// Las funciones específicas existirán o no dependiendo de la compilación
#ifdef DISPENSER_MODEL_PREMIUM
extern void ST7789_TFT_Init(void);
extern void ST7789_DrawString(const char* msg);
#endif
int main(void) {
// Inicialización base común a todos los modelos
// Hardware_Clock_Init();
// Este mensaje solo se imprimirá en el laboratorio.
// Al compilar para la gasolinera (Comentando DEBUG_ENABLE), esta línea literalmente
// no existirá, ahorrando memoria y tiempo de CPU.
LOG_PRINT("INICIANDO DESPACHADOR DE GAS...\r\n");
// ==========================================================
// INICIALIZACIÓN CONDICIONAL DE HARDWARE
// ==========================================================
#ifdef DISPENSER_MODEL_PREMIUM
LOG_PRINT("Modelo Premium Detectado. Iniciando Pantalla Color.\r\n");
ST7789_TFT_Init();
ST7789_DrawString("BIENVENIDO");
#elif defined(DISPENSER_MODEL_BASIC)
LOG_PRINT("Modelo Basico Detectado. Pantalla omitida.\r\n");
// Inicializar un simple LED de estado
// LED_Verde_Init();
#else
// Medida de seguridad: Si el ingeniero olvida definir un modelo,
// forzamos al compilador a detenerse y lanzar un error rojo.
#error "ERROR FATAL: ¡Debes definir un modelo en system_config.h!"
#endif
// ==========================================================
while(1) {
// Logica de Despacho...
// float litros = Calcular_Flujo();
#ifdef DISPENSER_MODEL_PREMIUM
// Solo los despachadores caros grafican en tiempo real
// ST7789_DrawFloat(litros);
#endif
#ifdef REGION_EUROPE
// Lógica fiscal Europea (ATEX Zone / MID)
// ...
#endif
}
return 0;
}
Objetivo del día: Arquitectura de Familia de Productos (Product Line Engineering).
Cuando lleves tu máquina al mercado, un cliente te dirá: *"Quiero 50 despachadores, pero no tengo presupuesto para la pantalla táctil, ¿puedes venderme una versión barata que solo se conecte al servidor?"*. Otro cliente te dirá: *"Quiero la versión de lujo"*. Si manejas el código incorrectamente, tendrás que crear dos proyectos de software. El día que descubras una mejora en la seguridad de la válvula (Watchdog Metrológico, Día 158), tendrás que programarlo dos veces.
Al implementar la **Compilación Condicional (`#ifdef`)**, has logrado que tu Tesis sea una plataforma industrial completa. Mantienes un solo archivo inmaculado de `main.c`. Cuando llega el pedido de las máquinas baratas, vas a `system_config.h`, activas `#define DISPENSER_MODEL_BASIC`, y le das a "Compilar". El preprocesador recorta mágicamente todos los drivers SPI, gráficos y las librerías de fuentes de la memoria, reduciendo el binario final de 60 KB a solo 20 KB. Entregas la máquina optimizada sin sacrificar tu tranquilidad mental como desarrollador.
LOG_PRINT expulsa cadenas de texto en el laboratorio, pero se auto-aniquila (se reemplaza por vacío) al compilarse en modo Producción (Release), evitando que los hackers lean tus registros o extraigan información sensible del binario final.