La mcHID.dll

               
                                      Esquema para el Reloj de trabajo
                                      (Revisar Datasheet PIC18F2550)
                                     
Para comenzar usaremos el código de ejemplo para el PIC de MikroPascal en
C:\Archivos de programa\Mikroelektronika\mikroPascal\Examples\EasyPIC5\extra examples\USB HID\P18F4550.

Recordar que la familia 18F4550/2550/2455/4455 viene implementada con un multiplicador para frecuencias del reloj de trabajo. Para USB se nesecita que la frecuencia de trabajo sea de 48MHz, podemos usar un cristal de 48MHz directamente hacia USBDIV-0(por favor abra el esquema en el link abajo), o hacer uso del PLL Prescaler; por ejemplo si usamos un cristal de 20MHz deberemos ingresar su señal por PLLDIV(÷5); la idea siempre sera que de PLLDIV siempre salgan 4MHz que son los necesarios para producir 96MHz, y como se ve en el diagrama esos 96MHz se dividirán (÷2) para tener los 48MHz necesarios para el protocolo de comunicacion USB.

(ver Esquema para el Reloj de trabajo)

                                            
Asi que abrimos el proyecto de ejemplo de MikroPascal, y luego Project/Edit Project si usamos un cristal de 20MHz deberemos marcar la casilla PLLDIV_5_1L, si usamos un cristal de 4MHz marcar la casilla PLLDIV_1_1L; como sabemos apartir de 4MHz es HS así que la casilla FOSC_HSPLL_HS_1H esta activada, luego clic en OK y no tocamos nada mas por ahora.

Cuando el PIC se conecte con la PC, se le solicitara información como el fabricante, el nombre del producto, VID y PID, etc. Toda esta información debe estar contenida en la memoria del PIC, el descriptor; en el caso de este ejemplo el descriptor se encuentra en la unidad USBdsc.ppas. click en Tool/Hid Terminal/Descriptor. El VendorID(VID) que vemos le corresponde a MikroElectrónica no modificarlo, el ProductID(PID) podemos modificarlo; dos proyectos diferentes (en un PIC por ejemplo) no pueden tener el mismo PID.

Report Lengt indica la cantidad de bytes que se transferirán en cada trama PC-PIC; por ahora no lo modificamos y debe ser 1 en ambos casos; en Vendor Name y Product Name podemos poner lo que mas nos agrade, al final cada proyecto en el PIC se identifica con su VID y PID; luego clic en Create y elegimos la carpeta donde reside el proyecto.

En la unidad USB_HID_test.ppas solo modificaremos TRISB:=0 y agregamos la linea 68

Código Pascal [-]
Begin
   ch := userRD_buffer[0];
   userWR_buffer[0] := ch;
   PORTB:=ch;                        //linea 68
   HID_Write(@userWR_buffer, 1);
   inc(i);
End;


Click en Build Project y ya tenemos listo el .hex para meterlo en el PIC.

El circuito eléctrico es mas sencillo que para la comunicacion RS-232

                                                          Esquema Electrico
 
Ahora solo falta el codigo para la PC, recordemos que vamos a usar la mcHID.dll.

Como ya dijimos en el apartado Delphi USB y Herramientas, si ya tenemos la utilidad EasyHid.exe la ejecutamos, pero no nos interesara ninguna de las configuraciones que nos ofrece pues estas seran para crear un template para el PIC en BASIC; nosotros ya tenemos ya nuestro .hex para el PIC; eso si en la ultima configuracion debemos elegir Borland Delphi para que tambien cree un template para la PC en Delphi. Esta utilidad tambien colocara la mcHID.dll en Sytem32; pero seguramente algunos preferiremos tener esta dll a la vista, y como sabemos para poder usar una dll podra estar alojada en Sytem32 o en la carpeta donde reside nuestro .exe; esto ultimo es lo que haremos.

Para los que no han bajado la EasyHID, solo nesecitamos la dll y el template.
USBProject.zip

El template para Delphi que EasyHid crea lo puedes descargar arriba en USBProject.zip; este no tiene nada de especial : 3 unidades que pudieron estar todas en una sola, pero por una cuestion de claridad estan en tres.

