Autor Tema: Conversión digital analoga [SOLUCIONADO]  (Leído 9817 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #15 en: 05 de Abril de 2018, 16:46:30 »
Hola buenas, justamente ayer el profesor explicó eso que hiciste ahí (publiqué antes de la clase pensando que no nos iba a decir ni explicar nada jaja).

Bien, teniendo la expresión como tal, nos hizo entender que es importante escalizar la salida de esa operación previamente dicha. Observando tu codigo, y teniendo en cuenta lo que el nos dijo, iniciamos a evaluar cuando n=2, ahora bien, tengo un par de inquietudes...

¿cómo puedo escalizar esa señal teniendo en cuenta que ese valor es el que va a leer el PWM para mostrarlo en la salida del CPP ?
¿basicamente lo unico que va en el ciclo sería la operación como tal? en mi caso, ¿iría dentro del while y el resto de la definición iria en el main?, considerando que nos dice que cada 10 ms, el programa tiene que repasar la operación...

Desconectado Eduardo2

  • PIC24H
  • ******
  • Mensajes: 1004
Re:Conversión digital analoga
« Respuesta #16 en: 06 de Abril de 2018, 13:36:35 »
Citar
Bien, teniendo la expresión como tal, nos hizo entender que es importante escalizar la salida de esa operación previamente dicha. Observando tu codigo, y teniendo en cuenta lo que el nos dijo, iniciamos a evaluar cuando n=2, ahora bien, tengo un par de inquietudes...

¿cómo puedo escalizar esa señal teniendo en cuenta que ese valor es el que va a leer el PWM para mostrarlo en la salida del CPP ?

Es hacer una operaci'on tipo y=a+b*x

Si generás un seno de amplitud ligeramente menor que 1 en punto flotante y necesitás escalarlo a un entero de 10bit pues hacés:   xpwm = (x+1)*512

Lo de generarlo "ligeramente menor que 1" es para no tener overflow o underflow cuando x vale +/-1 , pues debido a los redondeos de las operaciones en punto flotante podría ocurrir que (x+1)*512 tome valores negativos o mayores de 1023 (0x3FF)


Citar
¿basicamente lo unico que va en el ciclo sería la operación como tal? en mi caso, ¿iría dentro del while y el resto de la definición iria en el main?, considerando que nos dice que cada 10 ms, el programa tiene que repasar la operación...

Algo así:

Generás un tic de 10ms

Código: [Seleccionar]
int1 tic = 0 ;

#INT_TIMER1
void tic_10ms() {
    set_timer1(xxxx);  // xxxx el que haga falta para 10ms
    tic = 1 ;
}

Y en main:

Código: [Seleccionar]
int16   xpwm    ;  // Global por si la uso fuera de main

void main(){

   .........  demás definiciones
   .................
    float32 x = .95 ;   // Valor inicial = Amplitud. Por prevención ligeramente menor que 1
    float32 d = 0   ;

    float32 k = 1/32  ;
    // El ángulo de avance será  ang = 2*asin(sqrt(k)/2)
    // Y la frecuencia  f = ang/(2*PI*T)
    // para T=10ms, k=1/32  resulta f=2.68Hz
   
    ..... Habilitaciones, etc
    ...................................

    xpwm = (x+1)*512 ;   // pwm duty

    while(TRUE)
    {
        if(tic){
            tic = 0 ;
            x += d -= x*k      ;   // genero el nuevo valor de seno
            xpwm = (x+1)*512   ;   // pwm duty
   
            .........  reajustar el duty cycle
            .........  si sincronizado, mejor.
 
        }
     ......... Demás tareas
     .........................
    }
}


Las operaciones en punto flotante son consumidoras de recursos, por eso no meterlas dentro de la interrupción, pues pueden hacer  "bailar" los tics de 10ms



Saludos.

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #17 en: 08 de Abril de 2018, 11:31:43 »
Hola, llegué a un código basándome en lo que me dijiste para la ecuación en diferencias, asumiendo una f=10Hz y el Ts=ms:

y(k)=1.61y(k-1)-y(k-2)

Código: C
  1. #include <16f877a.h>
  2. #device adc=10
  3. #fuses hs,nowdt,noprotect,noput
  4. #use delay (clock=4M)
  5. #include <math.h>
  6. int1 tic = 0 ;
  7.  
  8. #INT_TIMER1
  9. void tic_10ms()
  10. {
  11.     set_timer1(64911);
  12.     tic = 1 ;
  13. }
  14.  
  15. int16   xpwm    ;  
  16.  
  17. void main(){
  18.  
  19.    set_tris_c(0b000);
  20.    setup_timer_2(T2_DIV_BY_1,199,1);
  21.    setup_ccp1(ccp_pwm);
  22.    enable_interrupts(int_timer1);
  23.    enable_interrupts(global);
  24.     float32 y1 = 1.61 ;  
  25.     float32 y2 = 1   ;
  26.     float32 y;
  27.     int16 q;
  28.     xpwm = (y+2)*512 ;   // pwm duty
  29.  
  30.     while(TRUE)
  31.     {
  32.         if(tic)
  33.         {
  34.             tic = 0 ;
  35.            
  36.             set_adc_channel(0);
  37.             delay_us(20);
  38.             q=read_adc();
  39.             y=q*(1.61*y1)-y2      ;   // ecuacion en diferencias, asumiendo 10Hz
  40.             xpwm = (y+2)*512;// escalización
  41.             set_pwm1_duty(xpwm);
  42.             y1=y;
  43.             y2=y1;
  44.    
  45.            
  46.         }
  47.    
  48.     }
  49. }



Mira la señal que me genera :shock:

Calculé el timer 2 para una frecuencia de 8kHz, no sé que pueda estar asumiendo mal, o no teniendo en cuenta,

Gracias por toda la ayuda que me han brindado  :o :o

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #18 en: 08 de Abril de 2018, 11:36:00 »
Se me olvidaba,

Cuando trato de variar la entrada análoga, el cambio no se evidencia ni siquiera en la siguiente muestra, por lo que me toca hacer stop en la simulación y reiniciar con un valor diferente, ¿qué podrá ser?

Desconectado tsk

  • PIC18
  • ****
  • Mensajes: 258
Re:Conversión digital analoga
« Respuesta #19 en: 08 de Abril de 2018, 12:11:53 »
primero revisa esta parte de tu código

Código: [Seleccionar]
            y1=y;
            y2=y1;

Al final el contenido de y1 y y2 va a ser el  valor actual de y

Código: [Seleccionar]
            y2=y1;
            y1=y;

De esta forma y2 va a tener el valor de y1 y y1 va a tener el valor actual de y

Desconectado Eduardo2

  • PIC24H
  • ******
  • Mensajes: 1004
Re:Conversión digital analoga
« Respuesta #20 en: 08 de Abril de 2018, 14:42:27 »
De momento veo unos cuantos errores

Código: [Seleccionar]
    float32 y1 = 1.61 ;
    float32 y2 = 1   ;

Inicializando así te sale con una amplitud de casi 1.7

Para una amplitud 1 eso debió ser

Código: [Seleccionar]
    float32 y1 = 0.587 ;  // A*seno(2*PI*f*tic)
    float32 y2 = 0     ;  // Cero , pues inicia en 0

También por simplicidad se la puede inicializar con un valor cómodo, ya
que la fase inicial acá no importa.

Código: [Seleccionar]
    float32 y1 = .95   ;  // La amplitud real es ligeramente mayor
    float32 y2 = .95   ;  // que 0.95

En ambos casos, es preferible que la amplitud no esté al límite pues
para valores particulares, al final puede haber overflows.

Como para eso entra en juego el truncamiento/redondeo de las operaciones,
lo mas sencillo es ejecutar el programa y si ocurran overflows -> bajar
la amplitud.


Código: [Seleccionar]
    float32 y;
    int16 q;
    xpwm = (y+2)*512 ;   // pwm duty

Nones, fijate que para y positivos xpwm sale mayor se 1023 (los 10bits)
Para una amplitud menor que 1 debio ser xpwm = (y+1)*512 ; 

Código: [Seleccionar]
    q=read_adc();
    y=q*(1.61*y1)-y2      ;   // ecuacion en diferencias, asumiendo 10Hz
Ahí multiplicando por q, además que no se qué quisiste hacer, destrozás la recurrencia.

Va solamente       y = 1.61*y1 - y2

Si bien no es un error, es preferible un valor mas cercano a 2 (menor frecuencia)  para que el seno esté formado por mayor cantidad de puntos  (ejemplo: 1.95).

Si querés usar el valor del ADC para variar la amplitud lo tenés que hacer en el escalado:  xpwm = q*y/2+512 ;

Si querés usarlo para variar la frecuencia ya hay que recalcular el valor de K y un valor inicial.



Código: [Seleccionar]
    xpwm = (y+2)*512;// escalización
    set_pwm1_duty(xpwm);

De nuevo, el escalado a 10bit:  xpwm = (y+1)*512 ;   
Siempre que la amplitud de "y" sea menor que 1 claro.


Código: [Seleccionar]
    y1=y;
    y2=y1;

Esto ya lo vió tsk ,  debe ser:

Código: [Seleccionar]
    y2 = y1 ;
    y1 = y  ;



Saludos.
« Última modificación: 08 de Abril de 2018, 14:47:28 por Eduardo2 »

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #21 en: 09 de Abril de 2018, 00:27:40 »
Lo que hice fue realizar una tabla de valores para la ecuación en diferencias:

Citar
La recurrencia para un seno es   Xn = k*Xn-1 - Xn-2   ,  n > 1

Siendo
     k = 2*cos(ang)        ,  ang : El ángulo que avanza en cada paso.

Hace de cuenta que partí de esta ecuación, asumiendo una f=10Hz y un Ts=10ms. Como el ángulo es w.Ts=2pi*10*0.01=0.628, por eso, al reemplazar y operar esa k que tienes, se obtiene 1.61, que es el valor inicial que tome. Básicamente lo que observé en la tabla de valores que armé, es que el seno que se forma, tiene una amplitud aproximada a 2, por eso hice la escalización a 2.

Citar
Si querés usar el valor del ADC para variar la amplitud lo tenés que hacer en el escalado:  xpwm = q*y/2+512 ;

Por qué pones y/2?

Hice las modificaciones para hacer la variación en xpwm, y sigue sin mostrarse la variación  en la salida del cpp :/

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #22 en: 09 de Abril de 2018, 00:49:57 »
Sin embargo, hay dos cosas que aun no te entiendo del codigo que me sugeriste a seguir:

Por  qué un if (tic), y declararlo al principio como tic=0, luego como 1, y así?

Por qué me dices que hay que multiplicar por el 512 al xpwm?

Desconectado Eduardo2

  • PIC24H
  • ******
  • Mensajes: 1004
Re:Conversión digital analoga
« Respuesta #23 en: 10 de Abril de 2018, 11:47:43 »
Disculpame la demora en responder.

Lo que hice fue realizar una tabla de valores para la ecuación en diferencias:
..............
Hace de cuenta que partí de esta ecuación, asumiendo una f=10Hz y un Ts=10ms. Como el ángulo es w.Ts=2pi*10*0.01=0.628, por eso, al reemplazar y operar esa k que tienes, se obtiene 1.61, que es el valor inicial que tome. Básicamente lo que observé en la tabla de valores que armé, es que el seno que se forma, tiene una amplitud aproximada a 2, por eso hice la escalización a 2.

Si ok, el inconveniente eran los valores iniciales y1,y2  que parecían puestos al azar y son muy importantes porque te definen la amplitud de la oscilación.


Citar
Citar
Si querés usar el valor del ADC para variar la amplitud lo tenés que hacer en el escalado:  xpwm = q*y/2+512 ;

Por qué pones y/2?

Porque q llega hasta 1023,  y si le sumás 512 te pasás de los 10 bits del PWM.

De todas formas, como vi que estás usando el 16F877  a 4MHz ,  para un duty 100%  el máximo es 800.


Citar
Sin embargo, hay dos cosas que aun no te entiendo del codigo que me sugeriste a seguir:

Por  qué un if (tic), y declararlo al principio como tic=0, luego como 1, y así?

Como las operaciones en punto flotante son consumidoras de tiempo, es preferible que no estén dentro de interrupciones que deben ocurrir a intervalos precisos.  En muchos casos puede no ser necesario, yo lo hice por default   :) .

