DÍA 113 DE 365

Fase 2: Fundamentos de Hardware y Electrónica

📖 Teoría a Estudiar

El Filtro de Promedio Móvil (Moving Average)

El concepto de un promedio móvil es simple: en lugar de confiar ciegamente en la última lectura del ADC, confías en el promedio de las últimas $N$ lecturas. Cuando llega una lectura nueva, descartas la más antigua, ingresas la nueva a la "ventana", y recalculas el promedio. Este efecto "suaviza" la señal matemática, aplastando los picos aleatorios de ruido.

El Búfer Circular (Circular Buffer)

En ingeniería de software inexperta, para descartar la lectura más antigua, el programador mueve todos los datos de un arreglo (`array`) una posición hacia atrás usando un bucle `for`. Eso es un desperdicio criminal de CPU. En Bare-Metal, usamos un Búfer Circular: el arreglo se mantiene quieto y lo único que movemos es un puntero (índice). Cuando el índice llega al final del arreglo, salta de vuelta al principio, sobrescribiendo el dato más viejo de forma natural.

La Magia de la Potencia de 2 (Bitwise Optimization)

Para calcular un promedio, debes dividir la suma entre $N$. La instrucción de división (/) en lenguaje C toma muchos ciclos de reloj en la ALU del procesador. Pero si elegimos un tamaño de ventana que sea Potencia de 2 (Ej. 8, 16, 32 muestras), podemos reemplazar la pesada división por un simple desplazamiento de bits (Bit-Shift >>) hacia la derecha. Además, podemos reemplazar la lenta operación matemática Módulo (%) por una veloz máscara AND lógica (&).

FILTRO DIGITAL: PROMEDIO MÓVIL EN BÚFER CIRCULAR Señal Analógica (Ruido vs Filtro) 140 PSI Crudo (Ruido EMI) Filtrado (Moving Avg) Estructura C: Búfer Circular (N=8) [0] 2854 [1] 2860 [2] NUEVO DATO [3] 2845 ... [7] 2850 Index ptr LENTO: index = (index + 1) % 8; BARE-METAL: index = (index + 1) & 7;

⚙️ Ejercicios Prácticos

Ejercicio 1: Crea la función filtrar_adc_ruido(). Utilizaremos un Búfer Circular de 8 posiciones (Potencia de 2). Llevaremos una variable con la suma total constante para no tener que sumar las 8 casillas con un for cada vez. Si $N=8$, la máscara de reinicio del índice es $8-1=7$.


/**
 * @file dsp_filter.c
 * @brief Algoritmo DSP Bare-Metal: Promedio Móvil Rápido (N=8)
 */

#include <stdint.h>

// Tamaño del Búfer. DEBE ser potencia de 2 (2, 4, 8, 16, 32...)
#define MUESTRAS_N 8

// Variables de estado del filtro (Memoria estática)
uint16_t buffer_adc[MUESTRAS_N] = {0}; // Arreglo circular
uint32_t suma_actual = 0;              // Acumulador persistente
uint8_t  indice_actual = 0;            // Puntero del búfer

/**
 * @brief Filtra el ruido de la señal inyectando una nueva muestra.
 *        Costo computacional: O(1) - Constante extrema.
 * @param nueva_muestra Valor crudo entregado por el ADC.
 * @return uint16_t Valor del ADC suavizado.
 */
uint16_t filtrar_adc_ruido(uint16_t nueva_muestra) {
    
    // 1. Restar la muestra más antigua de la suma total
    // (La muestra que estamos a punto de sobrescribir)
    suma_actual -= buffer_adc[indice_actual];
    
    // 2. Guardar la nueva lectura fresca en el arreglo
    buffer_adc[indice_actual] = nueva_muestra;
    
    // 3. Sumar la lectura fresca a la suma total
    suma_actual += nueva_muestra;
    
    // 4. Avanzar el índice al siguiente espacio.
    // OPTIMIZACIÓN MÓDULO: En lugar de "indice % 8" (Lento),
    // aplicamos la máscara AND de bits "(indice) & 7".
    // 000... 111 & 111 = 111 (7)
    // 1000 (8) & 111 = 000 (Vuelve a 0 instantáneamente)
    indice_actual = (indice_actual + 1) & (MUESTRAS_N - 1);
    
    // 5. Retornar el promedio dividiendo entre N.
    // OPTIMIZACIÓN DIVISIÓN: En lugar de "/ 8" (Lento),
    // desplazamos 3 bits a la derecha (2^3 = 8).
    return (uint16_t)(suma_actual >> 3);
}

// Ejemplo de integración en el bucle principal
extern uint16_t leer_adc1_crudo(void);

int main(void) {
    // inicializar_adc1_pa1();
    
    // Pre-llenar el filtro para que no empiece desde cero absoluto
    for(int i=0; i<MUESTRAS_N; i++) {
        filtrar_adc_ruido(leer_adc1_crudo());
    }
    
    while(1) {
        // Leemos la presión cruda con picos electromagnéticos
        uint16_t presion_sucia = leer_adc1_crudo();
        
        // La pasamos por la caja matemática del filtro
        uint16_t presion_limpia = filtrar_adc_ruido(presion_sucia);
        
        // Ahora usamos 'presion_limpia' para la fórmula (Día 112)
        // uint32_t presion_psi = obtener_presion_psi(presion_limpia);
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Inmunidad al Ruido Electromagnético en Metrología.

Dentro del chasís de acero de tu despachador hay contactores industriales que manejan cientos de voltios. Cada vez que la bomba de gas arranca, genera un pulso electromagnético invisible que viaja por el aire e impacta el frágil cable de tu sensor de presión analógico. Si tu procesador lee el ADC en ese microsegundo exacto, creerá que la presión saltó a 200 PSI y cerrará de golpe las válvulas, arruinando la venta del cliente (Falso Positivo).

Al procesar la lectura en C a través de este filtro de Promedio Móvil, el pico de ruido queda "aplastado" y diluido entre las lecturas sanas anteriores. Y lo más hermoso de esta implementación es el uso estricto de Aritmética de Bits (Bitwise). La operación & 7 y el shift >> 3 garantizan que tu procesador complete todo el filtro en menos de 10 ciclos de reloj. Tu código Tesis ahora posee robustez y estabilidad analógica de grado SCADA.

📝 Resumen del Día