# Analizador de espectro con LabVIEW y PIC



## RockKaiser2 (Oct 17, 2011)

Saludos! He logrado crear una interfaz gráfica en LabVIEW para realizar análisis espectral a señales adquiridas mediante un PIC18F4550. He aquí los detalles:


```
#include <18F4550.h>
#DEVICE ADC=8
#fuses HSPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)



#define USB_HID_DEVICE     FALSE             //deshabilitamos el uso de las directivas HID
#define USB_EP1_TX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for IN bulk/interrupt transfers
#define USB_EP1_RX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers
#define USB_EP1_TX_SIZE    1                //size to allocate for the tx endpoint 1 buffer
#define USB_EP1_RX_SIZE    2                 //size to allocate for the rx endpoint 1 buffer

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


#define modo      recibe[0]
#define param     recibe[1]
#define voltaje   envia[0]


int8 envia[1];


void main(void) 
{
   setup_timer_0(RTCC_INTERNAL);   
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   disable_interrupts(GLOBAL);     

   usb_init();                     
   usb_task();                     
   usb_wait_for_enumeration();      
     
   setup_port_a( ALL_ANALOG );      
   #asm                                               
    movlw 0b00010101 ;justificacion_izquierda,4Tad,16Tosc
    iorwf 0xFC0,1 ; direccion de ADCON2        
   #endasm                               
   setup_adc(ADC_CLOCK_DIV_16);  
   set_adc_channel(0);
  
   while (usb_enumerated())
   {
     delay_us(4);        
     voltaje=read_adc();
     delay_us(5); 
     usb_put_packet(1,envia,1,USB_DTS_TOGGLE);
     output_toggle(PIN_B1);
   }  
}
```

Tomo la señal por el puerto AN0 del PIC, y luego mando esa medición hacia LabVIEW usando el hardware:







Claro, a la entrada debe llevar un circuito acondicionador de señal, como un amplificador de audio para el micrófono, o un filtro anti-aliasing...

Pero en el include, observen: *#include "USB_BULK.h"
*

En ese archivo de cabecera se configuran los descriptores del USB del PIC:


