# Muestreo de señal seno con el PIC18f2550



## jenn_t (Feb 9, 2009)

hola mis amigos, recurro a  ustedes para unas dudas que me aquejan.

resulta que estoy tratando de enviar una señal seno de 60Hz por USB al  pc mediante LABVIEW, pero cuando la vizualizo, el conversor en LV solo me entrega los valores picos de la señal obteniendo asi solo una señal triangular pero esta no es periodica, ni constante en amplitud. es por esto que quisiera saber que puedo hacer, pues se ve que es un problema de muestreo, porque tengo un filtro antes de la entrada analogica del pic.

quisiera saber como puedo establecer el periodo de muestreo ps  lo unico que hago es  una modificación a este  codigo, llamado PIC usb de J1M, sin embargo lo posteo en su totalidad para que se entienda mejor lo que quiero hacer


```
#include <18F2550.h>
#device ADC=10
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL3,CPUDIV1,VREGEN
#use delay(clock=48000000)

#define USB_HID_DEVICE     FALSE             
#define USB_EP1_TX_ENABLE  USB_ENABLE_BULK   
#define USB_EP1_RX_ENABLE  USB_ENABLE_BULK  
#define USB_EP1_TX_SIZE    1                 
#define USB_EP1_RX_SIZE    3                


#include <pic18_usb.h>     //Microchip PIC18Fxx5x Hardware layer for CCS's PIC USB driver
#include <PicUSB.h>         //Configuración del USB y los descriptores para este dispositivo
#include <usb.c>           //handles usb setup tokens and get descriptor reports

 
#define LEDV    PIN_B6
#define LEDA    PIN_B7
#define LED_ON  output_high
#define LED_OFF output_low

#define modo        recibe[0]
#define param1     recibe[1]
#define param2     recibe[2]
#define resultado   envia[0]


void main(void) {

   int8 recibe[3];                   
   int8 envia[1];
   
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_INTERNAL);
   set_adc_channel(0);
    

   LED_OFF(LEDV);                   
   LED_ON(LEDA);
   
   

   usb_init();                      
   usb_task();                      
   usb_wait_for_enumeration();    
   LED_OFF(LEDA);
   LED_ON(LEDV);                   

   while (TRUE)
   {
      if(usb_enumerated())           
      {
         if (usb_kbhit(1))           
         {
            usb_get_packet(1, recibe, 3);  


            if (modo == 0) // ADC_Mode
            {
            while(True) 
            {
            
            delay_ms(8);
               
             resultado = read_adc();
             
               usb_put_packet(1, envia, 1, USB_DTS_TOGGLE); //enviamos el paquete de tamaño 1byte del EP1 al PC
               
            }
            }

            if (modo == 1) // Modo_Led
            {
               if (param1 == 0) {LED_OFF(LEDV); LED_OFF(LEDA);} 
               if (param1 == 1) {LED_ON(LEDV); LED_OFF(LEDA);} 
               if (param1 == 2) {LED_OFF(LEDV); LED_ON(LEDA);} 
            }
         }
      }
   }
  }
```

este programa lo probe conun voltaje dc y funciono de maravilla, pero ahora que lo intento con uno A.C pasa lo que pasa.

una duda que me aqueja es si tal vez esta función setup_adc(ADC_CLOCK_INTERNAL); pueda ser configurada para obtener un muestreo mas preciso, o si talvez necesito de una herramienta en LV para que la señal se sincronice y sea mas sincronica, o sera que debo almacenar los datos en algun array?, pero en ese caso, como hago eso?. help!


quisiera saber si alguien me puede ayudar con esto, si ese si es el preiodo de muestreo correcto, cuantas muestras como maximo puede tomar el pic,  como puedo configurar el conversor para que trabaje a la frecuencia de muestreo que quiero, o si puedo hacer algo para que envie mas datos no valores locos .... seria de gran ayuda, gracias


----------



## Tratante (Feb 9, 2009)

Hola

No he revisado en detalle tu codigo pero me llama la atensión que tienes un retardo de 8 milisegundos
en el ciclo principal de trabajo, si analizas el periodo de la señal de 60hz es 1/60 = 0.016 ms, es decir, la señal de 60Hz solo tarda 0.016ms en un ciclo completo y estas consumiendo la mitad del tiempo esperando, seguro que no podras muestrear la señal, baja el retardo a 1 ms y obtendras 16 puntos por cada ciclo aprox. en teoria el USB puede enviar facilmente este volumen de datos (serian 1000 muestras por segundo).

Saludos !


----------



## jenn_t (Feb 10, 2009)