En la unidad cUSBInterfaceTypes.pas se encuentran algunas constantes que corresponden a los mensajes que se recibiran a los eventos que ocurran en el puerto; en la unidad cUSBInterface.pas se encuentra el codigo que importa, las funciones y procedimientos de la dll; en la unidad FormMain.pas es donde gestionaremos todo desde el Form.

En la ultima unidad podemos ver 3 procedimientos: onCreate, donde nuestra aplicacion digamoslo asi se conecta con el puerto USB; en onDestroy, nos desconectamos del puerto y USBEvent que es donde procesamos los datos que llegan del dispositivo USB(PIC).

El procedimiento USBEvent se ejecuta al dispararse el mensaje WM_HID_EVENT, el cual esta definido en la mcHID.dll. Los flags de este mensaje nos informan si el dispositivo HID se conecta, desconecta, envia datos. Esos Flags que nos interesan son NOTIFY_PLUGGED, NOTIFY_UNPLUGGED y NOTIFY_READ, estos nos indican respectivamente cuando se conecta, se desconecta y recibe datos de un dispositivo HID.
 
Asi que vamos a terminar con esto:
3 Button(Enviar, Limpiar e Info), 2 Memo, 2 check y un edit.

 


En la unidad del Form se definen 2 constantes : VENDOR_ID y PRODUCT_ID, estas deben ser las mismas que se definieron en MikroPascal, VID y PID, si no se modificaron deben ser $1234 y $0001. Tambien en la unidad debemos definir las constantes BufferInSize = 1 ; BufferOutSize = 1; pues es 1 byte el que se enviara y recibira en este ejemplo.

En el procedimiento USBEvent se debera mostrar un mensaje en Memo1 cuando el dispositivo es insertado(NOTIFY_PLUGGED) o extraido(NOTIFY_UNPLUGGED ), en NOTIFY_READ se leera los datos que llegen del PIC. Pero cada vez que se dispare este procedimiento deberemos verificar que se trata de nuestro PIC mediante su VID y PID, en otro caso intentemos por ejemplo enviarle datos al teclado y con un poco de suerte colgaremos la PC.


En este ejemplo los datos que se envian y reciben son de un solo byte, y se almacenan en FBufferIn y FBufferOut; la funcion GetOutputReportLength() exportada de la dll nos dara la longitud en byte del dato recibido, el bit-0 del byte no se debe leer, asi que se debe leer:

for:=1  to GetOutputReportLength() -1

Veremos varias funciones que no conocemos y son las importadas de la dll(ver la unidad donde se declaran)

Código Delphi [-]
NOTIFY_READ :
        begin
           beep;
           DevHandle := Msg.LParam;
           // verificamos si se trata de nuestro dispositivo HID - USB
           //con su VID PID
           if (GetVendorID(DevHandle) = VENDOR_ID) and
              (GetProductID(DevHandle) = PRODUCT_ID) then
           begin
               // primer byte recibido es el report ID y no debe leerse...
               Read(DevHandle,@FBufferIn);
               // la funcion GetOutputReportLength  obtiene la longitud(bytes)
               //del paquete que ha llegado del puerto
               for index:=1 to GetOutputReportLength(DevHandle)-1 do
               //pudimos usar ... to BufferInSize
               begin
                   // ver el paquete como cadena de caracteres
                   if NumberCheckBox.Checked then
                   begin
                     S:= S +':'+IntToStr(FBufferIn[ index ]);
                   end;
                   // ver el paquete como cadena de numeros
                   if TextCheckBox.Checked then
                   begin
                     S:= S + Char(FBufferIn[ index ]);
                   end;

               end;
               Memo2.Lines.Add('Recibido: '+S);
               S:='';
           end;
        end;
 El proceso de envio de datos es similar, en el procedimiento EnviarButtonClick.
Código Delphi [-]
procedure TForm1.EnviarButtonClick(Sender: TObject);
var index     : Integer;
    DevHandle : cardinal;
begin
   // el primer byte enviado es el report ID, y debe ser $0
  FBufferOut[0] := $0;

   //podemos usar 1 en lugar de BufferOutSize
   //pues en este ejemplo se recibe y envia 1 byte
  for index := 1 to BufferOutSize do                            
       FBufferOut[index] := StrToInt((Edit1.Text));         
 
   //escribimos primero, el puerto es mas rapido
  Memo2.Lines.Add(' Enviado: '+IntToStr(FBufferOut[ 1 ]));
  DevHandle := GetHandle(Vendor_ID, Product_ID);
  Write(DevHandle,@FBufferOut);
 
           
