views:

925

answers:

1

Not a question, just a statement to help anyone else who might spend hours getting the Microchip PIC USB DLL to work properly with Unicode.

It expects several strings, and although these are simple PAnsiChar, getting the right combination of DLL call convention took me ages. There are a lot of people on the 'net using Delphi (non-Unicode) with this DLL and getting away with murder.

unit UArtPic32USBDriver;

interface

uses
  Windows,
  SysUtils,
  UArtGeneralHwDefs;


type
  EArtPic32Usb = class( EArtGeneralHw );

function Pic32Usb_GetDllVersion : integer;
// Returns a number representing the DLL version.

function Pic32Usb_GetDeviceCount( const AVendorProductID : string ) : integer;
// Returns the number of devices with this vendor ID

function Pic32Usb_Open(
           AInstance : DWORD;
           const AVendorProductID, AEndpointID : string;
           ADirectionCode : integer;
           ARaise         : boolean ) : THANDLE;
// Opens an endpoint. Can raise an exception if no valid handle is returned.


procedure Pic32Usb_Close( AHandle : THandle );
// Closes the device

function Pic32Usb_Write(
            AHandle              : THANDLE;
            ABufferPtr           : pointer;
            ALengthBytes         : integer ) : DWORD;
// Performs a write using this handle. Returns the number of bytes written

function Pic32Usb_Read(
            AHandle              : THANDLE;
            ABufferPtr           : pointer;
            ALengthBytes         : integer ) : DWORD;
// Performs a Read using this handle. Returns the number of bytes read

const
  MP_WRITE                   = 0;
  MP_READ                    = 1;

implementation



var

  _MPUSBGetDLLVersion  : function () :DWORD; cdecl stdcall;

  // Number of vid & pid matching USB Devices
  _MPUSBGetDeviceCount : function( pVID_PID : PAnsiChar ) : DWORD; cdecl;

  _MPUSBOpen           : function ( instance   : DWORD;
                                    pVID_PID   : PAnsiChar;
                                    pEP        : PAnsiChar;
                                    dwDir      : DWORD;
                                    dwReserved : DWORD ) : THANDLE; cdecl;

  _MPUSBClose          : function ( handle :THANDLE ) : DWORD; cdecl;

  _MPUSBRead           : function (     handle          : THANDLE;
                                        pData           : pointer;
                                        dwLen           : DWORD;
                                    var pLength         : DWORD;
                                        dwMilliseconds  : DWORD  ) : DWORD; cdecl;

  _MPUSBReadInt        : function (     handle         : THANDLE;
                                    var pData          : pointer;
                                        dwLen          : DWORD;
                                    var pLength        : PDWORD;
                                        dwMilliseconds : DWORD   ) : DWORD; cdecl;

  _MPUSBWrite          : function (     handle         : THANDLE;
                                        pData          : pointer;
                                        dwLen          : DWORD;
                                    var pLength        : DWORD;
                                        dwMilliseconds : DWORD ) : DWORD; cdecl;


  UsbDllHandle : THandle = 0;

const

  MillisecondPollingInterval = 100; //Represents 1 ms is hardware
  CharBufSize = 64;

type
  TAnsiCharBuf64 = array[0..CharBufSize-1] of AnsiChar;


function LoadDLL : THandle;
var
  S : string;