hola tratante, gracias por responder, en realidad el periodo de la señal de 60 HZ es de  16ms, no de 0,016mS, yo lo tengo en 8ms por aquello de la frecuencia de muestreo de nyquist en la cual seria 
1/(2*60 )= 8.3mS en todo caso creo que tienes razon, es un tiempo de espera demasiado grande, lo probare a  1ms a ver que pasa, pero sera que al AD del PIC si aguanta este tiempo?.


----------



## Eduardo (Feb 10, 2009)

jenn_t dijo:
			
		

> ... yo lo tengo en 8ms por aquello de la frecuencia de muestreo de nyquist en la cual seria
> 1/(2*60 )= 8.3mS ...


La frecuencia de Nyquist es un limite, si muestrearas exactamente al doble leerias valores iguales de signo alternado (o puros ceros) de acuerdo a la fase de la señal y el muestreo.

Si el objetivo es una visualizacion de la señal --> tenes que usar frecuencias mas altas, de acuerdo a la cantidad de puntos por ciclo que quieras. 
Si antes de la graficacion podes hacer una interpolacion, entonces con frecuencias de solo el 25% superior a la de Nyquist (*Editado:* Lo habia puesto al reves)  no vas a tener dolores de cabeza. Para frecuencias mayores se empieza a disparar la cantidad de puntos necesaria  ( ->infinito para f -> Fnyquist)



Es un error frecuente pensar que la frecuencia de muestreo debe ser ~el doble de la señal, y usar el doble de la frecuencia fundamental cuando debe ser el doble de la maxima armonica de interes (si hay mas altas de pasar por un filtro antialias).
Si por ejemplo tuviera una señal de 60Hz con un poco de distorsion y muestreo 'correctamente' a 150Hz lo unico que puedo reconstruir es una senoide pura (se pierden todas las armonicas)


----------



## jenn_t (Feb 10, 2009)

hola eduardo, gracias por responder, yo quisiera muestrear  para que tomara por lo menos 100 muestras por ciclo, pero es esto posible?, ya veo que nyquist solo funciona en teoria. yo antes de poner la señal al pic, la pongo en un filtro y de ahi si entra al puerto analogo AN(0)


----------



## Dr. Zoidberg (Feb 10, 2009)

jenn_t dijo:
			
		

> hola eduardo, gracias por responder, yo quisiera muestrear  para que tomara por lo menos 100 muestras por ciclo, pero es esto posible?, ya veo que nyquist solo funciona en teoria. yo antes de poner la señal al pic, la pongo en un filtro y de ahi si entra al puerto analogo AN(0)



Quien dice que funciona solo en la teoría? SI eso fuera cierto, no existirían los CD de audio, entre otras cosas. El problema es que si *no* conoces el rango espectral de señal que vas a muestrear, *no puedes aplicar Nyquist (o Shanon)*.
Lo que dice el teorema de Shanon/Nyquist, como te lo explicó _Eduardo_ antes es que la *frecuencia de muestreo debe ser, al menos, el doble de la frecuencia de la mayor componente espectral contenida en la señal*. Por eso es que se usan filtros pasabajos antes de muestrear una señal...para limitar las frecuencias involucradas en la señal a muestrear, como vos dices que vas a hacer ahora.
Pero ahora aparece el otro problema, y es el orden del filtro que tenes que usar. Ningun FPB real elimina todas las frecuencias que caen en la banda de rechazo, sino que la atenuación es función del orden del filtro y en teoría habría que usar un filtro de orden infinito para cortar el espectro frecuencial en el punto deseado, como eso no es posible, tengo que aumentar la frecuencia de muestreo, tanto mas cuanto menor sea el orden del filtro, de manera de asegurarme que solo las señales de alta frecuencia y que esten muy atenuadas son la que pueden  ser iguales a la frecuencia de muestreo.

Saludos!


----------



## jenn_t (Feb 10, 2009)

ahhhh, ya comprendo, muchas gracias Ezavalla, intentare aumentar la frecuencia de muestreo, lo que tu dices es  cierto, el filtro que tengo es de 7mo orden y aun asi no corta perfecto (eso solo lo hacen los ideales), muchas gracias por su colaboración!, estare trayendoles noticias de que ocurrio


----------



## jenn_t (Feb 12, 2009)

Hola amigos, como lo prometido es deuda, pues volví con los resultados de sus consejos, en cuanto al programa del PIC, lo modifique de la siguiente manera  para ver qué opinan  es decir,  si estoy haciendo uso correcto de la interrupción por ccp2.



