TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: rarc_357 en 04 de Septiembre de 2009, 11:18:36

Título: Generar Senoidal
Publicado por: rarc_357 en 04 de Septiembre de 2009, 11:18:36
Necesito generar ondas senoidales de diferentes frecuencias, (10-15Khz) para lo cual utilizaré tablas lookup y un DAC para obtener la salida analógica de la señal. En CCS existe este ejemplo:


BYTE CONST SINE_WAVE[200] = {
128,132,136,139,143,147,150,154,158,161,165,169,172,176,179,
182,186,189,192,195,199,202,204,207,210,213,215,218,220,223,
225,227,229,231,233,235,237,238,240,241,242,243,244,245,246,
247,247,247,248,248,248,248,248,247,247,247,246,245,244,243,
242,241,240,238,237,235,233,231,229,227,225,223,220,218,215,
213,210,207,204,202,199,195,192,189,186,182,179,176,172,169,
165,161,158,154,150,147,143,139,136,132,128,124,120,117,113,
109,106,102,98,95,91,87,84,80,77,74,70,67,64,61,57,54,52,49,
46,43,41,38,36,33,31,29,27,25,23,21,19,18,16,15,14,13,12,11,
10,9,9,9,8,8,8,8,8,9,9,9,10,11,12,13,14,15,16,18,19,21,23,
25,27,29,31,33,36,38,41,43,46,49,52,54,57,61,64,67,70,74,77,
80,84,87,91,95,98,102,106,109,113,117,120,124};

BYTE sine_index;

#int_rtcc
void isr() {

   set_rtcc(102);         // frequency of interrrupt = (clock/(4*divisor))/(256-reload)
                          //                 2029 hz = (20000000/(4*16))/(256-102)

   write_dac(SINE_WAVE[sine_index]);

   if(++sine_index==200) {
      sine_index=0;
   }
}


void main() {

  setup_counters( RTCC_INTERNAL, RTCC_DIV_16);
  enable_interrupts(INT_RTCC);
  enable_interrupts(GLOBAL);

  while (TRUE) ;
}

La tabla ya está calculada y tiene 200 muestras, mi duda es como generar esa tabla, y creo q a mayor frecuencias quizá necesite menos muestras, tampoco me queda claro como puedo hacer variar la frecuencia (este ejemplo es a 10Hz)
Título: Re: Generar Senoidal
Publicado por: Suky en 04 de Septiembre de 2009, 11:46:25
Para generar la tabla en muy sencillo, una calculadora y listo  :mrgreen: Y=127.5+ 127.5*sen(2*Pi*f*t) donde t dependerá de la velocidad de muestreo. Igual lo que puedes hacer es hacer una sola tabla de cierta cantidad de pasos, y por la frecuencia que quieres generar mas de 20 no creo que puedas usar. Y después para reproducir las distintas frecuencias lo único que haces es variar el tiempo entre envío de muestras.



