openblt/Host/Source/MicroBoot/interfaces/can/lawicel/CanUsb.pas

497 lines
17 KiB
ObjectPascal

unit CanUsb;
//***************************************************************************************
// Description: Lawicel CANUSB API interface wrapper.
// File Name: CanUsb.pas
//
//---------------------------------------------------------------------------------------
// C O P Y R I G H T
//---------------------------------------------------------------------------------------
// Copyright (c) 2016 by Feaser http://www.feaser.com All rights reserved
//
// This software has been carefully tested, but is not guaranteed for any particular
// purpose. The author does not offer any warranties and does not guarantee the accuracy,
// adequacy, or completeness of the software and is not responsible for any errors or
// omissions or the results obtained from use of the software.
//
//---------------------------------------------------------------------------------------
// L I C E N S E
//---------------------------------------------------------------------------------------
// This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You have received a copy of the GNU General Public License along with OpenBLT. It
// should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
//
//***************************************************************************************
interface
//***************************************************************************************
// Includes
//***************************************************************************************
uses
Windows, Messages, SysUtils, Classes;
//***************************************************************************************
// Global constant declarations
//***************************************************************************************
const
// filter mask settings
CANUSB_ACCEPTANCE_CODE_ALL = $00000000;
CANUSB_ACCEPTANCE_MASK_ALL = $FFFFFFFF;
// message flags
CANMSG_EXTENDED = $80;
CANMSG_RTR = $40;
// status bits
CANSTATUS_RECEIVE_FIFO_FULL = $01;
CANSTATUS_TRANSMIT_FIFO_FULL = $02;
CANSTATUS_ERROR_WARNING = $04;
CANSTATUS_DATA_OVERRUN = $08;
CANSTATUS_ERROR_PASSIVE = $20;
CANSTATUS_ARBITRATION_LOST = $40;
CANSTATUS_BUS_ERROR = $80;
//***************************************************************************************
// Type Definitions
//***************************************************************************************
type
// CAN handle to the actual hardware adapter
CANHANDLE = Longint;
// CAN baudrate identifiers
CANBaudrate = ( CAN_BAUD_1M = 0, // 1 MBit/sec
CAN_BAUD_800K = 1, // 800 kBit/sec
CAN_BAUD_500K = 2, // 500 kBit/sec
CAN_BAUD_250K = 3, // 250 kBit/sec
CAN_BAUD_125K = 4, // 125 kBit/sec
CAN_BAUD_100K = 5, // 100 kBit/sec
CAN_BAUD_50K = 6, // 50 kBit/sec
CAN_BAUD_20K = 7, // 20 kBit/sec
CAN_BAUD_10K = 8 // 10 kBit/sec
);
// CAN Frame
CANMsg = record
id : Longword; // message id
timestamp : Longword; // timestamp in
flags : Byte; // [extended_id|1][RTR:1][reserver:6]
len : Byte; // frame size (0.8)
data : array[0..7] of Byte; // databytes 0..7
end;
// DLL interface methods
TDllCanUsbOpen = function(szID: PAnsiChar; szBitrate: PAnsiChar; acceptance_code: Longword; acceptance_mask: Longword; flags: Longword): CANHANDLE; stdcall;
TDllCanUsbClose = function(h: CANHANDLE): Integer; stdcall;
TDllCanUsbRead = function(h: CANHANDLE; var msg: CANMsg): Integer; stdcall;
TDllCanUsbWrite = function(h: CANHANDLE; var msg: CANMsg): Integer; stdcall;
TDllCanUsbStatus = function(h: CANHANDLE): Integer; stdcall;
// CANUSB API interface wrapper class
TCanUsb = class(TObject)
private
{ Private declarations }
FDllCanUsbOpen: TDllCanUsbOpen;
FDllCanUsbClose: TDllCanUsbClose;
FDllCanUsbRead: TDllCanUsbRead;
FDllCanUsbWrite: TDllCanUsbWrite;
FDllCanUsbStatus: TDllCanUsbStatus;
FHCanUsbAdapter: CANHANDLE;
FHCanUsbLib: THandle;
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create;
destructor Destroy; override;
function LoadDll: Boolean;
procedure UnloadDll;
function IsDllLoaded: Boolean;
function Connect(baudRate: CANBaudrate; acceptanceCode: Longword; acceptanceMask: Longword): Boolean;
function Disconnect: Boolean;
function Transmit(msg: CANMsg): Boolean;
function Receive(var msg: CANMsg): Boolean;
function Status: Integer;
procedure FindOptimumSingleRxFilter(id: Longword; ext: Boolean; var code: Longword; var mask: Longword);
end;
implementation
//***************************************************************************************
// Local constant declarations
//***************************************************************************************
const
CANBaudrateVals: array[0..8] of AnsiString =
( '1000', // CAN_BAUD_1M
'800', // CAN_BAUD_800K
'500', // CAN_BAUD_500K
'250', // CAN_BAUD_250K
'125', // CAN_BAUD_125K
'100', // CAN_BAUD_100K
'50', // CAN_BAUD_50K
'20' , // CAN_BAUD_20K
'10' // CAN_BAUD_10K
) ;
// error return codes
ERROR_CANUSB_OK = 1;
ERROR_CANUSB_GENERAL = -(1);
ERROR_CANUSB_OPEN_SUBSYSTEM = -(2);
ERROR_CANUSB_COMMAND_SUBSYSTEM = -(3);
ERROR_CANUSB_NOT_OPEN = -(4);
ERROR_CANUSB_TX_FIFO_FULL = -(5);
ERROR_CANUSB_INVALID_PARAM = -(6);
ERROR_CANUSB_NO_MESSAGE = -(7);
ERROR_CANUSB_MEMORY_ERROR = -(8);
ERROR_CANUSB_NO_DEVICE = -(9);
ERROR_CANUSB_TIMEOUT = -(10);
ERROR_CANUSB_INVALID_HARDWARE = -(11);
//***************************************************************************************
// NAME: Create
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Object constructor. Calls TObject's constructor and initializes the
// private member variables to their default values.
//
//***************************************************************************************
constructor TCanUsb.Create;
begin
// call inherited constructor
inherited Create;
// initialize private members
FHCanUsbLib := 0;
FHCanUsbAdapter := 0;
FDllCanUsbOpen := nil;
FDllCanUsbClose := nil;
FDllCanUsbRead := nil;
FDllCanUsbWrite := nil;
FDllCanUsbStatus := nil;
end; //*** end of Create ***
//***************************************************************************************
// NAME: Destroy
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Object destructor. Calls TObject's destructor
//
//***************************************************************************************
destructor TCanUsb.Destroy;
begin
// clean up by unloading the dll
UnloadDll;
// call inherited destructor
inherited Destroy;
end; //*** end of Destroy ***
//***************************************************************************************
// NAME: LoadDll
// PARAMETER: none
// RETURN VALUE: True if the DLL was successfully loaded, False otherwise.
// DESCRIPTION: Loads the CANUSB API dll.
//
//***************************************************************************************
function TCanUsb.LoadDll: Boolean;
begin
// init result
Result := True;
// nothing to do if the dll is already loaded
if IsDllLoaded then
begin
Exit;
end;
// attempt to load the CANUSB API dll
FHCanUsbLib := LoadLibrary(PChar('CANUSBDRV.DLL'));
// check result
if FHCanUsbLib = 0 then
begin
Result := False;
Exit;
end;
// still here so library loaded. attempt to obtain the function pointers
@FDllCanUsbOpen := GetProcAddress(FHCanUsbLib, 'canusb_Open');
@FDllCanUsbClose := GetProcAddress(FHCanUsbLib, 'canusb_Close');
@FDllCanUsbRead := GetProcAddress(FHCanUsbLib, 'canusb_Read');
@FDllCanUsbWrite := GetProcAddress(FHCanUsbLib, 'canusb_Write');
@FDllCanUsbStatus := GetProcAddress(FHCanUsbLib, 'canusb_Status');
// check if the functions were found in the interface library
if not Assigned(FDllCanUsbOpen) then Result := False;
if not Assigned(FDllCanUsbClose) then Result := False;
if not Assigned(FDllCanUsbRead) then Result := False;
if not Assigned(FDllCanUsbWrite) then Result := False;
if not Assigned(FDllCanUsbStatus) then Result := False;
// check if functions were all successfully loaded
if not Result then
begin
FreeLibrary(FHCanUsbLib);
FHCanUsbLib := 0;
end;
end; //*** end of LoadDll ***
//***************************************************************************************
// NAME: UnloadDll
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Unloads the CANUSB API dll.
//
//***************************************************************************************
procedure TCanUsb.UnloadDll;
begin
// only continue if the dll is actually loaded at this point
if not IsDllLoaded then
begin
Exit;
end;
// make sure that the connection with the CANUSB adapter is closed
Disconnect;
// unload the DLL
FreeLibrary(FHCanUsbLib);
FHCanUsbLib := 0;
end; //*** end of UnloadDll ***
//***************************************************************************************
// NAME: IsDllLoaded
// PARAMETER: none
// RETURN VALUE: True if the DLL is loaded, False otherwise.
// DESCRIPTION: Determines if the CANUSB API dll is currently loaded.
//
//***************************************************************************************
function TCanUsb.IsDllLoaded: Boolean;
begin
Result := (FHCanUsbLib <> 0);
end; //*** end of IsDllLoaded ***
//***************************************************************************************
// NAME: Connect
// PARAMETER: baudRate Baudrate id.
// acceptanceCode Code part of the acceptance filter. Set to
// CANUSB_ACCEPTANCE_CODE_ALL to get all messages.
// acceptanceMask Mask part of the acceptance filter. Set to
// CANUSB_ACCEPTANCE_MASk_ALL to get all messages.
// RETURN VALUE: True if successful, False otherwise.
// DESCRIPTION: Opens the connection with the first CANUSB hardware adapter found.
//
//***************************************************************************************
function TCanUsb.Connect(baudRate: CANBaudrate; acceptanceCode: Longword; acceptanceMask: Longword): Boolean;
begin
// initialize the result
Result := True;
// do not continue if the DLL is not loaded
if not IsDllLoaded then
begin
Result := False;
Exit;
end;
// make sure the connection is closed before opening
Disconnect;
// open the connection
FHCanUsbAdapter := FDllCanUsbOpen(nil, PAnsiChar(CANBaudrateVals[Ord(baudRate)]), acceptanceCode, acceptanceMask, 0);
// check the result
if FHCanUsbAdapter <= 0 then
begin
Result := False;
FHCanUsbAdapter := 0;
end;
end; //*** end of Connect ***
//***************************************************************************************
// NAME: Disconnect
// PARAMETER: none
// RETURN VALUE: True if successful, False otherwise.
// DESCRIPTION: Closes the connection with the CANUSB hardware adapter
//
//***************************************************************************************
function TCanUsb.Disconnect: Boolean;
begin
// initialize the result
Result := True;
// only continue if the DLL is loaded
if IsDllLoaded then
begin
// check if the connection with the CANUSB adapter is open
if FHCanUsbAdapter <> 0 then
begin
// close the connection and set the result
Result := (FDllCanUsbClose(FHCanUsbAdapter) > 0);
FHCanUsbAdapter := 0;
end;
end;
end; //*** end of Disconnect ***
//***************************************************************************************
// NAME: Transmit
// PARAMETER: msg CAN message to transmit.
// RETURN VALUE: True if successful, False otherwise.
// DESCRIPTION: Submits a CAN message for transmission.
//
//***************************************************************************************
function TCanUsb.Transmit(msg: CANMsg): Boolean;
begin
// only continue if the DLL is loaded
if not IsDllLoaded then
begin
Result := False;
Exit;
end;
// check if the connection with the CANUSB adapter is open
if FHCanUsbAdapter = 0 then
begin
Result := False;
Exit;
end;
// submit message for transmission and set the result
Result := (FDllCanUsbWrite(FHCanUsbAdapter, msg) = ERROR_CANUSB_OK);
end; //*** end of Transmit ***
//***************************************************************************************
// NAME: Receive
// PARAMETER: msg CAN message to store received message.
// RETURN VALUE: True if successful, False otherwise.
// DESCRIPTION: Receives the oldest message from the receive fifo, if one is present.
//
//***************************************************************************************
function TCanUsb.Receive(var msg: CANMsg): Boolean;
begin
// only continue if the DLL is loaded
if not IsDllLoaded then
begin
Result := False;
Exit;
end;
// check if the connection with the CANUSB adapter is open
if FHCanUsbAdapter = 0 then
begin
Result := False;
Exit;
end;
// extract oldest message from the receive fifo, if one is present
Result := (FDllCanUsbRead(FHCanUsbAdapter, msg) = ERROR_CANUSB_OK);
end; //*** end of Receive ***
//***************************************************************************************
// NAME: Status
// PARAMETER: none
// RETURN VALUE: Status bits (CANSTATUS_xxx).
// DESCRIPTION: Obtains status of the CANUSB adapter.
//
//***************************************************************************************
function TCanUsb.Status: Integer;
begin
// init result
Result := 0;
// only continue if the DLL is loaded
if not IsDllLoaded then
begin
Exit;
end;
// check if the connection with the CANUSB adapter is open
if FHCanUsbAdapter = 0 then
begin
Exit;
end;
// read and return status bits
Result := FDllCanUsbStatus(FHCanUsbAdapter);
end; //*** end of Status ***
//***************************************************************************************
// NAME: FindOptimumSingleRxFilter
// PARAMETER: id CAN message identifier to optimize the filter for.
// ext True if the id is 29-bit, False otherwise.
// code Buffer for storing the code part of the acceptance filter.
// mask Buffer for storing the mask part of the acceptance filter.
// RETURN VALUE: none
// DESCRIPTION: Finds the best code and mask values for receiving just a single CAN
// message with the reception acceptance filter. For 11-bit identifiers,
// this will find a perfect match, for 29-bit identfiers, it will always
// still let a group of messages pass because bits 0..12 are always
// don't care.
//
//***************************************************************************************
procedure TCanUsb.FindOptimumSingleRxFilter(id: Longword; ext: Boolean; var code: Longword; var mask: Longword);
var
ACR0, ACR1, ACR2, ACR3: Byte;
AMR0, AMR1, AMR2, AMR3: Byte;
begin
// CANUSB's SJA1000 is in dual filter mode. this means it can be set to receive 1 single
// 11-bit identifier or a small group of 29-bit identifiers.
if not ext then
begin
ACR0 := Byte(id shr 3);
AMR0 := $00;
ACR1 := (Byte(id shl 5) or $1f);
AMR1 := $1F;
ACR2 := Byte(id shr 3);
AMR2 := $00;
ACR3 := (Byte(id shl 5) or $1f);
AMR3 := $1F;
end
else
begin
ACR0 := Byte(id shr 21);
AMR0 := $00;
ACR1 := Byte(id shr 13);
AMR1 := $00;
ACR2 := Byte(id shr 21);
AMR2 := $00;
ACR3 := Byte(id shr 13);
AMR3 := $00;
end;
// set the results
code := (ACR3 shl 24) and $ff000000;
code := code or ((ACR2 shl 16) and $00ff0000);
code := code or ((ACR1 shl 8) and $0000ff00);
code := code or ((ACR0 shl 0) and $000000ff);
mask := (AMR3 shl 24) and $ff000000;
mask := mask or ((AMR2 shl 16) and $00ff0000);
mask := mask or ((AMR1 shl 8) and $0000ff00);
mask := mask or ((AMR0 shl 0) and $000000ff);
end; //*** end of FindOptimumSingleRxFilter ***
end.
//******************************* end of CanUsb.pas *************************************