Ayer obtuvimos el periodo exacto entre pulsos ($\Delta T$) medido en microsegundos. La fórmula fundamental para obtener el caudal ($Q$) en Litros por Minuto (LPM) es:
$$ Q = \frac{f}{K} \times 60 $$
Donde $f$ es la frecuencia en Hz, y $K$ es el Factor K (pulsos por litro). Sabiendo que $f = \frac{1,000,000}{\Delta T}$, la ecuación completa se convierte en:
$$ Q = \frac{60,000,000}{\Delta T \times K} $$
Podríamos declarar las variables como float y hacer la división, para luego usar la pesada función sprintf(buffer, "%f", Q) y enviarla a la pantalla LCD. Sin embargo, en arquitecturas M0/M0+ o incluso M4 restringidas, incluir la librería de punto flotante de C arrastra kilobytes de código innecesario, aumenta el consumo de RAM y destruye el rendimiento.
La técnica industrial de optimización es usar Aritmética de Punto Fijo (Fixed-Point). En lugar de calcular Litros (ej. 12.345 L), calcularemos Mililitros enteros (12345 mL). Multiplicamos el numerador masivamente por 1000 antes de dividir. Todo el sistema se mantiene en variables uint32_t o uint64_t, permitiéndonos crear funciones itoa (Integer to ASCII) ultraligeras y personalizadas para la pantalla LCD.
Ejercicio 1: Crea la función calcular_caudal_LPM(). Implementa la constante del numerador de $60,000,000,000$. Al ser un número que sobrepasa el límite de 32 bits ($4.29 \times 10^9$), necesitamos obligar al compilador a tratarlo como un número de 64 bits utilizando el sufijo ULL (Unsigned Long Long). Extrae el valor y diseña un conversor a cadena (string) eficiente.
/**
* @file metrologia.c
* @brief Algoritmo de Cálculo de Flujo mediante Punto Fijo
*/
#include <stdint.h>
// Factor K físico de la turbina
#define FACTOR_K 450UL
// Constante pre-calculada: (60,000,000 * 1000)
// Escala por 1000 para obtener precisión de 3 decimales sin usar floats.
// Se usa ULL para reservar 64 bits en la memoria de la operación.
#define NUMERADOR_CAUDAL 60000000000ULL
/**
* @brief Calcula el caudal en mili-Litros por Minuto.
* @param delta_t_us: Periodo capturado por el Timer en microsegundos.
* @return uint32_t Caudal escalado (Ej. 12405 significa 12.405 LPM)
*/
uint32_t calcular_caudal_punto_fijo(uint32_t delta_t_us) {
// Si la bomba se apaga y el delta de tiempo es inmenso (o 0), protegemos la división
if (delta_t_us == 0 || delta_t_us > 5000000) {
return 0; // Flujo cero
}
// El Cortex-M maneja la división de 64 bits mediante instrucciones de hardware o
// librerías matemáticas estáticas eficientes de GCC.
uint32_t flujo_ml_por_min = (uint32_t)(NUMERADOR_CAUDAL / ((uint64_t)delta_t_us * FACTOR_K));
return flujo_ml_por_min;
}
/**
* @brief Conversor de entero fijo a String (Reemplazo de sprintf)
*/
void formato_lcd_caudal(uint32_t flujo_ml, char* buffer_salida) {
// Extraemos la parte entera y la parte decimal matemáticamente
uint32_t parte_entera = flujo_ml / 1000;
uint32_t parte_decimal = flujo_ml % 1000;
// Conversión simple (Custom ITOA) - Evita el uso de pesadas librerías stdio.h
// Nota: Esta es una implementación simplificada para un dígito entero y 3 decimales.
buffer_salida[0] = (parte_entera % 10) + '0';
buffer_salida[1] = '.';
buffer_salida[2] = ((parte_decimal / 100) % 10) + '0';
buffer_salida[3] = ((parte_decimal / 10) % 10) + '0';
buffer_salida[4] = (parte_decimal % 10) + '0';
buffer_salida[5] = '\0'; // Fin de cadena
}
int main(void) {
// Ejemplo de uso
uint32_t delta_tiempo_hardware = 10750; // Extraído del registro TIM2->CCR1
uint32_t caudal = calcular_caudal_punto_fijo(delta_tiempo_hardware);
char lcd_texto[10];
formato_lcd_caudal(caudal, lcd_texto); // Devuelve "12.403"
while(1) {
// ...
}
return 0;
}
Objetivo del día: Optimización de Firmware de Grado Aeroespacial.
Podrías haber usado float y sprintf(). Tu placa habría funcionado. Pero la ingeniería de excelencia se trata de escalabilidad. Al estructurar tus cálculos matemáticos en Punto Fijo y prescindir de las funciones estándar, acabas de reducir el tamaño de tu archivo binario compilado (.bin) en más de un 60%, y has liberado miles de ciclos de reloj de tu procesador STM32.
Este nivel de optimización de C es lo que te permitirá migrar tu código de Tesis en el futuro hacia microcontroladores sumamente limitados y económicos (como la línea Cortex-M0+ o NXP Kinetis), reduciendo el costo de manufactura de tu Despachador de Gas LP para su producción en masa, sin sacrificar una gota de precisión en la metrología.
sprintf con funciones de conversión a cadena (ASCII) personalizadas basadas en operaciones de módulo (%) es una de las técnicas de optimización Bare-Metal más poderosas de la industria.