Para obtener la informacion almacenada en el PIC: VID, PID, ProducName y 
VendorName usamos el procedimiento InfoButtonClick.
Código Delphi [-]
procedure TForm1.InfoButtonClick(Sender: TObject);
var
  DevHandle,Len  : cardinal;
  TextBuffer     : Array [0..255] of char;
  S              : String;
begin
   DevHandle:=GetHandle(Vendor_ID, Product_ID);
   GetVendorName(DevHandle, TextBuffer, Len);
   S:=TextBuffer;
   Memo2.Lines.Add('-------------------------------------------------------');
   Memo2.Lines.Add('Vendor Name:        '+ S);
   GetProductName(DevHandle, TextBuffer, Len);
   S:=TextBuffer;
   Memo2.Lines.Add('Product Name:       '+ S);
   Memo2.Lines.Add('VendorID:              '+IntToStr(GetVendorID(DevHandle)));
   Memo2.Lines.Add('ProductID:             '+IntToStr(GetProductID(DevHandle)));
   Memo2.Lines.Add('-------------------------------------------------------');
Adjunto el proyecto completo y otro con una pequeña variacion.

USB - HID EJEMPLO1.zip
 
 
UN SEGUNDO EJEMPLO UN POCO MAS INTERESANTE:

Ahh recordar verificar en la definicion de las constantes VENDOR_ID y PRODUC_ID 
en la unidad del Form, que deben coincidir con las que definimos en
el descriptor con Mikropascal. Para este segundo ejemplo son $1234 y $2.
Incluyo aqui los 2 ejemplos anteriores pero con todas las fuentes en Delphi y
MikroPascal:
USB - HID EJEMPLO1.zip
USB - HI EJEMPLO 2 FULL.zip
ahora el segundo ejemplo con la mcHID.dll:

La diferencia es que la longitud en bytes del paquete de datos que se envia y recibe sera de 16, asi que habra que ir a TOOL\HID TERMINAL\DESCRIPTOR, y definir los Report Leng a 16.

 

Tambien en unidad del Form modificar las constantes BufferInSize y BufferOutSize a 16. 

 

El ejemplo es muy similar la USB - Hid EJEMPLO1 ; el PIC acepta como comandos los numeros del 0,1,2,...,15. con los cuales se prende y apaga los leds conectados a PORTB(muy sencillo verdad?), pero ademas cada vez que ocurra esto el PIC te informara de la accion, por ejemplo si envias el numero 5 se enciende el led RB5 y el PIC te enviara el mensaje 'RB5 ENCENDIDO'.

 

Para la funcion de MikroPascal HID_Write() se debe siempre definir como longitud del paquete de datos a enviar el valor de Report Length(definida en el descriptor), si no lo hacemos asi la funcion fallara, es decir no enviara el dato; un pequeño obstaculo verdad; pues si del PIC solo queremos enviar un paquete de 9 bytes y Report Length esta definido a 16 byte, a la PC llegaran 16 bytes, los 9 que enviamos y los restantes indeterminados.

 

Para solucionar esto ponemos siempre en el primer byte que se envie a la PC osea en userWR_buffer[0] la longitud del paquete y a la hora de leer el paquete de llegada en la PC tomar como su longitud FBufferIn[ 1 ].

 

A la hora de enviar datos al PIC debemos calcular la longitud de carateres(byte) que se edita en Edit1 en el procedimiento EnviarButtonClick; pero en este ejemplo solo se envia 1 byte asi que solo bastara:

 

FBufferOut[ 1 ]:=StrToInt(Edit1.Text);

 

Código Delphi [-]

procedure TForm1.EnviarButtonClick(Sender: TObject);
var index     : Integer;
    DevHandle : cardinal;
begin

   // el primer byte enviado es el report ID, y debe ser $0
  FBufferOut[0] := $0;
  for index := 1 to BufferOutSize do
      //para esteejemplo solo mandamos un byte
      FBufferOut[ 1 ]:=StrToInt(Edit1.Text);                
  //escribimos primero, el puerto es mas rapido
  Memo2.Lines.Add(' Enviado: '+IntToStr(FBufferOut[ 1 ]));
  DevHandle := GetHandle(Vendor_ID, Product_ID);
  Write(DevHandle,@FBufferOut);
  