Lo que hago con el tic es ponerlo en 1 cuando ocurre la interrupcion, de esa manera se entera el bucle en main() y ejecuta los cálculos con todo el tiempo del mundo disponible y sin problemas ante otras interrupciones que podrían ocurrir.
Luego de el if(tic) se pone en 0 para que no se vuelva ejecutar hasta que lo active la interrupción.

Citar
Por qué me dices que hay que multiplicar por el 512 al xpwm?

xpwm debe ser un numero entre 0 y 1023  a 20MHz y entre 0 y 800 a 4MHz

- Si tenés un float entre +/-1  tenés multiplicarlo por la nueva amplitud y sumarle un offset: 
xpwm = y*512 + 512 = (y+1)*512   (para 20MHz)
xpwm = y*400 + 400 = (y+1)*400   (para 4MHz)

- Si querés variar es amplitud con la lectura del ADC (0-1023) tenés que escalarla también:
xpwm = (q/1024) *y*512 + 512 = y*q/2 + 512        (para 20MHz)
xpwm = (q/1024) *y*400 + 400 = y*q/2.56 + 400   (para 4MHz)

--------------------------------------------------------------------------

Te pongo un ejemplo sin la parte de ADC.   
- Genero un seno de 4Hz que luce mejor que de 10Hz.

