A nivel de hardware, los procesadores sencillos solo entienden sumas, restas y desplazamientos binarios (ALU). Cuando declaras float costo = volumen * 18.45;, estás pidiendo operaciones en el formato estándar IEEE-754. Si el chip no tiene un periférico dedicado para esto (Hardware FPU), el compilador enlazará rutinas de software invisibles (como __aeabi_fmul) que simulan la operación flotante usando cientos de operaciones enteras diminutas.
En el despacho de gas LP, si una iteración del bucle principal tarda $50\mu s$ y la siguiente tarda $800\mu s$ solo porque el valor del float cambió y requirió más pasos de emulación, tendrás Jitter. Esto provocará que pierdas pulsos del caudalímetro o envíes comandos erráticos a las válvulas.
¿Cómo medimos el impacto real? No usamos `printf` ni cronómetros de software, ya que estos introducen su propio retraso. Usamos instrumentación física: activamos un pin GPIO en ALTO (3.3V) justo antes de la línea matemática y lo bajamos a CERO inmediatamente después. Al conectar un osciloscopio, el ancho del pulso eléctrico revelará exactamente cuántos microsegundos tardó la CPU en resolver la ecuación.
Ejercicio 1: Perfilado GPIO. Usaremos la técnica de instrumentación de código manipulando directamente los registros de memoria del puerto (Bit-Banding) para evitar que las funciones de alto nivel contaminen la medición.
#include <stdint.h>
// --- REGISTROS HIPOTÉTICOS DEL MCU (STM32/NXP) ---
// Apuntadores directos a la memoria del Output Data Register del Puerto A
#define GPIOA_ODR (*((volatile uint32_t *)0x40020014))
#define PIN_OSCILOSCOPIO 5
// --- VARIABLES GLOBALES PARA EVITAR OPTIMIZACIÓN DEL COMPILADOR ---
volatile float f_volumen = 15.45f;
volatile float f_precio = 18.50f;
volatile float f_total = 0.0f;
volatile uint32_t u_volumen = 15450; // ml
volatile uint32_t u_precio = 1850; // centavos
volatile uint32_t u_total = 0;
/**
* @brief Demostración de latencia inyectando señales GPIO.
*/
void Medir_Latencia_Matematica(void) {
// ==========================================
// PRUEBA 1: EL DESASTRE DEL FLOAT
// ==========================================
// Encender Pin (Inicio de la medición)
GPIOA_ODR |= (1 << PIN_OSCILOSCOPIO);
// Cálculo flotante (Inyecta ~150-300 ciclos de rutinas Soft-FPU)
f_total = f_volumen * f_precio;
// Apagar Pin (Fin de la medición)
GPIOA_ODR &= ~(1 << PIN_OSCILOSCOPIO);
// [Pausa temporal para que el osciloscopio separe los pulsos]
for (volatile int i = 0; i < 50; i++);
// ==========================================
// PRUEBA 2: LA SOLUCIÓN METROLÓGICA (Punto Fijo)
// ==========================================
// Encender Pin
GPIOA_ODR |= (1 << PIN_OSCILOSCOPIO);
// Cálculo entero puro (La ALU lo resuelve en 1 ciclo de reloj)
// Se divide entre 1000 para ajustar la escala de punto fijo
u_total = (u_volumen * u_precio) / 1000;
// Apagar Pin
GPIOA_ODR &= ~(1 << PIN_OSCILOSCOPIO);
}
Hito Logrado: Erradicación total de la librería de coma flotante y validación empírica.
Si compilaras este código con y sin las variables float, notarías que el tamaño de tu firmware (sección .text en la Flash) se encoge varios Kilobytes al usar exclusivamente uint32_t. Al aplicar este principio, el núcleo metrológico de tu despachador LP no solo responde instantáneamente (cero Jitter) para cortar el paso del fluido exacto a tiempo, sino que liberas el espacio crítico de la memoria Flash para albergar el futuro Bootloader (FOTA) que diseñaremos en fases posteriores.
float en micros sin FPU obliga al compilador a incluir funciones de emulación. Es ineficiente en tiempo (ciclos) y espacio (Flash).1850 (asumiendo escala x100) permite usar multiplicaciones nativas en 1 ciclo.