end;

Escribir comentario

Comentarios: 17
  • #1

    Jorge Riquelme (domingo, 11 marzo 2012 10:43)

    Muy buena aportacion, ya lo probe con delphi6 y funciona correctamente...

  • #2

    Fernandes Gabriel (martes, 24 julio 2012)

    Por favor, como eu poderia fazer o Programa Host em C++ Builder 6?

  • #3

    Jorge Meza (miércoles, 17 octubre 2012 23:18)

    Muy buen aporte, tengo una duda cómo poder saber si los leds están encendidos o no, es decir hay alguna rutina que podamos revisar los puertos desde delphi. Estaré atento a tú ayuda Gracias.

  • #4

    fenixariel (miércoles, 24 octubre 2012 22:58)

    Para c++, solo implementar la interfaz para la dll, como cualquier dll.

    saludos....

  • #5

    fenixariel (miércoles, 24 octubre 2012 22:59)

    Hola Jorje no te entendi bien.....

  • #6

    Jorge Meza (lunes, 29 octubre 2012 15:09)

    Es decir, en el evento que se cierre la aplicación los leds pueden quedar encendidos,pero al abrir nuevamente la aplicación no sabe cuáles estan encendidos o no. Hay alguna rutina para leer si estos están on por ejemplo. Te agradecería tu ayuda Gracias

  • #7

    fenixariel (lunes, 29 octubre 2012 15:43)

    Desde el pic puedes saber cuando se fue la coneccion, solo revisar la documentacion de microc o micropascal.
    Otra forma seria escribir algo de codigo adicinal en el pic, cuando se establesca la comunicacion enviar a la pc el estado de los leds........el ejemplo que se puso es muy muy basico, para que lo adaptes a lo que quieres....

  • #8

    Jorge Meza (lunes, 29 octubre 2012 17:39)

    No hay manera de saberlo desde delphi, con la programación del pic actual?

  • #9

    fenixariel (sábado, 03 noviembre 2012 01:18)

    no.....

  • #10

    u=14847 (domingo, 05 mayo 2013 23:12)

    I shared this upon Facebook! My pals will definitely like it!

  • #11

    Jorge Riquelme (jueves, 13 febrero 2014 14:47)

    Como puedo hacer para leer cuatro canales analogicos del pic18f4550?

  • #12

    fenixariel (lunes, 17 febrero 2014 08:05)

    De la misma forma en que se hace en uno de la serie 16 o uno de la serie 32.
    Luego envías el valor numérico por el USB, nada mas.

  • #13

    Jorge Meza (martes, 01 abril 2014 00:06)

    fenixariel hace casi dos años utilicé tu proyecto le hice una serie de modificaciones y te cuento que me funciona de maravilla, te agradezco por ser tan colaborador con esta página sigue así...

  • #14

    fenixariel (miércoles, 02 abril 2014 22:36)

    Gracias, pronto tratare de subir mas cosas que aprendi en este tiempo...

  • #15

    Fran (domingo, 10 abril 2016 09:12)

    Estoy intentado leer un lector RFID que lee una serie de numero como un lector de codigo de barras, al tener dos lectores quiero implementar que lea uno u otro como podria hacerlo.
    Gracias

  • #16

    fenixariel (domingo, 10 abril 2016 19:37)

    Como que no entendí....

  • #17

    Fran (viernes, 15 abril 2016 11:32)

    Tengo un teclado por USB-HID con un numero VID (Vendedor_ID) "VID:046D:" y un PID (Product_ID) "PID:C31C"
    Tengo un lector por USB-HID con un numero VID (Vendedor_ID) "VID:13BA" y un PID (Product_ID) "PID:0018"
    Dicha informacion la puedo sacar del Regedit o desde el administrador de dispositivos, los dos utilizan el mismo controlador.
    Luego dispongo de dos TEdit "Edit1" y "Edit2" la tecla 0 el la key=#48, pues bien la intencion es que si lee la tecla 0 del dispositivo HID de teclado vaya ela "Edit1" pero si por el contrario leo con el lector, que para ello le he dicho que la primera letra sea un 0, vaya al "Edit2".
    Se podria saber de donde viene la tecla 0 si del lector o del teclado??

    Gracias de uevo