unit XcpTransport; //*************************************************************************************** // Description: XCP transport layer for CAN. // File Name: XcpTransport.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, Forms, IniFiles, CanUsb; //*************************************************************************************** // Global Constants //*************************************************************************************** // a CAN message can only have up to 8 bytes const kMaxPacketSize = 8; //*************************************************************************************** // Type Definitions //*************************************************************************************** type TXcpTransport = class(TObject) private packetTxId : LongWord; packetRxId : Longword; extendedId : Boolean; canDriver : TCanUsb; canHardware : Integer; { not used right now } canChannel : Word; { currently supported is 1 } canBaudrate : CANBaudrate; { as enum } connected : Boolean; public packetData : array[0..kMaxPacketSize-1] of Byte; packetLen : Word; constructor Create; procedure Configure(iniFile : string); function Connect: Boolean; function SendPacket(timeOutms: LongWord): Boolean; function IsComError: Boolean; procedure Disconnect; destructor Destroy; override; end; implementation //*************************************************************************************** // NAME: Create // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class constructore // //*************************************************************************************** constructor TXcpTransport.Create; begin // call inherited constructor inherited Create; // construct the can driver object canDriver := TCanUsb.Create; // load the CAN driver's dll canDriver.LoadDll; // reset the packet ids packetTxId := 0; packetRxId := 0; // use standard id's by default extendedId := false; // reset packet length packetLen := 0; // disconnected by default connected := false; end; //*** end of Create *** //*************************************************************************************** // NAME: Destroy // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Class destructor // //*************************************************************************************** destructor TXcpTransport.Destroy; begin // unload the CAN driver's dll canDriver.UnloadDll; // release the CAN driver canDriver.Free; // call inherited destructor inherited; end; //*** end of Destroy *** //*************************************************************************************** // NAME: Configure // PARAMETER: filename of the INI // RETURN VALUE: none // DESCRIPTION: Configures both this class from the settings in the INI. // //*************************************************************************************** procedure TXcpTransport.Configure(iniFile : string); var settingsIni : TIniFile; begin // read XCP configuration from INI if FileExists(iniFile) then begin // create ini file object settingsIni := TIniFile.Create(iniFile); // set hardware configuration canHardware := settingsIni.ReadInteger('can', 'hardware', 0); canChannel := settingsIni.ReadInteger('can', 'channel', 0) + 1; case settingsIni.ReadInteger('can', 'baudrate', 2) of 0: canBaudrate := CAN_BAUD_1M; 1: canBaudrate := CAN_BAUD_800K; 2: canBaudrate := CAN_BAUD_500K; 3: canBaudrate := CAN_BAUD_250K; 4: canBaudrate := CAN_BAUD_125K; 5: canBaudrate := CAN_BAUD_100K; 6: canBaudrate := CAN_BAUD_50K; 7: canBaudrate := CAN_BAUD_20K; 8: canBaudrate := CAN_BAUD_10K; else canBaudrate := CAN_BAUD_500K; end; // set message configuration packetTxId := settingsIni.ReadInteger('can', 'txid', $667); packetRxId := settingsIni.ReadInteger('can', 'rxid', $7e1); extendedId := settingsIni.ReadBool('can', 'extended', false); // release ini file object settingsIni.Free; end; end; //*** end of Configure *** //*************************************************************************************** // NAME: Connect // PARAMETER: none // RETURN VALUE: True if successful, False otherwise. // DESCRIPTION: Connects the transport layer device. // //*************************************************************************************** function TXcpTransport.Connect: Boolean; var code, mask: Longword; begin // init result value result := false; // disconnect first if still connected if connected then Disconnect; // get the optimum setting for the acceptance filter for receiving just 1 identifier canDriver.FindOptimumSingleRxFilter(packetRxId, extendedId, code, mask); // attempt to connect to the CAN hardware interface if canDriver.Connect(canBaudrate, code, mask) then begin connected := true; result := true; end; end; //*** end of Connect *** //*************************************************************************************** // NAME: IsComError // PARAMETER: none // RETURN VALUE: True if in error state, False otherwise. // DESCRIPTION: Determines if the communication interface is in an error state. // //*************************************************************************************** function TXcpTransport.IsComError: Boolean; var status: Integer; begin // init result to no error. result := false; // check for bus off and bus heavy conditions status := canDriver.Status; if ((status and CANSTATUS_BUS_ERROR) <> 0) then begin result := true; end; end; //*** end of IsComError *** //*************************************************************************************** // NAME: SendPacket // PARAMETER: the time[ms] allowed for the reponse from the slave to come in. // RETURN VALUE: True if response received from slave, False otherwise // DESCRIPTION: Sends the XCP packet using the data in 'packetData' and length in // 'packetLen' and waits for the response to come in. // //*************************************************************************************** function TXcpTransport.SendPacket(timeOutms: LongWord): Boolean; var txMsg: CANMsg; rxMsg: CANMsg; byteIdx: Byte; responseReceived: Boolean; timeoutTime: DWORD; begin // initialize the result value result := false; // do not send data when the packet length is invalid or when not connected // to the CAN hardware if (packetLen > kMaxPacketSize) or (not connected) then begin Exit; end; // prepare the packet for transmission in a CAN message txMsg.id := packetTxId; if extendedId then txMsg.flags := CANMSG_EXTENDED else txMsg.flags := 0; txMsg.len := packetLen; for byteIdx := 0 to (packetLen-1) do begin txMsg.data[byteIdx] := packetData[byteIdx]; end; // transmit the packet via CAN if not canDriver.Transmit(txMsg) then begin Exit; end; // reset flag and set the reception timeout time responseReceived := false; timeoutTime := GetTickCount + timeOutms; // attempt to receive the packet response within the timeout time repeat // read out the next message in the receive queue if canDriver.Receive(rxMsg) then begin // was the newly received CAN message the response we are waiting for? if rxMsg.id = packetRxId then begin // was the id type also a match? if ((rxMsg.flags = 0) and (not extendedId)) or ((rxMsg.flags = CANMSG_EXTENDED) and (extendedId)) then begin // response received. set flag responseReceived := true; end; end; end; // give the application a chance to use the processor Application.ProcessMessages; until (GetTickCount > timeoutTime) or (responseReceived); // check if the response was correctly received if responseReceived then begin // copy the response for futher processing packetLen := rxMsg.len; for byteIdx := 0 to (packetLen-1) do begin packetData[byteIdx] := rxMsg.data[byteIdx]; end; // success result := true; end; end; //*** end of SendPacket *** //*************************************************************************************** // NAME: Disconnect // PARAMETER: none // RETURN VALUE: none // DESCRIPTION: Disconnects the transport layer device. // //*************************************************************************************** procedure TXcpTransport.Disconnect; begin // disconnect CAN interface if connected if connected then begin canDriver.Disconnect; end; connected := false; end; //*** end of Disconnect *** end. //******************************** end of XcpTransport.pas ******************************