Encoder‎ > ‎Encoder por Software‎ > ‎

USB Bulk Transfer

Control de dispositivos a través del USB.
con la librería MPUSBAPI.DLL

Control de dispositivos a través de un PIC con USB programado en FreeBasic y CCS.

Casi todos los programas para PC que desean conectarse a un PIC con USB suelen estar realizados en VisualBasic, C++ ó Delphi. Apena existe información de cómo hacerlo en FreeBasic
. Este apartado está dedicado a los usuarios de este lenguaje de programación. De todas formas, justo al final de esta página, encontrarás enlaces muy bien documentados de proyectos parecidos hechos en VisualBasic, C++ ó Delphi.

Se trata de manejar el modo "Bulk Transfer" gracias a la librería MPUSBAPI.DLL desde FreeBasic, así evitamos usar el modo (o clase) CDC (puerto virtual de comunicaciones) que nos obliga ir al "Panel de Control" de Windows para averiguar el número de puerto COM creado o conectado. También sucede que en la clase CDC obtenemos un número de puerto, pero al cambiarlo a otra entrada USB del mismo ordenador te pone otro número de puerto, porque a cada entrada de USB se le asigna uno. Esto no sucede en la clase (o modo) Bulk Transfer; el PIC se identifica en cualquier puerto USB sólo a través del VID&PID. En este modo obtenemos velocidades de transferencias muy superiores a la clase CDC. Eso sí, en el modo Bulk Transfer hay que invocar una librería llamada "MPUSBAPI.DLL".

Propongo hacer un ejercicio muy sencillo: una simple suma. A partir de este ejemplo puedes añadir y modificar los programas (del PC y del PIC) para que hagan otras funciones, como por ejemplo controlar luces, motores, sensores, etc.

Ejercicio:

1.) Enviar al PIC dos números para sumar.
2.) El PIC realiza la suma y devuelve el resultado al PC, también visualiza el resultado a través de 8 LED.

FreeBasic code:
The translation could modify the code. Use the code without translating.
#Include Once "USB_Bulk_Transfer.bi"  ' Adjunta una parte del programa que se encarga de manejar la librería MPUSBAPI.DLL

Screen 12

Print
Print "Suma de dos numero. Han de ser menores de 128."
Print
Input "Cifra 1 (Ejemplo: 0..127)"; Put2PIC(0)
Input "Cifra 2 (Ejemplo: 0..127)"; Put2PIC(1)


MPUSBWrite(myOutPipe, @Put2PIC(0), 2, @SentDataLength, 1000) ' Envía al PIC las dos cifras.
MPUSBRead (myInpPipe, @Get2PIC(0), 1, @RecdDataLendth, 1000) ' Recibe del PIC el resultado.

Print "La inteligencia artificial del PIC piensa que es...: "; Get2PIC(0)

MPUSBClose ( MyInpPipe )
MPUSBClose ( MyOutPipe ) ' Cierra las "pipes" al salir del programa.

Sleep

End

Para simplificar el programa usé un truco en el que pongo las definiciones de la librería "MPUSBAPI.DLL" como fichero externo para ser invocado y unirse en el momento de la compilación. Se llama:

"USB_Bulk_Transfer.bi"

De esta forma nos concentraremos en lo esencial, que es enviar y recibir datos del PIC. Este fichero externo no está el código expuesto aquí, es necesario que lo descargues (lo encontrarás en la descarga de todo el proyecto), si no, evidentemente, no podría compilar.

Cabe destacar tres cuestiones:

1.) En los arrays "Put2PIC" y "Get2PIC" se almacenarán los valores de entrada y salida, lo hará en bytes (sin signo). Al ser un array, el primer byte se almacenará en 0, el segundo en 1, el tercero en 2, así hasta 64 posiciones posibles contando desde cero (0..63), pero sólo usamos dos bytes para enviar ( Put2PIC(0) y Put2PIC(1) ) y uno para recibir ( Get2PIC(0) ).


2.) La segunda es esta:

MPUSBWrite(myOutPipe, @Put2PIC(0), 2, @SentDataLength, 1000) ' Envía al PIC las dos cifras.
MPUSBRead (myInpPipe, @Get2PIC(0), 1, @RecdDataLendth, 1000) ' Recibe del PIC el resultado.

Aquí nos interesa tres detalles: 
  1. Los arrays "@Put2PIC(0)" y "@Get2PIC(0)" siempre ha de comenzar por el valor cero, porque son punteros (por eso tiene la arroba delante de la variable) y apunta a la dirección donde comienza los datos, pero no es necesario que sepas de punteros.
  2. El valor 2 en "MPUSBWrite" son los bytes para enviar al PIC, y el 1 en "MPUSBRead" es el único byte para recibir.
  3. El 1000 final es la latencia en milisegundos, es decir que se espera si fuese necesario 1 segundo tanto para enviar como para recibir. Puedes ponerle el valor que quieras, pero para este ejemplo recomiendo dejarlo tal como está.
Todo lo demás que ves en esas funciones son cuestiones técnicas que ahora mismo no nos interesa.

3.) El tercer punto es mostrar el resultado. Como sólo recibimos un byte, mostramos el primer byte del array:

