Microchip tiene 6 micros de la familia 18F con implementación del
USB 2.0. Estos son los 18F2450, 18F2455, 18F2550, 18F4450, 18F4455 y 18F4550.
Para comunicarnos con ellos desde nuestro PC haciendo uso de dicho
USB 2.0 sobre una plataforma
Windows los amables señores de Microchip ponen a nuestra disposición un DLL específica llamada
mpusbapi.dll.
Esta DLL puede ser llamada desde Visual Basic, Visual C++, C Builder o como es nuestro caso desde
Delphi. Este hilo está destinado a dar una idea básica de como hacerlo (sin meternos en jardines de los que no sepamos salir después)
Primero. Antes que nada necesitamos la dichosa DLL. La podéis descargar desde la Web de Microchip o al final de este post incluida en el proyecto Delphi de pruebas que vamos a construir.
Segundo. Necesitamos una API que podamos incluir en nuestro proyecto y que sepa cómo llamar a la funciones de mpusbapi.dll para inicializar el puerto USB, enviar y recibir los datos entre nuestro programa y el PIC.
Esta API la vamos a llamar
usbAPI.pas y su contenido es:
Unit UsbAPI;
Interface
Uses Windows, SysUtils;
Const MPUSB_FAIL = 0;
MPUSB_SUCCESS = 1;
MP_WRITE = 0;
MP_READ = 1;
MAX_NUM_MPUSB_DEV = 127;
// MAX_NUM_MPUSB_DEV is an abstract limitation.
// It is very unlikely that a computer system will have more
// then 127 USB devices attached to it. (single or multiple USB hosts)
Function MPUSBGetDeviceCount(pVID_PID: PCHAR) : DWORD; cdecl;
Function MPUSBOpen(instance : DWORD ; pVID_PID :PCHAR; pEP : PCHAR;
dwDir : DWORD; dwReserved : DWORD): THANDLE;cdecl;
Function MPUSBGetDLLVersion():DWORD;cdecl;
// Reads a data package to the USB device
// HANDLE: Handle to the device
// pData: Pointer to the input buffer
// dwDataLen: expected count of bytes to read
// dwDataLenRead: actually count read data
// waiting time
Function MPUSBRead(HANDLE: THANDLE; pData :Pointer; dwDataLen :DWORD;
pDataLenRead :PDWORD; dwMilliseconds :DWORD):DWORD; cdecl;
// Writes a data package to the USB device
// HANDLE: Handle to the device
// pData: Pointer to the output buffer
// dwDataLen: expected count of bytes to send
// dwDataLenSent: actually count of sent data
// waiting time
Function MPUSBWrite(HANDLE :THANDLE; pData :Pointer; dwDataLen : DWORD;
pLengthSent :PDWORD; dwMilliseconds :DWORD):DWORD; cdecl;
// see microchip documentation
Function MPUSBReadInt(HANDLE :THANDLE; pData :Pointer; dwDataLen : DWORD;
pLengthReceive :PDWORD; dwMilliseconds :DWORD):DWORD; cdecl;
Function MPUSBClose(HANDLE : THANDLE ): Boolean;cdecl;
Implementation
Function MPUSBGetDLLVersion; cdecl; external 'MPUSBAPI.Dll' index 1;
Function MPUSBGetDeviceCount; cdecl; external 'MPUSBAPI.Dll' index 2;
Function MPUSBOpen; cdecl; external 'MPUSBAPI.Dll' index 3;
Function MPUSBRead; cdecl; external 'MPUSBAPI.Dll' index 4;
Function MPUSBWrite; cdecl; external 'MPUSBAPI.Dll' index 5;
Function MPUSBReadInt; cdecl; external 'MPUSBAPI.Dll' index 6;
Function MPUSBClose; cdecl; external 'MPUSBAPI.Dll' index 7;
end.
y Tercero. Unos ejemplos concretos de como comunicar nuestro programa con la usbAPI.pas que a su vez realice las llamadas a la mpusbapi.dll.
Para que nos responda el PIC hemos debido previamente programarlo con el
Firmware desarrollado en el
Proyecto PicUSB del maestro
J1M.
En la sección interface de nuestro programa debemos incluir unas cuantas definiciones:
Const
vid_pid : PCHAR = 'vid_04d8&pid_0011'+#0;
out_pipe : PCHAR = '\MCHP_EP1' + #0;
in_pipe : PCHAR = '\MCHP_EP1' + #0;
DO_SUMMA = $00;
SET_LED = $01;
UsbBufSize = 64;
El
vid_pid es el que Jaime ha incluido tanto en dicho firmware como en el driver necesario para su conexión.
Las constantes
DO_SUMMA y
SET_LED son los comandos a los que va a responder dicho firmware y cuya paternidad también es achacable al amigo Jaime.
También dedemos definir unos cuantos
Types necesarios para utilizarlos como parámetros en las llamadas a la DLL:
Type
PByteBuffer = ^TByteBuffer;
TByteBuffer = Array[0..63] of Byte;
PUsbData = ^TUsbData;
TUSBData = Record
Cmd: Byte;
Data : Array[0..UsbBufSize-1] of Byte;
End;
Y en el public vamos a declarar las variables y funciones que vamos a manejar en nuestros ejemplos:
public
myOutPipe: THANDLE;
myInPipe: THANDLE;
Function GetSummary():Integer;
Function SendReceivePacket(SendData: PByteBuffer;
SendLength: DWORD;
ReceiveData: PByteBuffer;
var ReceiveLength: DWORD;
SendDelay: Word;
ReceiveDelay:Word):DWORD;
Procedure CheckInvalidHandle();
Las tres funciones aquí declaradas tienen el siguiente aspecto:
// Funciones de comuniaciones USB /////////////////////////////////////////////////////////////////
function TfrmUsbTest.GetSummary():Integer;
Var
tempPipe:THandle;
count:DWORD;
max_Count:DWORD;
i:Byte;
Begin
tempPipe := INVALID_HANDLE_VALUE;
count:=0;
max_count := MPUSBGetDeviceCount(vid_pid);
if(max_count=0) then
Begin
result:= max_count;
Memo1.Lines.add('No device found');
exit;
End
Else
memo1.lines.add(IntToStr(max_Count) + ' device(s) with ' + vid_pid + ' currently attached');
count := 0;
For i:=0 to MAX_NUM_MPUSB_DEV-1 Do
Begin
tempPipe := MPUSBOpen(i,vid_pid,NIL,MP_READ,0);
if(tempPipe <> INVALID_HANDLE_VALUE) then
Begin
memo1.lines.add('Instance Index ' + IntToStr(i));
MPUSBClose(tempPipe);
Inc(count);
end;
if(count = max_count) Then break;
end;
result:= max_count;
End;
Function TfrmUsbTest.SendReceivePacket(SendData: PByteBuffer;
SendLength: DWORD;
ReceiveData: PByteBuffer;
var ReceiveLength: DWORD;
SendDelay: Word;
ReceiveDelay:Word):DWORD;
var
SentDataLength: DWORD ;
ExpectedReceiveLength: DWORD;
Begin
ExpectedReceiveLength := ReceiveLength;
if(myOutPipe <> INVALID_HANDLE_VALUE) and ( myInPipe <> INVALID_HANDLE_VALUE) then
Begin
if MPUSBWrite(myOutPipe,SendData,SendLength,@SentDataLength,SendDelay) <> 0 then
Begin
if(MPUSBRead(myInPipe,ReceiveData, ExpectedReceiveLength,@ReceiveLength,ReceiveDelay)) <> 0 then
Begin
if (ReceiveLength = ExpectedReceiveLength) Then
Begin
result:=1;
Exit;
End
else
Begin
if (ReceiveLength < ExpectedReceiveLength) then
begin
result:=2;
End;
End
End
else
CheckInvalidHandle();
End
else
CheckInvalidHandle();
End;
result:=0;
End;
Procedure TfrmUsbTest.CheckInvalidHandle();
Begin
if (GetLastError() = ERROR_INVALID_HANDLE) then
Begin
MPUSBClose(myOutPipe);
MPUSBClose(myInPipe);
myOutPipe := INVALID_HANDLE_VALUE;
myInPipe := INVALID_HANDLE_VALUE;
End
else
Memo1.lines.Add('Error Code ' + IntToStr(GetLastError()));
End;
Primer ejemplo: Preguntarle a mpusbapi.dll su propia versión :
procedure TfrmUsbTest.btnGetUSBDriverVersionClick(Sender: TObject);
var
temp:DWORD;
versionInfo : Array[0..3] of Byte;
begin
temp := MPUSBGetDLLVersion();
move(temp,VersionInfo,sizeof(VersionInfo));
memo1.text:='USB Driver Version ' + intTOStr(VersionInfo[0]) + '.' +
intTOStr(VersionInfo[1]) + '.' +
intTOStr(VersionInfo[2]) + '.' +
intTOStr(VersionInfo[3]);
end;
Segundo Ejemplo : Enviar dos bytes siendo el primero el comando "Led" y el segundo el led que queremos que encienda o apague.
procedure TfrmUsbTest.chkSwitchLedClick(Sender: TObject);
var
selection: DWORD;
send_buf: TUsbData;
SentDataLength: DWORD;
Begin
Selection:=0;
myOutPipe := MPUSBOpen(selection, vid_pid, out_pipe, MP_WRITE, 0);
if (myOutPipe = INVALID_HANDLE_VALUE) then
Begin
if (myOutPipe = INVALID_HANDLE_VALUE) then memo1.lines.add('Failed to open out data pipe');
Exit; //USB Operation Failed
End;
send_buf.Cmd := SET_LED;
if RadioButton1.Checked then send_buf.Data[0]:=0;
if RadioButton2.Checked then send_buf.Data[0]:=1;
if RadioButton3.Checked then send_buf.Data[0]:=2;
if MPUSBWrite(myOutPipe,@Send_buf,2,@SentDataLength,1000) <> 0 then
Begin
if (SentDataLength <> 2) Then
memo1.lines.Add('Failure on sending command LED')
else
memo1.lines.Add('Command LED sended Ok')
End
else
CheckInvalidHandle();
MPUSBClose(myOutPipe);
myOutPipe := INVALID_HANDLE_VALUE;
end;
Tercer Ejemplo: Enviar tres bytes siendo el primero el comando "Suma" y los dos dos siguientes los numeros a sumar, tras ello esperamos a recibir un byte con el resultado de la suma:
procedure TfrmUsbTest.BtnSumaClick(Sender: TObject);
Var
Selection: DWORD;
RecvLength: DWORD;
send_buf: TByteBuffer;
receive_buf:TByteBuffer;
Begin
Selection:=0;
myOutPipe := MPUSBOpen(selection,vid_pid, out_pipe, MP_WRITE, 0);
myInPipe := MPUSBOpen(selection,vid_pid, in_pipe, MP_READ, 0);
If (myOutPipe = INVALID_HANDLE_VALUE) or (myInPipe = INVALID_HANDLE_VALUE) then
Begin
memo1.lines.add('Failed to open data pipes.');
Exit;
End;
send_buf[0] := DO_SUMMA;
send_buf[1]:=StrToInt(Edit1.Text);
send_buf[2]:=StrToInt(Edit2.Text);
RecvLength := 1;
if (SendReceivePacket(@send_buf,3,@receive_buf,RecvLength,1000,1000) = 1) Then
Begin
edit3.Text := IntToStr(receive_buf[0]);
Memo1.lines.add('Suma '+Edit1.Text+' + '+Edit2.Text+' = '+Edit3.Text);
End
Else
Memo1.lines.add('USB Operation Failed');
MPUSBClose(myOutPipe);
MPUSBClose(myInPipe);
myOutPipe := INVALID_HANDLE_VALUE;
myInPipe := INVALID_HANDLE_VALUE;
end;
Compilado y ejecutado este código nos muestra el siguiente resultado :

Aqui teneis el proyecto PicUSBDelphi completo para
Descargar