Código: [Seleccionar]
#include <16f877a.h>
#device adc=10
#fuses hs,nowdt,noprotect,noput
#use delay (clock=4M)

int1    tic = 0 ;
int16   xpwm    ;

#define PERIODO 65536-625

#INT_TIMER1
void tic_10ms()
{
    tic = 1    ;
    set_timer1(PERIODO);
    set_pwm1_duty(xpwm);
}

void main(){

    set_tris_c(0b000);
    setup_timer_1(t1_internal|t1_div_by_8);
    set_timer1(PERIODO);

    setup_timer_2(T2_DIV_BY_1, 199, 1);
    setup_ccp1(CCP_PWM);
    set_pwm1_duty(0);

    enable_interrupts(int_timer1);
    enable_interrupts(global);

    float32 k  = 1.937 ;  // 2*cos(2*PI*f*T)
    float32 y1 = .2363 ;  // Amp*seno(2*PI*f*T)
    float32 y2 = 0     ;  // Amp = .95  f = 4 Hz  T = .01s
    float32 y          ;

    xpwm = (y1+1)*400  ; 
    while(TRUE)
    {
        if(tic)
        {
            tic = 0 ;
            y = k*y1-y2   ;     
            xpwm = (y+1)*400   ;
            y2 = y1 ; y1 = y   ;
        }
    }
}