Print "La inteligencia artificial del PIC piensa que es...: "; Get2PIC(0)



Pasemos ahora al código en CCS C.

Código CCS:
The translation could modify the code. Use the code without translating.
#include <18F4550.h>

#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN // Configurado para cristal de 20MHz.
#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                 // Un byte para enviar.
#define USB_EP1_RX_SIZE    2                 // Dos bytes para recibir.

#include <pic18_usb.h>      //Microchip PIC18Fxx5x Hardware layer for CCS's PIC USB driver.
#include "usb_desc_scope.h" //Enumerador USB, de Pedro, alias "Palitroquez!"
#include <usb.c>            //handles usb setup tokens and get descriptor reports.

#use fast_io(b)

void main(void)
{
   int8 recibe[2];
   int8 envia[1];
   
   set_tris_b(0x00); // Todo el puerto B como salidas.
   output_b(0x00);   // Pone a cero el puerto B.
   
   setup_adc (adc_clock_div_32); // Sin ADC, sin comparadores, etc...
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   port_b_pullups(FALSE);
   
   usb_init();  //init USB.
   usb_task();  //enable usb device and interruptions.
   usb_wait_for_enumeration();  //wait.
   
   output_high(PIN_B7);  // PortB.7 tiene un LED que parpadea tres veces al comienzo.
   delay_ms(500);
   output_low(PIN_B7);
   delay_ms(500);
   output_high(PIN_B7);
   delay_ms(500);
   output_low(PIN_B7);
   delay_ms(500);
   output_high(PIN_B7);
   delay_ms(500);
   output_low(PIN_B7);
   delay_ms(500);
   
   while (true)
   {
      if(usb_enumerated())  //if PicUSB is configurated.
      {
         if (usb_kbhit(1))  // Si recibe algún dato, entonces...
         {
            usb_get_packet(1, recibe, 2); // Guarda los 2 bytes recibidos en la variable "recibe".
                                          // El primer byte queda en recibe[0] y segundo byte en recibe[1].
            envia[0]=recibe[0]+recibe[1]; // Suma los dos bytes y el resultado lo guarda en "envia".
            
            output_b(envia[0]); // El resultado de la suma lo saca por el puerto B (8 LEDs).
            
            usb_put_packet(1, envia, 1, USB_DTS_TOGGLE); // Y también envía el resultado al PC.                                             
         }
      }
   }
}



Puedes cambiar el PIC poniendo la librería (.h) correspondiente. Por ejemplo, en vez de usar el PIC 18F4550 queremos usar el PIC 18F2550, entonces hacemos lo siguiente, donde pone:

#Include <18F4550.h>
Lo cambiamos por:
#Include <18F2550.h>  

Te servirá cualquier PIC de la serie 18Fxx5mientras respete ese '5' en posición segunda comenzando por la derecha porque significa que soporta comunicación USB.

Nota:
Los "#fuses" del programa CCS están configurados para poner un cristal de 20 MHz. Si quieres poner cualquier otro cristal (de 4 a 20 MHz.) haz clic aquí.



Descargar todo el proyecto haciendo clic aquí.
(Si usas antivirus Avast has de añadir una exclusión para poder ejecutarlo. Para analizar este o cualquier otro archivo puedes hacer clic aquí)

Está todo lo necesario, es decir, el código fuente y ejecutable en FreeBasic, en CCS (.C y .HEX), los drivers de instalación para el PIC y el fichero "usb_desc_scope.h"

Si vas a compilar el código fuente hecho en FreeBasic y tienes una versión antigua de este compilador has de tener la librería "libmpusbapi.dll.a". Este fichero lo encontarás en el zip de descarga y has de copiarla o moverla a la carpeta (...)\FreeBASIC\lib\win32. Aunque si tienes actualizado el compilador no creo que sea necesaria esta acción.

Está probado en Windows XP, Windows 7 y Colossus
. No puedo garantizar el funcionamiento en otras plataformas Windows porque de momento no he tenido la oportunidad de hacerlo. Sin embargo los drivers y librería que contiene el zip de descarga están actualizados para todas las versiones Windows (Windows 95/98/SE, XP/32/64, Windows 2000, Vista/32/64, Windows 7/32/64, etc.).

Windows 7 da un error si la librería "MPUSBAPI.DLL" es antigua, cosa que no ha de sucederte porque he puesto en el zip de descarga la última versión (Versión 6).

Haz clic aquí y tendrás un ejemplo de aplicación, donde se lee 5 encoders con un sólo PIC a través de USB (usando la librería MPUSBAPI.DLL) y un programa de PC toma esa información para mover un brazo robot virtual.


Agradecimientos y enlaces relacionados con este artículo:

A continuación encontrarás proyectos parecidos, en los que yo también me he servido, perfectamente documentados realizados en VisualBasicC++ ó Delphi.


J1M              hobbypic (antiguamente ese era el nombre de su web).

[J1M fue al parecer quien abrió camino sobre el tema PIC USB en foros de habla hispana.]

Y reconocer a MichaelW de FreeBasic Forum la ayuda que me ofreció para poder implementar la librería MPUSBAPI.DLL en FreeBasic.