```
#IFNDEF __USB_DESCRIPTORS__
#DEFINE __USB_DESCRIPTORS__

#include <usb.h>

//////////////////////////////////////////////////////////////////
///
/// start config descriptor
/// right now we only support one configuration descriptor.
/// the config, interface, class, and endpoint goes into this array.
///
//////////////////////////////////////////////////////////////////

#DEFINE USB_TOTAL_CONFIG_LEN 32 //config+interface+class+endpoint

//configuration descriptor
char const USB_CONFIG_DESC[] = {
//config_descriptor for config index 1
USB_DESC_CONFIG_LEN, //length of descriptor size
USB_DESC_CONFIG_TYPE, //constant CONFIGURATION (0x02)
USB_TOTAL_CONFIG_LEN,0, //size of all data returned for this config
1, //number of interfaces this device supports
0x01, //identifier for this configuration. (IF we had more than one configurations)
0x00, //index of string descriptor for this configuration
0xC0, //bit 6=1 if self powered, bit 5=1 if supports remote wakeup (we don't), bits 0-4 reserved and bit7=1
0x32, //maximum bus power required (maximum milliamperes/2) (0x32 = 100mA)

//interface descriptor 0 alt 0
USB_DESC_INTERFACE_LEN, //length of descriptor
USB_DESC_INTERFACE_TYPE, //constant INTERFACE (0x04)
0x00, //number defining this interface (IF we had more than one interface)
0x00, //alternate setting
2, //number of endpoints, not counting endpoint 0.
0xFF, //class code, FF = vendor defined
0xFF, //subclass code, FF = vendor
0xFF, //protocol code, FF = vendor
0x00, //index of string descriptor for interface

//endpoint descriptor
USB_DESC_ENDPOINT_LEN, //length of descriptor
USB_DESC_ENDPOINT_TYPE, //constant ENDPOINT (0x05)
0x81, //endpoint number and direction (0x81 = EP1 IN)
0x02, //transfer type supported (0 is control, 1 is iso, 2 is bulk, 3 is interrupt)
USB_EP1_TX_SIZE,0x00, //maximum packet size supported
0x01, //polling interval in ms. (for interrupt transfers ONLY)

//endpoint descriptor
USB_DESC_ENDPOINT_LEN, //length of descriptor
USB_DESC_ENDPOINT_TYPE, //constant ENDPOINT (0x05)
0x01, //endpoint number and direction (0x01 = EP1 OUT)
0x02, //transfer type supported (0 is control, 1 is iso, 2 is bulk, 3 is interrupt)
USB_EP1_RX_SIZE,0x00, //maximum packet size supported
0x01, //polling interval in ms. (for interrupt transfers ONLY)

};

//****** BEGIN CONFIG DESCRIPTOR LOOKUP TABLES ********
//since we can't make pointers to constants in certain pic16s, this is an offset table to find
// a specific descriptor in the above table.

//NOTE: DO TO A LIMITATION OF THE CCS CODE, ALL HID INTERFACES MUST START AT 0 AND BE SEQUENTIAL
// FOR EXAMPLE, IF YOU HAVE 2 HID INTERFACES THEY MUST BE INTERFACE 0 AND INTERFACE 1
#define USB_NUM_HID_INTERFACES 0

//the maximum number of interfaces seen on any config
//for example, if config 1 has 1 interface and config 2 has 2 interfaces you must define this as 2
#define USB_MAX_NUM_INTERFACES 1

//define how many interfaces there are per config. [0] is the first config, etc.
const char USB_NUM_INTERFACES[USB_NUM_CONFIGURATIONS]={1};

#if (sizeof(USB_CONFIG_DESC) != USB_TOTAL_CONFIG_LEN)
#error USB_TOTAL_CONFIG_LEN not defined correctly
#endif


//////////////////////////////////////////////////////////////////
///
/// start device descriptors
///
//////////////////////////////////////////////////////////////////

//device descriptor
char const USB_DEVICE_DESC[] ={
USB_DESC_DEVICE_LEN, //the length of this report
0x01, //constant DEVICE (0x01)
0x10,0x01, //usb version in bcd
0x00, //class code (if 0, interface defines class. FF is vendor defined)
0x00, //subclass code
0x00, //protocol code
USB_MAX_EP0_PACKET_LENGTH, //max packet size for endpoint 0. (SLOW SPEED SPECIFIES 8)
[B]0xD8,0x04, //vendor id (0x04D8 is Microchip)
0x10,0x00, //product id[/B]
0x01,0x00, //device release number
0x01, //index of string description of manufacturer. therefore we point to string_1 array (see below)
0x02, //index of string descriptor of the product
0x00, //index of string descriptor of serial number
USB_NUM_CONFIGURATIONS //number of possible configurations
};


//////////////////////////////////////////////////////////////////
///
/// start string descriptors
/// String 0 is a special language string, and must be defined. People in U.S.A. can leave this alone.
///
/// You must define the length else get_next_string_character() will not see the string
/// Current code only supports 10 strings (0 thru 9)
///
//////////////////////////////////////////////////////////////////

//the offset of the starting location of each string.
//offset[0] is the start of string 0, offset[1] is the start of string 1, etc.
const char USB_STRING_DESC_OFFSET[]={0,4,12};

#define USB_STRING_DESC_COUNT sizeof(USB_STRING_DESC_OFFSET)

char const USB_STRING_DESC[]={
//string 0
4, //length of string index
USB_DESC_STRING_TYPE, //descriptor type 0x03 (STRING)
0x09,0x04, //Microsoft Defined for US-English
//string 1 --> la compañia del producto ???
8, //length of string index
USB_DESC_STRING_TYPE, //descriptor type 0x03 (STRING)
'U',0,
'S',0,
'B',0,
//string 2 --> nombre del dispositivo
28, //length of string index
USB_DESC_STRING_TYPE, //descriptor type 0x03 (STRING)
'P',0,
'G',0,
'E',0,
' ',0,
'7',0,
'4',0,
'4',0,
'5',0,
' ',0,
'-',0,
' ',0,
'1',0,
'1',0
};
#ENDIF
```

En negritas marco el VID y PID del USB del PIC. (VID: 04D8 para Microchip/PID: 0010, por monerías mías)

Una vez en LabVIEW, se procesa el dato de la siguiente forma:






El driver para que LabVIEW reconozca el dispositivo USB del PIC debe ser generado mediante la aplicación Driver Wizard del NI VISA, sólo hay que indicar el VID y PID del PICUSB.

Bueno, la única limitante es que sólo puede muestrear a 500Hz. Mi idea era hacerlo trabajar para analizar señales de hasta 20KHz, aún trabajo en eso...

Si tienen alguna idea con respecto a eso, bienvenidas las sugerencias!


----------



## juanma2468 (Oct 17, 2011)