begin
  S := 'mpusbapi.dll';
  UsbDllHandle := LoadLibrary( PChar(S) );

  If UsbDllHandle = 0 then
     Raise EArtPic32Usb.CreateFmt(
       'The usb library is required but cannot be loaded. Check that it is installed. (%s)',
       [S]
        );

  @_MPUSBGetDLLVersion := GetProcAddress(UsbDllHandle,'_MPUSBGetDLLVersion');
  Assert(@_MPUSBGetDLLVersion <> nil);

  @_MPUSBGetDeviceCount := GetProcAddress(UsbDllHandle,'_MPUSBGetDeviceCount');
  Assert(@_MPUSBGetDeviceCount <> nil);

  @_MPUSBOpen := GetProcAddress(UsbDllHandle,'_MPUSBOpen');
  Assert(@_MPUSBOpen <> nil);

  @_MPUSBClose := GetProcAddress(UsbDllHandle,'_MPUSBClose');
  Assert(@_MPUSBClose <> nil);

  @_MPUSBRead := GetProcAddress(UsbDllHandle,'_MPUSBRead');
  Assert(@_MPUSBRead <> nil);

  @_MPUSBReadInt := GetProcAddress(UsbDllHandle,'_MPUSBReadInt');
  Assert(@_MPUSBReadInt <> nil);

  @_MPUSBWrite := GetProcAddress(UsbDllHandle,'_MPUSBWrite');
  Assert(@_MPUSBWrite <> nil);

  Result := UsbDllHandle;

end;

procedure NeedDLL;
begin
  If UsbDllHandle = 0 then
    LoadDll;
end;



function Pic32Usb_GetDllVersion : integer;
// Returns a number representing the DLL version.
begin
  NeedDLL;
  Result := _MPUSBGetDLLVersion();
end;


function Pic32Usb_GetDeviceCount( const AVendorProductID : string ) : integer;
// Returns the number of devices with this vendor ID
var
  bufVendorProductID : TAnsiCharBuf64;
begin
  NeedDLL;

  StrPCopy( bufVendorProductID, AnsiString(AVendorProductID) );

  Result := _MPUSBGetDeviceCount( @bufVendorProductID );
end;


function Pic32Usb_Open(
           AInstance : DWORD;
           const AVendorProductID, AEndpointID : string;
           ADirectionCode : integer;
           ARaise : boolean ) : THANDLE;
// Opens an endpoint. Can raise an exception if no valid handle is returned.
var
  bufVendorProductID, bufEndpointID : TAnsiCharBuf64;
begin
  NeedDLL;

  StrPCopy( bufVendorProductID, AnsiString(AVendorProductID) );
  StrPCopy( bufEndpointID, AnsiString(AEndpointID) );

  Result := _MPUSBOpen(
    AInstance,
    @bufVendorProductID,
    @bufEndpointID,
    DWORD(ADirectionCode),
    0 );

  if Result = 0 then
    If ARaise then
      Raise EArtPic32Usb.CreateFmt(
        'Unable to open USB device "%s", endpoint "%s"', [AVendorProductID, AEndpointID]);
end;


procedure Pic32Usb_Close( AHandle : THandle );
begin
  If UsbDllHandle <> 0 then
    _MPUSBClose( AHandle );
end;


function Pic32Usb_Write(
            AHandle              : THANDLE;
            ABufferPtr           : pointer;
            ALengthBytes         : integer ) : DWORD;
// Performs a write using this handle. Returns the number of bytes written
var
  I : integer;
begin
  I := _MPUSBWrite(
    AHandle,
    ABufferPtr,
    DWORD( ALengthBytes ),
    Result,
    MillisecondPollingInterval );
  if I <> 1 then
    Raise EArtPic32Usb.CreateFmt( 'Error performing USB write', []);
end;


function Pic32Usb_Read(
            AHandle              : THANDLE;
            ABufferPtr           : pointer;
            ALengthBytes         : integer ) : DWORD;
// Performs a Read using this handle. Returns the number of bytes read
var
  I : integer;
begin
  I := _MPUSBRead(
    AHandle,
    ABufferPtr,
    DWORD( ALengthBytes ),
    Result,
    MillisecondPollingInterval );
  if I <> 1 then
    Raise EArtPic32Usb.CreateFmt( 'Error performing USB read', []);
end;



initialization
finalization
  If UsbDllHandle <> 0 then
    FreeLibrary(UsbDllHandle)
end.
+1  A: 

I'll post this as temporary answer. @Brian Frost can post the full code. The gist of the story is to use single byte ANSI string when you are dealing with a driver that expects it.

StrPCopy( bufVendorProductID, AnsiString(AVendorProductID) );
eed3si9n