DÍA 018 DE 365

Fase 1: Fundamentos de C y Lógica de Software

📖 Teoría Explícita: El Peligro de los Floats en el RTOS

Un microcontrolador STM32 sin unidad de punto flotante (FPU) procesa sumas y multiplicaciones enteras en 1 o 2 ciclos de reloj de hardware puro. Sin embargo, si declaras un precio como float precio = 12.50;, el compilador GCC inyecta silenciosamente una librería de software pesado (__aeabi_fmul) que puede consumir hasta 300 ciclos por cada operación.

En un sistema multitarea con FreeRTOS, realizar cálculos lentos bloquea temporalmente al planificador. Si durante esos 300 ciclos el sensor de presión lanza una alerta de emergencia, el microcontrolador no podrá detener la bomba de gas a tiempo. Además, el estándar IEEE 754 para números flotantes causa imprecisiones catastróficas (ej. guardar un 10.20 puede resultar internamente en un 10.199999). En dinero y volumen legal, esto es inaceptable.

El Método de Punto Fijo (Fixed-Point)

En lugar de guardar decimales, multiplicamos los valores por un factor constante (Escala) y trabajamos exclusivamente con números enteros (uint32_t). Para los cálculos, el punto decimal existe únicamente en la mente del programador, hasta el momento exacto en que vamos a mostrar los datos en la pantalla o enviarlos al ESP32 por UART.

ARQUITECTURA DE CÁLCULO EN PUNTO FIJO (ENTEROS) Flujómetro (Sensor) Ej: 4612 Pulsos Volumen (Escala 1000) Int32 = 10,248 (Es 10.248 L) X Precio Base (Escala 100) Int32 = 2050 (Es $20.50) Importe Interno (Int64) 10248 * 2050 = 21,008,400 (Escala 100,000) Pantalla / ESP32 UART $210.08

⚡ Hardware y Lógica: El Flujómetro

En tu PCB, el flujómetro (usualmente de efecto Hall o turbina magnética) genera pulsos que entran a un pin del STM32 configurado como interrupción de hardware (EXTI). Supongamos que tu datasheet indica un Factor K de 450 pulsos por litro. En lugar de dividir pulsos / 450.0 y guardar flotantes, contaremos pulsos en un entero estricto y haremos la conversión matemática escalada dentro de nuestra tarea de RTOS.

⚙️ Ejercicios Prácticos: Aritmética de Punto Fijo

Ejercicio 1: Programa el cálculo de venta (Litros e Importe) cuidando celosamente el desbordamiento (Overflow). Cuando multiplicas dos números muy grandes (Liters x1000 por Precio x100), el resultado de 32 bits explotará. Para esta multiplicación, debes hacer un "Casteo" (Cast) temporal a uint64_t.


#include <stdint.h>
#include <stdio.h>

// --- 1. Definición de Escalas y Factores Físicos ---
#define ESCALA_VOLUMEN      1000  // El volumen se manejará en Mililitros (3 decimales)
#define ESCALA_PRECIO       100   // El precio se manejará en Centavos (2 decimales)
#define PULSOS_POR_LITRO    450   // Según hoja de datos del flujómetro

// Variables de Estado
static volatile uint32_t contador_pulsos_hw = 0; // Se modifica en una interrupción
static uint32_t precio_actual_centavos = 2050;   // $20.50 MXN/Litro

/**
 * @brief Rutina de Servicio de Interrupción (ISR) del Flujómetro.
 *        Se ejecuta cada vez que el sensor detecta un imán.
 */
void EXTI_Flujometro_Callback(void) {
    contador_pulsos_hw++; 
}

/**
 * @brief Tarea de FreeRTOS para cálculo financiero (Totalmente Entera).
 */
void calcular_y_mostrar_despacho(void) {
    
    // 1. Obtener Volumen en mililitros (Punto fijo con 3 decimales implicitos)
    // Fórmula: (Pulsos * 1000) / Factor. Multiplicamos ANTES de dividir para no perder precisión.
    uint32_t volumen_ml = (contador_pulsos_hw * ESCALA_VOLUMEN) / PULSOS_POR_LITRO;

    // 2. Calcular Importe Bruto. 
    // volumen_ml (escala x1000) * precio_centavos (escala x100) = importe (escala x100,000)
    // Obligatorio usar uint64_t para evitar overflow en ventas grandes.
    uint64_t importe_interno_bruto = (uint64_t)volumen_ml * precio_actual_centavos;

    // 3. Re-escalar importe a Centavos puros (Eliminamos la escala de volumen dividiendo entre 1000)
    uint32_t importe_centavos = (uint32_t)(importe_interno_bruto / ESCALA_VOLUMEN);

    // 4. Formatear para Pantalla o WiFi sin librerías flotantes
    // Ej: importe_centavos = 21008
    uint32_t pesos = importe_centavos / 100;    // = 210
    uint32_t centavos = importe_centavos % 100; // = 8
    
    uint32_t litros = volumen_ml / 1000;
    uint32_t mililitros = volumen_ml % 1000;

    // Se envía por UART simulando "%d.%02d"
    // printf("Volumen: %lu.%03lu L\r\n", litros, mililitros);
    // printf("Total: $%lu.%02lu\r\n", pesos, centavos);
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Hito Logrado: Precisión Financiera y Liberación de CPU.

Al erradicar los flotantes, has reducido el tiempo de cómputo del hilo comercial de tu FreeRTOS drásticamente. Ahora la máquina de estados del Despachador (Día 4) tiene prioridad total de hardware, y tus cálculos de precio están blindados contra la pérdida de centavos que sufren las operaciones clásicas de C al redondear decimales, lo cual garantiza que cumples con las regulaciones de pesos y medidas gubernamentales.

📝 Resumen del Día

❓ Cuestionario de Autoevaluación

1. Si un despachador cuenta 900 pulsos con un factor de 450 (Pulsos/Litro). Usando la fórmula incorrecta: `volumen = (900 / 450) * 1000;` nos daría 2000 ml. Pero si los pulsos fueran 800, el resultado de `(800 / 450) * 1000` sería 1000 ml en enteros, robándole 777 ml al cliente. ¿Por qué ocurre esto y cómo lo resolvimos en el código de hoy?
Respuesta: Al hacer la división primero, el entero trunca el residuo (800 / 450 = 1.77, que se guarda como 1). Luego multiplica 1 * 1000 = 1000. Lo resolvimos escalando todo antes de dividir: `(800 * 1000) / 450`. Ahora el procesador hace 800,000 / 450 = 1777 ml. Conservamos una precisión metrológica implacable.