Muy bueno el proyecto, o sea que con lo que tienes hasta el momento, solo puedes ingresar una señal de 250Hz o menos. El muestreo lo haces para ver en la computadora en tiempo real??. Te doy algunas ideas para mejorarlo, podrias aumentar tu frecuencia de muestreo e ir almacenando los datos en una memoria, pero ten en cuenta que un exceso en el muestreo trae una elevada cantidad de muestras, por consiguiente mucha memoria para poder almacenar tanta informacion, puedes manejar alguna SD talves. Otra opcion un poco mas complicada es trabajar con submuestreo, veras una version realentizada o de menor frecuencia a la señal original, pero si sabes cuanto es la frecuencia de la señal original podras saber toda su informacion, por supuesto tendras aliasing pero si los espectros se encuantran lo sufucientemente alejados uno de otro podras tener una idea del espectro.


----------



## RockKaiser2 (Oct 19, 2011)

Los datos se mandan en tiempo real, y hasta ahora sólo se pueden ver componentes espectrales de señales de hasta 250Hz.

Los de la memoria lo he considerado, y pronto investigaré acerca de ello.

Y en cuanto al submuestreo, esa no es la idea, ya que se producen errores de lectura e interpretación, y este proyecto está ideado más que todo con fines didácticos para estudiantes de telecomunicaciones, por lo que sería bueno alcanzar mayores frecuencias, como por ejemplo poder observar tranquilamente componentes espectrales de hasta 20KHz.

También he pensado en usar un MCP3002 en conjunto con una memoria externa, pero no tengo ninguna experiencia en cuanto a interfaz SPI.


----------



## juanma2468 (Oct 20, 2011)

Una duda la limitacion de la frecuancia de muestreo la tenes por parte del PIC o del programa LabVIEW?, saludos


----------



## RockKaiser2 (Oct 20, 2011)

La limitación en el muestreo viene del PIC. Sospecho que estoy haciendo algo mal, pero no se decir aún cuál es el error...


----------



## juanma2468 (Oct 20, 2011)

Por lo general la frecuencia del conversor analogico/digital suele ser menor que la maxima del bus interno, pero igual no es baja, tipicamente < 1Mhz por lo que si le pones un cristal de 24 o 32 Mhz tendras tiempo para usar algunas intrucciones en el medio entre conversion y conversion lo que haria que tu frecuencia de muestreo aumente considerablemente.


----------



## RockKaiser2 (Oct 23, 2011)

Bueno, sucede que en algún lugar de la programación estoy perdiendo mucho tiempo, pero aún no logro entender exactamente dónde...

Me habían recomendado tomar varias muestras antes de mandarlas al PC mediante un ciclo for, pero el dato no llega adecuadamente...


```
#include <18F4550.h>
#DEVICE ADC=8
#fuses HSPLL,MCLR,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)



#define USB_HID_DEVICE     FALSE             //deshabilitamos el uso de las directivas HID
#define USB_EP1_TX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for IN bulk/interrupt transfers
#define USB_EP1_RX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers
#define USB_EP1_TX_SIZE    8                //size to allocate for the tx endpoint 1 buffer
#define USB_EP1_RX_SIZE    2                 //size to allocate for the rx endpoint 1 buffer

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


//#define modo      recibe[0]
//#define param     recibe[1]
//#define voltaje   envia[0]


char voltaje [];
//int8 envia[1];
int c=0;


void main(void) 
{
   setup_timer_0(RTCC_INTERNAL);   
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   disable_interrupts(GLOBAL);     

   usb_init();                     
   usb_task();                     
   usb_wait_for_enumeration();      
     
   setup_port_a( ALL_ANALOG );      
   #asm                                               
    movlw 0b00010101 ;justificacion_izquierda,4Tad,16Tosc
    iorwf 0xFC0,1 ; direccion de ADCON2        
   #endasm                               
   setup_adc(ADC_CLOCK_DIV_16);  
   set_adc_channel(0);
  
   while (usb_enumerated())
   {
    for(c=0;c==7;++c){
     voltaje[c]=read_adc();
    }
    usb_put_packet(1,voltaje,8,USB_DTS_TOGGLE);
   }
}
```


----------



## juanma2468 (Oct 23, 2011)

Pues tene en cuenta que cuando programas en lenguaje de alto nivel como C o pascal, estos al compilar pueden llegar a generar codigos no optimos, o sea que ponen codigo redundantes o inecesarios, lo cual puede que te este generando el problema, de ser posible si podes programarlo en asambler para optimizarlo, se que es algo mas engorroso y costoso pero a veces es necesario. Cuantas muestras estas considerando en tu ventana, y que tipo de ventana estas utilizando?


----------