```
#include <18F2550.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL1,CPUDIV1,VREGEN    
#use delay(clock=48000000)


#define USB_HID_DEVICE     FALSE            
#define USB_EP1_TX_ENABLE  USB_ENABLE_BULK   
#define USB_EP1_RX_ENABLE  USB_ENABLE_BULK  
#define USB_EP1_TX_SIZE    1                 
#define USB_EP1_RX_SIZE    3                 



#include <pic18_usb.h>     
#include <PicUSB.h>         
#include <usb.c>           


#define LEDV    PIN_B0
#define LEDA    PIN_B1
#define PULSO    PIN_B2 // Defino el Pin de  control
#define LED_ON  output_high
#define LED_OFF output_low

#define modo      recibe[0]
#define param1    recibe[1]
#define param2    recibe[2]
#define resultado envia[0]


int1 flagConmuta=0;
int  conver;



#int_ccp2
void handle_ccp2_int(){

  if(++flagConmuta==1){
    setup_ccp2(CCP_COMPARE_CLR_ON_MATCH); 
  } else{
    setup_ccp2(CCP_COMPARE_SET_ON_MATCH); 
  }
  CCP_2=0;                               
  set_timer1(0);                         
  CCP_2 = 41439;                          // Quiero que se dispare cada 2ms en bajo y 2ms en alto...
                                          // luego pongo los Ticks de TMR1 para ese tiempo.
   Output_Toggle(PULSO);                  // pin de control
   conver = Read_ADC();
}


void main(void) {

   int8 recibe[3];                  
   int8 envia[1];
   
  setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
  setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  setup_adc_ports(AN0);
  setup_adc(ADC_CLOCK_INTERNAL);
  set_adc_channel(0);
  setup_ccp2(CCP_COMPARE_SET_ON_MATCH);
  enable_interrupts(int_ccp2);         
 
   LED_OFF(LEDV);                   
   LED_ON(LEDA);
   
   

   usb_init();                     

   usb_task();                   
   usb_wait_for_enumeration();    

   LED_OFF(LEDA);
   LED_ON(LEDV);                 

   while (TRUE)
   {
      if(usb_enumerated())      
      {
         if (usb_kbhit(1))          //si el endpoint de salida contiene datos del host
         {
            usb_get_packet(1, recibe, 3); 

            if (modo == 0) // ADC_Mode
            {
            while(True)
            {
             
             resultado = conver;
             
               usb_put_packet(1, envia, 1, USB_DTS_TOGGLE); 
               
            }
            }

            if (modo == 1) // Modo_Led
            {
               if (param1 == 0) {LED_OFF(LEDV); LED_OFF(LEDA);}
               if (param1 == 1) {LED_ON(LEDV); LED_OFF(LEDA);}
               if (param1 == 2) {LED_OFF(LEDV); LED_ON(LEDA);}
            }
         }
      }
   }
  }
```


Pero lastimosamente los  resultados en labview no fueron los esperados como se puede ver en el archivo adjunto, ahora en vez de tomar una señal triangular solo obtengo una señal bastante irregular, ahora si quede más perdida aun, si supuestamente trabaja en modo bulk no se por qué pareciera que los datos se demoran para llegar al labview.
Otra cosa es que yo puse la interrupción para  4mS, pero la monitoreo mediante un pulso por RB2 y resulta que esta a 6ms, entonces no sé  como calcular  un tiempo exacto en lenguaje C. alguna idea?, gracias por su ayuda muchachos, bye


----------



## jokelnice (Feb 14, 2009)

tengo practicamente el mismo problema que el tuyo la idea es que entre todos podamos o por lo menos entre los dos logremos bien el muesteo, les muestro lo que he hecho la señal tampoCo se ver muy bien pero ha mejorado un poco


----------



## jenn_t (Feb 14, 2009)

pues tu señal se ve un poco mejor que la mia, jejej aunque sea tiene una forma mas definida, tu con que recibes los datos, hiciste un driver con el visa? o como hago yo, mediante el  call library function node?,pienso que talvez mi problema se deba a que hace falta una señal de sincronismo o depronto un buffer, no c, ahi me cuentas


----------



## jokelnice (Feb 14, 2009)

si la comunicacion la hice con el call library ..... y te cuento que al principio se veia muy mal , pude arreglar algo ( no mucho ) logrando enviar el conversor adc a 10 bits ya que solo habia podido con 8 bits ( 1 byte )


----------



## sangreaztk (Feb 15, 2009)