- Cambié las constantes del filtro RC.  Aún así, le falta un poco.
- Al osciloscopio le dejé dos trazos para marcar 0 y 5V
- Los glitches no estoy seguro si es el simulador que hace mal la parte analógica al no ser en tiempo real o algún problemita raro con el intervalo entre interrupciones.



« Última modificación: 10 de Abril de 2018, 11:54:05 por Eduardo2 »

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #24 en: 10 de Abril de 2018, 15:13:42 »
No hay problema, ya me varía la amplitud con la entrada análoga, sin embargo:

¿por qué asumes 400 para la escalización a 4 MHz, porque es simplemente un valor entre 0 y 800? ¿ahora bien, hay alguna fórmula o expresión que dicte que ese valor tiene que ser entre 0 y 800?

¿por qué en la recurrencia:

Citar
La recurrencia para un seno es   Xn = k*Xn-1 - Xn-2   ,  n > 1

Siendo
     k = 2*cos(ang)        ,  ang : El ángulo que avanza en cada paso.
     X0 = 0
     X1 = A*seno(ang)   ,  A : Amplitud.
 

X1=A*sen(ang)?

Desconectado Eduardo2

  • PIC24H
  • ******
  • Mensajes: 1004
Re:Conversión digital analoga
« Respuesta #25 en: 10 de Abril de 2018, 15:51:37 »
....
¿por qué asumes 400 para la escalización a 4 MHz, porque es simplemente un valor entre 0 y 800? ¿ahora bien, hay alguna fórmula o expresión que dicte que ese valor tiene que ser entre 0 y 800?

