problema con filtro digital mediante ecuacion

buenos días, disculpen tengo una duda espero algún forero listo por aquí me pueda ayudar a encontrar la respuesta, me tope con este código para hacer filtros por aquí en el foro (era para un promediador)
y lo adapte para una frecuencia de muestreo de 300 cada segundo lo que resultaría repetir la interrupción cada 3.3 ms y modifique la operación de promediar para que elimine el ruido de señales de 60Hz (esta ecuación viene en un libro de procesamiento de señales, y la corrobore con software MATLAB osea si funciona) se supone que una limitante seria: si el tomar valor del AnalogRead y la operación tardan mas tiempo de 3.3ms que es el tiempo que tengo antes de la próxima interrupción pero ya hice pruebas y hacer cada ISR ya con el valor enviado al PORT D tarda alrededor de 150 microsegundos, pero mi señal de salida no se ve nada bien.

Pd. utilizo este mismo codigo pero sin la operación del filtro, únicamente tomar valores y mandarlos al port D a 300 Hz y mi señal se ve muy bien!

si alguien tiene sugerencias, explicaciones o modificaciones en el código lo agradecere mucho.
aquí lo adjunto y también la librería usada timerOne:

#include <TimerOne.h>

volatile int sensor;
volatile float x[7]={0,0,0,0,0,0,0};
volatile float y;