Lo que pasa es que una vez que obtienen las muestras, por lo que veo en las gráficas, usan un retenedor de primer orden. Y para que reconstruyan la señal analógica tendrían que emplear un extrapolador.
Chale, se supone que debería de saber de estas cosas, pero toy bien chavo (novato) jejeje. Tratare de explicar lo que yo entiendo.
Cuando envían las muestras lo que hace LabView es unirlas mediante rectas, si quisieran una señal más parecida a la analógica el numero de muestras tendría que ser numeroso (haciendo más alta la fs -frecuencia de muestreo- o lo que es lo mismo mas pequeño el Ts -periodo de muestreo-), es algo así como el plot de MatLab. Ahora bien manteniendo la condición de ws >= wmax (teorema de Nyquist) para reconstruir la señal pueden ocupar una interpolación ( o era extrapolación ¿? ) como en métodos numéricos, ya saben... interpolación polinomial..... y todos los demás métodos que no me sé, .
Como una imagen vale más que mil palabras, trata de entender la siguiente...







Buena Vibra!


----------



## jenn_t (Feb 15, 2009)

creo que sangreaztk tiene razon no crees jokelnice?, voy a intentarlo en labview a ver que sucede, en cuanto a lo del conversor, como lo configuraste en C, p or que uno manda en por  bytes no es asi?


----------



## jokelnice (Feb 16, 2009)

jenn_t dijo:
			
		

> creo que sangreaztk tiene razon no crees jokelnice?, voy a intentarlo en labview a ver que sucede, en cuanto a lo del conversor, como lo configuraste en C, p or que uno manda en por  bytes no es asi?



como estas jenn_t vamos haber que podemos realizar con lo que explico el compañero ;  en cuanto a la duda del conversor es sencilla y surgio de una duda que tenia ..... yo tenia supuestamente trabajando el conversor del micro a diez bits ( 1024 ) sin embargo cuando hacia la transmisión y visualizaba en la grafica nunca llegaba a este valor , llegaba hasta 256 ( 8 bits ) entonces suena tonto pero despues fue que me di cuenta que leia igual que tu el conversor y lo enviaba con usb_put_packet(1,envia, 1,USB_DTS_TOGGLE) es decir enviaba un solo byte es decir enviaba solo 8 bits la solucion : separar el valor del conversor en parte alta y parte baja msb y lsb y los envie asi y en labview los concatene si asi se puede decir  :

```
set_adc_channel(0);
        delay_us(4);
        dato=read_adc();
        delay_us(10);
        parte_baja =(int)dato;   // separo la parte baja 
        parte_alta =(int)((dato & 0x300)>>8); // separa la parte alta 
         envia[0]=parte_baja;
         envia[1]=parte_alta;
         usb_put_packet(1,envia,2,USB_DTS_TOGGLE); //enviamos el paquete de tamaño 1byte del EP1 al PC**/
```


----------



## luis_e (Feb 16, 2009)

Hace un tiempo atras necesitaba muestrar una señal de ca, para hacer un voltimetro. Para ver lo que entraba al micro hice un programa que leia el adc cada 100us y almacenaba los valores en un array, una vez que llenaba el array, mandaba los datos por el puerto serie, capturaba los datos un programa parecido al hyperterminal y los guardaba en un archivo de texto. Para ver la grafica de la señal pegaba los datos en una plañilla de exel, y agregaba un grafico con esos datos y listo.
No se para que tratan de muestrar la señal, pero a lo mejor con hacer esto les sirve, al menos van a saber si es problema de comunicacion o de muestreo.

Una cosa, para mandarle la señal senoidal al micro, me parece que le tienen que sumar un offset, para que no llegue voltaje negativo al adc, ya que por lo que entendi, no se puede entrar al adc ni se le puede poner como referencia negativa un voltaje menor a vss(creo que se llama asi el pin al que le se conecta el gnd), por lo menos con microchip.


----------



## jokelnice (Feb 18, 2009)

gracias luis e vamos haber si podemos sacar algo de ese manera o por lo menos muy parecida


----------



## jenn_t (Feb 20, 2009)

hola jokelnice y luis como estan?, jaja yo esperando respuesta tuya por que no me enviaron al correo la notificación de que me habian respondido , pero bueno al grano como dijo el dermatologo;

pues fijate que yo hice esto, algo parecido a lo que tu hiciste jokel, definir la variable como entero de 16 bits, que hace lo mismo que tu  haces al dividir lo que lee el conversor en parte alta y baja, pero igualmente no pasa nada, no se si se deba a que tiene mucho retardo el conversor intentare hacerlo como lo tienes tu a ver que suscede

int16 envia[1];


           if (usb_kbhit(1))          //si el endpoint de salida contiene datos del host
         {
            while(True) 
            {
            resultado = Read_ADC();
             delay_ms(1);

            usb_put_packet(1, envia, 2, USB_DTS_TOGGLE);                
            }
            }