Saludos!
Título: Re: Generar Senoidal
Publicado por: gera en 04 de Septiembre de 2009, 12:04:57
Y por q no usas la funcion sin() de la libreria math.h?
Yo una vez haciendo un filtro digital hice un oscilador sin querer jeje, pero no guarde el codigo :(
saludos!
Título: Re: Generar Senoidal
Publicado por: Suky en 04 de Septiembre de 2009, 12:24:25
Y por q no usas la funcion sin() de la libreria math.h?
Yo una vez haciendo un filtro digital hice un oscilador sin querer jeje, pero no guarde el codigo :(
saludos!
Noooo! ocupa muchas instrucciones, es imposible hacer algo 10kHz con eso  :?
Título: Re: Generar Senoidal
Publicado por: migsantiago en 04 de Septiembre de 2009, 12:53:59
Acabo de probar la generación de una senoidal con puras funciones flotantes y Suky tiene razón, un PIC18 corriendo a 40MHz (10MIPS) no puede ser tan rápido...

Código: [Seleccionar]
#include <18f452.h>
#use delay(clock=40000000)

#include <math.h>

void main(void)
{
float t;
const float p = 0.00005; //muestreo requerido de 20kHz
float f = 10000.0; //frecuencia senoidal
const float dos_pi = 6.2831;
int8 salf;
int8 salida;

while(1)
   {
   salf = 255.0 * sin(dos_pi * f * t);
   salida = (int8)salf; //aquí falta desplazar la muestra a polaridad positiva pero bueno XD
   t += p;
   if(t>1.0)
      t=0;
   }
  
}

El bloque del while(1) consume 653us aproximadamente en calcular el seno, multiplicar por la amplitud de 255, hacer el casting a entero e incrementar el tiempo.

Si cada 0.65ms se puede conocer una muestra senoidal, la frecuencia en tiempo real sería de 1,538Hz aproximadamente... muy por debajo de los 15kHz que necesita rarc.

Para hacer la tabla de constantes que está arriba se puede usar excel o cualquier otra cosa siguiendo la fórmula de mi ejemplo, pero al modificar la frecuencia de la senoidal o el valor de t la tabla resulta inútil porque todos sus valores cambian.

Lo bueno es que los PIC18 tienen mucha memoria de programa y puedes llenarlas con cuantas tablas de frecuencias necesites.
Título: Re: Generar Senoidal
Publicado por: gera en 04 de Septiembre de 2009, 13:11:58
hmm, es cierto.. entonces con tabla sera. Aunq pienso q tiene q haber una forma mas eficaz.
saludos!!
Título: Re: Generar Senoidal
Publicado por: Suky en 04 de Septiembre de 2009, 13:19:35
Con un PIC a esa frecuencia te ves muy limitado  :tongue:
Título: Re: Generar Senoidal
Publicado por: RICHI777 en 04 de Septiembre de 2009, 13:20:21
Hola, los metodos de tabla con valores precalculados son los metodos mas eficaces que existen, nada es mas rapido que indexar y tomar el valor de una posicion de memoria.

Saludos !
Título: Re: Generar Senoidal
Publicado por: Suky en 04 de Septiembre de 2009, 13:26:38
Exacto, y yo crearía una única tabla de 256 valores, para usar un "puntero" de 8 bits para no usar esta rutina:

Código: C
  1. if(++sine_index==200) {
  2.       sine_index=0;
  3.    }

por ejemplo que le quita rendimiento, aparte de no usar interrupción  :undecided:

Saludos!
Título: Re: Generar Senoidal
Publicado por: PalitroqueZ en 04 de Septiembre de 2009, 13:27:45
Con tablas es una forma muy rapida, los chips de analog Devices usan tablas ROM super rapidas

(http://www.todopic.com.ar/foros/index.php?action=dlattach;topic=27436.0;attach=10034)

fuente: 450968421DDS_Tutorial_rev12-2-99.pdf

Título: Re: Generar Senoidal
Publicado por: migsantiago en 04 de Septiembre de 2009, 13:33:03
Exacto, y yo crearía una única tabla de 256 valores, para usar un "puntero" de 8 bits para no usar esta rutina:

Suky, ¿pero cómo modificarías la frecuencia y el periodo de muestreo si la tabla se calcula con f y t constantes? Según entiendo esa tabla de 256 valores sería a partir de una f y una t, pero al cambiarlas la tabla dejaría de servir.

Tendría que tener muchas tablas, cada una con un par de f y t.  :?:
Título: Re: Generar Senoidal
Publicado por: Suky en 04 de Septiembre de 2009, 13:37:53
Exacto, y yo crearía una única tabla de 256 valores, para usar un "puntero" de 8 bits para no usar esta rutina:

Suky, ¿pero cómo modificarías la frecuencia y el periodo de muestreo si la tabla se calcula con f y t constantes? Según entiendo esa tabla de 256 valores sería a partir de una f y una t, pero al cambiarlas la tabla dejaría de servir.

Tendría que tener muchas tablas, cada una con un par de f y t.  :?:
Los niveles de tensión son exactamente los mismos, si siempre muestreo por ejemplo con 16 muestras por periodo de la señal, claro esta que la frecuencia de muestreo va a variar en cada caso, pero es una solución rápida dentro de un pequeño rango de frecuencias. Después para la generación lo único que se varia es el tiempo en presentar las muestras.
Título: Re: Generar Senoidal
Publicado por: migsantiago en 04 de Septiembre de 2009, 13:39:50
Me has convencido, tienes razón. Es solo cuestión de estirar o comprimir la senoidal... incluso se podría generar solo el semiciclo positivo y negar el otro.  :mrgreen:
Título: Re: Generar Senoidal
Publicado por: Suky en 04 de Septiembre de 2009, 13:43:24
Me has convencido, tienes razón. Es solo cuestión de estirar o comprimir la senoidal... incluso se podría generar solo el semiciclo positivo y negar el otro.  :mrgreen:
Claro a eso me refería, estirar o comprimir la señal. Eso para un determinado rango de frecuencias, porque para generar 50 Hz con 16 muestras creo que tiene muy poca resolución  :undecided:
Título: Re: Generar Senoidal
Publicado por: migsantiago en 04 de Septiembre de 2009, 13:48:25
Sí, por ejemplo generas una tabla de 16 muestras para una señal rápida como de 1kHz pero luego generas una tabla de 128 muestras para una de 50Hz.  :mrgreen:
Título: Re: Generar Senoidal
Publicado por: rarc_357 en 05 de Octubre de 2009, 14:49:17
ok ok he estado trabajando y desde esa fecha para aca muchas cosas me han quedado claras
Efectivamente estoy utilizando la tabla de look-up para generar la onda senoidal, ya he podido obtener una salida, con un dac0800, q por cierto me quitó algo de tiempo echarlo a andar, (errores tontos) y si, logro dibujar una senoidal, pero a frecuncia muy baja!!!, y pensando, vi q no es necesario o al menos eso pienso, utilizar la interrupción de overflow del rtcc para ir colocando las muestras.

Pienso dejar una sola tabla, y no colocar muchas, pues pretendo hacer un barrido d frecuencias, entonces, mejor modificaré el tiempo entre muestra y muestra con un retraso
asi puedo controlar el tiempo q lleva escribir todas las muestras, q correspondería al periodo.

peeeero al modificar el programa ahora ya no tengo salidas en PORTB, no se específicamente cuál sea el error. este es el código nuevo.

#include <18F452.h>
#fuses HS,NOWDT,NOPROTECT
#use delay(clock=4000000)

BYTE CONST SINE_WAVE[256] = {
128,131,134,137,140,144,147,150,153,156,159,162,165,168,171,174,
177,179,182,185,187,191,193,196,199,201,204,206,209,211,213,216,
218,220,222,224,226,228,230,232,233,235,237,239,240,241,243,244,
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
245,244,243,241,240,239,237,235,234,232,230,228,226,224,222,220,
218,216,213,211,209,206,203,201,199,196,193,191,188,185,182,179,
177,174,171,168,165,162,159,156,153,150,147,144,140,137,134,131,
128,125,122,119,116,112,109,106,103,100,97,94,91,88,85,82,79,76,
74,71,68,65,63,60,57,54,52,49,47,45,43,40,38,36,34,32,30,28,26,
24,22,21,19,17,16,15,13,12,11,10,8,7,6,6,5,4,3,3,2,1,1,1,1,1,1,
1,1,1,2,2,2,3,3,4,5,6,6,7,8,10,11,12,13,15,16,17,19,21,24,26,28,
30,32,34,36,38,40,43,45,47,50,52,55,57,60,63,65,68,71,74,77,79,
82,85,88,91,94,97,100,103,106,109,112,116,119,122,125,128};

BYTE sine_index;




void main() {

output_B(SINE_WAVE[sine_index]);

delay_ms(10);

   if(++sine_index==256) {
      sine_index=0;
 }
while (true);
}


Despues haré variable el retraso, controlado por un botón externo. Pero en este momento no logro ver salida en portB

Primero pretendo obtenr la máxima frecuencia posible con este pic, y cuento con osciladores XT de hasta 40Mhz

Saludos a todos los q han comentado
Título: Re: Generar Senoidal
Publicado por: Suky en 05 de Octubre de 2009, 15:10:43
El 452 creo que la máxima velocidad que soporta es de 32MHz con PLL, revisa eso. Ahora ese código que has colocado no va hacer nada, solamente va a mostrar un valor de la tabla y nada mas, debería ser así:

Código: C
  1. while(1){
  2.      output_B(SINE_WAVE[sine_index++]);
  3.      delay_ms(xx); // o delay_us(xx);
  4. }

Igual con una tabla de tantos valores mucha velocidad no vas a lograr, revisa todo lo que hemos comentado  :?


Saludos!
Título: Re: Generar Senoidal
Publicado por: rarc_357 en 13 de Octubre de 2009, 15:28:04
OK quiero decirles que todo va bien, gracias por su ayuda.
He podido generar una senoidal de forma exitosa, incluso he podido hacer variar la frecuencia utilizando una rutina de interrupción en B
que lo que hace es hacer variable un delay que coloco entre muestra y muestra de la tabla de look-up.

Lamentablemente, con o sin retrasos, solo he podido llegar hasta 1.2Khz con un clock a 16Mhz (por cierto ¿cuál es el clock maximo q han utilizado con un 18F452?)

La tabla la he disminuido a 200 muestras, se que puedo disminuirla mas, Pero:

Alguien me puede bajar de mi nube y decirme q jamás llegaré a frecuencias de 15-20Khz  :shock:

Saludos desde Veracruz, México.


Pronto compartiré el proyecto, si les viene bien  8)
Título: Re: Generar Senoidal
Publicado por: migsantiago en 13 de Octubre de 2009, 19:43:34
Lamentablemente, con o sin retrasos, solo he podido llegar hasta 1.2Khz con un clock a 16Mhz (por cierto ¿cuál es el clock maximo q han utilizado con un 18F452?)

Hola, yo he usado el PIC18F452 con un cristal de 10MHz y un PLL que lo eleva a 40MHz.

Edito en fuses para usar un xtal de 10MHz y elevarlo a 4MHz debes usar:

Código: [Seleccionar]
#fuses h4
Título: Re: Generar Senoidal
Publicado por: Suky en 13 de Octubre de 2009, 21:00:49
Utilizando PLL, llegas a 40MHz, osea un ciclo de trabajo de 100ns. La rutina para cargar el valor podría ser la siguiente:

Código: C
  1. while(Signal==1){
  2.     output_b(Tabla[i++]);
  3. }

con Tabla de 256 valores, configurando #use fast_io(b) vemos que el asm generado es:

Código: ASM
  1. ....................    while(Signal==1){
  2. 0170:  MOVLB  1
  3. 0172:  BTFSS  x1C.0
  4. 0174:  BRA    0190
  5. ....................       output_b(Tabla[i++]);    
  6. 0176:  MOVF   x1E,W
  7. 0178:  INCF   x1E,F
  8. 017A:  CLRF   03
  9. 017C:  ADDLW  1B
  10. 017E:  MOVWF  FE9
  11. 0180:  MOVLW  00
  12. 0182:  ADDWFC 03,W
  13. 0184:  MOVWF  FEA
  14. 0186:  MOVFF  FEF,125
  15. 018A:  MOVFF  125,F8A
  16. ....................    }
  17. 018E:  BRA    0172

Así que sin ningún tipo de demora el ciclo ocurrirá cada 14 instrucciones. Si nuestra tabla contiene 16 ciclos de la señal con 16 valores por ciclo, la frecuencia de la señal generada será:

14x100nsx16=22.4us-> 44.6kHz.- Así que se puede llegar a generar lo que necesitas ;-)


Saludos!
Título: Re: Generar Senoidal
Publicado por: jgpeiro06 en 15 de Octubre de 2009, 06:49:42
Conozco otras dos maneras de generar una onda senoidal que no son tan rapidas como la busqueda en una tabla, pero que pueden ser suficientes para.
    -El metodo de CORDIC, que calcula el sin(x) utilizando la busqueda binaria sobre una tabla de arctg. http://es.wikipedia.org/wiki/CORDIC
    -La transformada Z inversa de la transformada Z de la funcion sin(t) es una ecuacion recursiva. Pârtiendo de la transformada Z de la funcion sin(w0n)u(n) que aparece en http://es.wikipedia.org/wiki/Transformada_Z y operando obtenemos y[n] = x[n-1]*sin(w0) + y[n-1]*2*cos(w0) - y[n-2]. Si x[n] es una delta de dirac, y[n] será sin(w0*n). La funcion delta de dirac en tiempo discreto esta definida d[n] = 1 si n == 0 o 0 si n != 0.
Título: Re: Generar Senoidal
Publicado por: fuente en 15 de Octubre de 2009, 08:33:41
En www.bitscope.com, en la seccion de Downloads, hay un archivo que se llama wavePOD.zip.
Ahí tienen un generador de funciones con un 16F84, con el código asm bastante bien explicado, así como el esquema.

Por cierto y por si le interesa a alguien, hace como unos 7 años compré un osciloscopio de estos, cuando todavía te los mandaban en kit y te los tenías que montar y ajustar, y la verdad es que va bastante bien para el uso que le doy en casa. El mío va por puerto serie, así que los que tienen ahora por USB o ethernet tienen que ir de perlas.

Saludos.
Título: Re: Generar Senoidal
Publicado por: matyvico en 02 de Febrero de 2013, 15:38:00
Disculpen por revivir este post, pero estuve buscando por todo el foro sobre la generación de una función senoidal y este fué el post más completo (y cercano a lo que yo busco).
Yo estoy (en este (http://www.todopic.com.ar/foros/index.php?topic=40063.0) post) tratando de que entre todos armemos un generador de señales, hasta ahora funciona todo bastante bien, el inconveniente es generar una función senoidal (o cualquier otra en sí) de frecuencia y amplitud variables (así como ciclo de trabajo en la cuadrada, ect).

No discuto que la forma mas rapida de mostrar valores es leyendo la tabla y poniendo ese valor en el puerto (usando fast_io también), pero lo que simpre vi fueron discusiones sobre tablas CONSTANTES, no vi en ningún lugar que nadie planteara la idea de primero (en base a parámetros elegidos por el usuario como amplitud, y frecuencia) generar una tabla haciendo uso por ejemplo de la función sin(); de math.h y luego en otro loop hacer la muestra común y corriente leyendo rapidísimo desde esta tabla recién generada y no de una tabla constante. He tratado de implementar esto que digo pero no logro resultados, aunque estoy seguro que el problema es con el tipo de datos usado en alguna variable, un casting o algo por el estilo, mi idea es así:

Código: [Seleccionar]
#INCLUDE <math.h>

    static int salida[200]={};
    float temp;
    float incremento=((2*pi)/199);
    float fase;
    i=199;
    x=0;

    //SEÑAL SENOIDAL
        case 3:
        {
            for(fase=0;fase<=(2*pi);fase+=incremento)
            {
                 temp=ceil((0.5*sin(fase)+0.5)*255.0);
                 strcpy(salida[x],(int)temp);
                 x++;
            }
            while(salir)
            {
                delay_ms(1);
                output_d(salida[i]);
                i--;
                if(i==0){i=199;}
            }
            break;
        }

Eso es parte de un programa que actualmente estoy desarrollando para el generador del post que les puse mas arriba, estoy probando con eso para generar la tabla del seno desde calculos hechos por el mismo pic, luego si hago funcionar eso, implementar una modificación de la amplitud sería tan simple (supongo) como modificar el multiplicador en frente a la función sin(), y su frecuencia en función de incrementar o disminuir el argumento de un delay... Pero como dije, si hago lo que puse arriba, solo obtengo que el array salida[] se llene de vez en cuando en alguna de las 200 posiciones con algún valor, y en el resto solo tengo caracteres nulos...

Si me pudieran tirar alguna pista estaría muy agradecido! Muchas gracias por adelantado!
Título: Re: Generar Senoidal
Publicado por: AngelGris en 03 de Febrero de 2013, 14:46:16
  Hice el siguiente ejemplo en XC8, en el cual almaceno valores en una tabla. Cuando el pin B0 está en estado bajo, la tabla se llena con valores correspondientes a la función seno. Si está en estado alto, se llena con valores correspondientes a función triangular.

  Variando el delay dentro del ciclo for se puede cambiar la frecuencia. Me encuentro con un inconvenientes y es que según la frecuencia o la forma de onda, debo cambiar el filtro pasabajos colocado al final del dar R-2R. El filtro está colocado directamente sobre la salida del pic y tal vez sería mejor colocar un operacional y luego el filtro.

  El programa está hecho para un PIC16F886 corriendo a 20MHz. Si saco el delay del ciclo for obtengo una máxima frecuencia de aproximadamente 16KHz. Me imagino que con un 18F2550 corriendo a 48MHz se podrá lograr mucha mayor frecuencia de salida. Incluso quizá se pueda lograr aumentar el número de valores de la tabla.

Programa:

Código: C
  1. #include <xc.h>
  2.  
  3. #pragma config FOSC = HS, MCLRE = OFF, WDTE = OFF
  4. #pragma config BOREN = OFF, IESO = OFF, FCMEN = OFF
  5.  
  6. #define _XTAL_FREQ 20000000
  7.  
  8. unsigned char tabla[16];
  9. unsigned char indice;
  10.  
  11. void main(void)
  12. {
  13.     ANSEL = 0;
  14.     ANSELH = 0;
  15.     TRISB = 255;
  16.     TRISC = 0;
  17.     PORTC = 0;
  18.  
  19.     if (PORTBbits.RB0 == 0)
  20.     {
  21.       tabla[0] = 100;
  22.       tabla[1] = 138;
  23.       tabla[2] = 170;
  24.       tabla[3] = 192;
  25.       tabla[4] = 200;
  26.       tabla[5] = 192;
  27.       tabla[6] = 170;
  28.       tabla[7] = 138;
  29.       tabla[8] = 100;
  30.       tabla[9] = 62;
  31.       tabla[10] = 30;
  32.       tabla[11] = 8;
  33.       tabla [12] = 0;
  34.       tabla[13] = 8;
  35.       tabla[14] = 30;
  36.       tabla[15] = 62;
  37.     }
  38.     else
  39.     {
  40.       tabla[0] = 100;
  41.       tabla[1] = 125;
  42.       tabla[2] = 150;
  43.       tabla[3] = 175;
  44.       tabla[4] = 200;
  45.       tabla[5] = 175;
  46.       tabla[6] = 150;
  47.       tabla[7] = 125;
  48.       tabla[8] = 100;
  49.       tabla[9] = 75;
  50.       tabla[10] = 50;
  51.       tabla[11] = 25;
  52.       tabla [12] = 0;
  53.       tabla[13] = 25;
  54.       tabla[14] = 50;
  55.       tabla[15] = 75;
  56.     }
  57.  
  58.     while(1)
  59.     {
  60.         for (indice = 0; indice < 16; indice++)
  61.         {
  62.             __delay_us (100);
  63.             PORTC = tabla[indice];
  64.         }
  65.     }
  66. }

Dejo la captura del osciloscopio de proteus.