Quejas a Microchip  ;-) 
Honestamente no he estudiado profundamente el datasheet, donde seguramente de manera poco clara explican la relación.

Yo lo generé via el PIC Wizard del CCS , en la pestaña Other/Code podés ver el código que va a usar.
Para dutys del 100% te da:  1023 @ 20MHz ,  800 @ 4MHz ,  200 @ 1MHz ....  (Valores que en el Proteus funcionan a la perfeccion).
Los  400 son un offset para aprovechar todo el rango : 
El mínimo en 0 :    -1*400+400 = 0   --> duty = 0%
Y el máximo en 800 :    1*400+400 = 800  --> duty = 100%



Citar
¿por qué en la recurrencia:
................................
X1=A*sen(ang)?

Eso pertenece a la solución de la ecuación de recurrencia.   
De esos dos valores iniciales depende la amplitud y la fase inicial.  Si uno quiere iniciar en valor 0 pues el siguiente debe ser ese, de lo contrario la amplitud será lo que decida Dios.

Tenés otras alternativas de inicialización, como ser en un máximo/míximo, con lo que X1 = X2 = +/-A   ( A: amplitud)

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #26 en: 10 de Abril de 2018, 18:04:47 »
Okay, revisaré el datasheet.

Citar
Xn - 2*cos(a)*Xn-1 + Xn-2 = sin(a)*En-1

Como tal hasta acá entiendo que haces, pero no entiendo porque asumes un X1=A*sin(ang). Hastá aca, lo que el seno esta multiplicando es a la entrada retrasada una muestra, lo que en algun punto va a ser 0 si asumimos una entrada impulso. Por esto no te entiendo porqué asumes que X1 tome ese valor, y luego sea que lo asumas como x(n-1)...  :-/


Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #27 en: 10 de Abril de 2018, 18:59:29 »
Básicamente la ecuación a tomar en cuenta será:

y(k)=1.937y(k-1)-y(k-2)+0.248u(k-1)

Tú en el código asumes que ese 0.248 (multiplicado por 0.95) de u(k-1), sea el valor inicial que tome el y(k-1) y que el y(k-2) sea cero.

Es esta parte que aun no entiendo...

Desconectado Eduardo2

  • PIC24H
  • ******
  • Mensajes: 1004
Re:Conversión digital analoga
« Respuesta #28 en: 10 de Abril de 2018, 22:45:32 »
Okay, revisaré el datasheet.

Citar
Xn - 2*cos(a)*Xn-1 + Xn-2 = sin(a)*En-1

Como tal hasta acá entiendo que haces, pero no entiendo porque asumes un X1=A*sin(ang). Hastá aca, lo que el seno esta multiplicando es a la entrada retrasada una muestra, lo que en algun punto va a ser 0 si asumimos una entrada impulso. Por esto no te entiendo porqué asumes que X1 tome ese valor, y luego sea que lo asumas como x(n-1)...  :-/

Como estás haciendo la antitransformada Z
   Xn = 0   si  n<0
   y  En es un impulso ==>  Vale  1 si n=0  y  0 si n!=0

Entonces

n=0 :   X0 - 2*cos(a)*X-1 + X-2 = sin(a)*E-1   ==>  X0 = 0   pues  X-1 = X-2 = E-1 = 0
n=1 :   X1 - 2*cos(a)*X0 + X-1 = sin(a)*E0     ==>  X1 = sin(a)   pues   X-1 = 0 , X0 = 0 del paso anterior  y  E0 = 1
n=2 :   X2 - 2*cos(a)*X1 + X0 = sin(a)*E1      ==>  X2 =  2*cos(a)*X1 - X0    esto es lo que repetís, pues  En = 0 para n<>0

Desconectado weland98

  • PIC10
  • *
  • Mensajes: 16
Re:Conversión digital analoga
« Respuesta #29 en: 11 de Abril de 2018, 11:29:47 »
AAAAAAAAAH vale, ya sé porqué no le encontraba sentido.

Lo que pasa es que yo asumo que en n=0, x0=1, asumiendo una señal paso. Y de ahí para adelante, obviamente los valores cambian. Por ejemplo, para X1=2cos(a)+sen(a), y asi sucesivamente. ¿te puedo preguntar por qué asumes X-1 = X-2 = E-1 = 0?