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.
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.
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 (&).
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;
}
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.
x % N) por AND Lógico (x & (N-1)), y la División (x / N) por un Desplazamiento (x >> log2(N)) transforma un código de nivel estudiante en Firmware Profesional de Altísimo Rendimiento.