void setup() {
  for (int i=0;i<8;i++){ // declaramos como salidas el port D completo
   pinMode(i,OUTPUT); }

Timer1.initialize(3333); //1/300=.003333 calculo para interrupcion 300Hz
  Timer1.attachInterrupt (timerIsr); // llama la Rutina de interrupcion Timer ISR
 void loop() {}

//Rutina de interrupcion
void timerIsr()

{int sensor = analogRead(A0); // tomo valores para x[0]
x[0]=((sensor)/4);    // arreglo  para pasar de 10 bits a 8 bits con DAC de resistencias ponderadas

y = x[0]-x[6]; // Ecuacion del filtro
PORTD = y; // imprime valor y en port D

x[6] = (x[5]); 
x[5] = (x[4]); 
x[4] = (x[3]);   // Actualizamos los valores del buffer
x[3] = (x[2]);
x[2] = (x[1]);
x[1] = (x[0]);
}

TimerOne.zip (7.32 KB)

Si tu problema es de rendimiento, te sugiero tres cosas:

  • Evita usar float.
  • Evita las divisiones.
  • El búfer x podría ser del tipo byte (ya que solo almacenas 8 bits por muestra).

Aquí te dejo este código mejorado:

#include <TimerOne.h>

//volatile int sensor; // Podemos prescindir de esta variable
volatile byte x[8]; // Ya por defecto se inicializan con cero. ¿Pueden ser ocho?
//volatile byte y; // Y de esta también

void setup() {
  DDRD = 0xFF; // declaramos como salidas el port D completo

  Timer1.initialize(3333); //1/300=.003333 calculo para interrupcion 300Hz
  Timer1.attachInterrupt (timerIsr); // llama la Rutina de interrupcion Timer ISR
}

void loop() {}

//Rutina de interrupcion
void timerIsr() {
  x[0] = analogRead(A0) >> 2; // tomo valores para x[0] y paso de 10 bits a 8 bits
  // Desplazar dos bits es mucho más rápido que una división

  PORTD = x[0]-x[7]; // Ecuacion del filtro e imprime valor en port D

  x[7] = x[6];
  x[6] = x[5]; 
  x[5] = x[4]; 
  x[4] = x[3];   // Actualizamos los valores del buffer
  x[3] = x[2];
  x[2] = x[1];
  x[1] = x[0];
}

Atención a esto:

x[0] = analogRead(A0) >> 2; // tomo valores para x[0] y paso de 10 bits a 8 bits
// Desplazar dos bits es mucho más rápido que una división

Veo que haces un desplazamiento a la derecha:

x[7] = x[6];
x[6] = x[5]; 
x[5] = x[4]; 
x[4] = x[3];   // Actualizamos los valores del buffer
x[3] = x[2];
x[2] = x[1];
x[1] = x[0];

Así que me preguntaba:

volatile byte x[8]; // Ya por defecto se inicializan con cero. ¿Pueden ser ocho?

Pregunto porque quizá esta sea una forma más rápida de desplazar un byte a la derecha:

uint64_t* p = (uint64_t*)x;
*p = *p >> 8;

Digo “quizá” porque no lo he probado ::slight_smile:

Cuando trabajas en velocidad piensa en optimizar todo
Esto es lento

x[0]=((sensor)/4);

usa

1 si quieres dividir por 2 y
2 si quieres dividir por 4.

Eso es notablemente veloz y es una optimización importante en tu código.
Otro tema.
Hablas de un DAC de 8 bits pero usas un array que va de 0 a 6 y que pasa con el 7mo bit?
Debería ser asi

#include <TimerOne.h>

volatile byte sensor;
volatile byte x[8]={0,0,0,0,0,0,0,0};
volatile byte y;

void setup() {
  for (int i=0;i<8;i++){ // declaramos como salidas el port D completo
     pinMode(i,OUTPUT); 
  Timer1.initialize(3333); //1/300=.003333 calculo para interrupcion 300Hz
  Timer1.attachInterrupt (timerIsr); // llama la Rutina de interrupcion Timer ISR
}

void loop() {}

//Rutina de interrupcion
void timerIsr() {
 sensor = analogRead(A0); // tomo valores para x[0]
 x[0]= sensor>>2;    // arreglo  para pasar de 10 bits a 8 bits con DAC de resistencias ponderadas

 // Ecuacion del filtro
 PORTD = x[0]-x[7]; // imprime valor y en el DAC

 x[7] = x[6];
 x[6] = x[5]; 
 x[5] = x[4]; 
 x[4] = x[3];   // Actualizamos los valores del buffer
 x[3] = x[2];
 x[2] = x[1];
 x[1] = x[0];
}

PORTD en el UNO

buenas tardes, ya tome sus consideraciones acerca de no usar float, de usar el comando DDRD = 0xFF; para poner el port D en salidas, de no usar la division si no el desplazamiento de bits y puse mi señal directa y se ve perfecta. pero me sigue marcando error al ver mi señal de salida sale ruidosa como si amplificara el ruido en vez de atenuarlo al realizar la operacion, no se si exista un conflicto de leer bits del pin A0, y realizar operaciones con los valores leidos anteriormente y guardados en el buffer? que creen que este pasando.

no entendi muy bien eso de usar la variable como byte asi que lo hice de ambas formas el metodo que yo estaba utilizando y tambien como byte en ambos dio igual

segun yo, lo que hace el programa es lee, del pin A0, convierte a 8 bits, guarda ese valor y luego en la siguiente lectura lo desplaza, y asi en circulo, hasta que el valor de x[7] tenga un valor, ya puede realizar la operacion y pasarla al port D pero es ahi cuando me presenta conflicto de hecho en el osciloscopio las primeras muestras donde el buffer es 0 la señal se ve bien todo el ruido empieza hasta que x[7] toma un valor y se realiza la operacion.

que creen que suceda?

encrone_90: pero me sigue marcando error al ver mi señal de salida sale ruidosa como si amplificara el ruido en vez de atenuarlo al realizar la operacion, no se si exista un conflicto de leer bits del pin A0, y realizar operaciones con los valores leidos anteriormente y guardados en el buffer? que creen que este pasando.

Tu algoritmo de supresión de ruido es... calcular la diferencia entre la muestra más antigua y la más reciente. ¿Seguro que es así?.

¿Has tomado en cuenta los posibles "subdesbordamientos"? El tipo de dato no maneja signo, y si el resultado de la resta es menor que cero, pues...

encrone_90: no entendi muy bien eso de usar la variable como byte

Solo vas a almacenar 8 bits y como número entero, ¿entonces para qué hacerlo en un tipo de dato más grande? O mejor aún: ¿para qué en un float que computacionalmente es el tipo más complicado de operar?

muy buena observación lucario448 , como es una resta, si el valor de x[0] es menor que el de x6 nota: no es el ultimo valor del buffer en realidad es la sexta muestra anterior la con esa ecuación elimina el ruido ese filtro (esta en un libro de procesamiento de señales y lo corrobore con MATLAB) osea si lleno un buffer de de 8 bits para enviarlo como byte, se pude, pero igual tendría que ser la sexta muestra anterior X[6] al nuevo x[0].

pero me parece muy buena observación eso del signo es probable que por eso no este dando como dices el tipo de dato no lleva un signo. yo a mi señal por supuesto para ver la respuesta procesada por Arduino estoy agregando un offset para que de puros valores positivos de voltaje 0-5V. pero la señal despues de la operación ahí la cosa cambia... si pudiera dar valores negativos

y alguna idea para este tipo de problema?

por que graficando la respuesta después del filtro en Matlab si me da la señal limpia.

Gracias encrone_90 por ignorar mi respuesta.

Te has perdido el 7mo bit eso es lo que te señalé. Tienes un DAC de 8 bits usando 7.

encrone_90: si pudiera dar valores negativos

Tienes dos opciones:

  • Perder un bit de resolución y usar el tipo char (no solo se usa para caracteres, también se maneja como el tipo byte pero con signo).
  • Usar el tipo int para preservar el rango ±255; o incluso para utilizar los 10 bits del ADC y todavía conservar la capacidad de almacenar valores negativos. Tiene la desventaja de penalizar un poco el tiempo de ejecución (el microprocesador es de 8 bits), sumado a tener que crear un DAC de 10 bits si se quiere utilizar la resolución completa del ADC.

Si hablas de soporte para voltajes negativos, te quedo mal porque hasta donde sé, tanto microcontrolador como ADC no están diseñados para voltajes simétricos.

encrone_90: y alguna idea para este tipo de problema?

¿Cuál exactamente? Ahí es donde me pierdo :confused:

surbyte: Te has perdido el 7mo bit eso es lo que te señalé.

¿Bit? ¿No se supone que lo que guarda cada elemento son 8 bits (byte)?

bit0 bit1 .... bit7 queda claro?