otra cosa es que yo visualizo directamente  lo que me llega en un chart, no estoy uniendo los bytes que me llegan, es necesrio unirlos? mi pregunta surje por que pues cuando leo voltajes directos estos salen perfectos igualitos que en el multimetro, pero cuando paso a una señal analogica se ve lo que te mostre anteriormente. entonces surje la duda sera que llega el paquete de 16 completo? o sera que se divide ?, voy a seguir mirando a ver que hacemos, tu como controlas el tiempo de muestreo?, yo lo hago con el modulo CCP2, sera que noesta funcionando?, bueno ya veremos que sucede... gracias por sus aportes


----------



## jokelnice (Feb 23, 2009)

que tal jenn_t yo lo hice de esa manera ya que simplemente pienso de que como todo en el usb se maneja por bytes de hecho la funciion usb_get_packet nos pide cuantos bytes queremos enviar , por eso me surgio la idea de separarlos y enviar cada byte independiente , pero voy a probar de esta manera a ver que tal , en cuanto al control no tengo uno com tal pienso que tendre que implemetarlo , saludos y por aca andaremos mirare los del modulo de comparacion (cpp) haber si hay esta el inconveniente


----------



## jenn_t (Mar 3, 2009)

hola, como vamos?, pues les comento que al finnn pude visualizar la dichosa señal de 60 hz, gracias a l a ayuda de jokelnice, y a un ligero cambio en el  call lybrary node en labview, pero ahora tengo otro inconvenientey no se como  solucionarlo, alguien sabe como puedo hacer para que la señal se visualice asi como la de un osciloscopio?, es decir, que no empiece como a correrse puesto  que  cada 100 muestras el bufer se llena y se envia para asi repetir el proceso, pero esta vez seran otros datos y por eso se  vera el efecto de que se mueve... bueno no se si me entiendan bien.

en pocas palabras alguien sabe como hacer para que la señal se muestre en l os mismos puntos siempre?

gracias pr la ayuda!


----------



## karl87 (Nov 27, 2009)

hola alguien sabe como aumento el numero de muestras en un segundo, pues tengo una señal de 1 khz que simplemete no la visualizo muy bien en ellabview, estoy usando un codigo similar que el posteado aca, pero no logro incrementar mis muestras, saludos y pues alguna idea será gratificada,,,,


----------



## GEORGE747 (Jun 8, 2013)

yo uso java y la grafica la hace bien, no se como funcione bview la verdad es que estoy en esos momento en los que al parecer funciona tu amplicación pero no sabes si realmente funciona jajaja
 yo hice una función que s esta:

void Ejecutar_ADC (void){
   set_adc_channel(0);
   delay_us(10);
   Dato_adc =read_adc();   // Muestrear para mayor estabilidad en la lectura.
   USB_ACD_RA0_A = Dato_adc >> 8;
   USB_ACD_RA0_B = Dato_adc & 0xFF;

   set_adc_channel(1);
   delay_us(10);
   Dato_adc = read_adc();   // Muestrear para mayor estabilidad en la lectura.
   USB_ACD_RA1_A = Dato_adc >> 8;
   USB_ACD_RA1_B = Dato_adc & 0xFF;
}

uso 2 canales ADC espero 10 us en cada convención suponiendo el tiempo que tarda en leer y convertir, mas el tiempo que tarda en seccionar los 10 bits en 2 bytes y guardarlos en las variables
son unos 15 us sobre lectura, en este caso son 2 lecturas 2 canales y la frecuencia de muestreo total se divide sobre 2,
de modo que si asumimos 15 us sobre lectura * 2 lectura(dos canales) 30 us
es apx 33,333,333 muestras por segundo.... osea 33.3333Khz? 
en mi caso la frecuencia mas alta a medir es 500Hz asi que me sobra y me basta, 
ahora para que sea un muestreo mas preciso usar un timer es mejo no creen?
guardar las muestras en un arreglo de bytes de 64 bytes para aprovechar el bulktrasfer 
que como cada conversión con 2 bytes entonces cada vez que la aplicación pida el bufer
al pic este mandará 32 muestras, en mi caso mi aplicación en java pide muestras cada 100 ms
y en la aplicación son seria algo como 3khz de muestreo graficado tomando encuenta el tiempo que tarda en trasformar los bytes en enteros y graficar las coordenadas según mis cálculos en la grafica de mi app se puede dibujar el muestreo a 3khz lo cual basta en mi caso para graficar adecuadamente mi señal que es de max 500Hz


----------

