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 *************************************