diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 44b72be62..9627c8fd1 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -30,6 +30,7 @@ # Import Modules # ============== from can import BusABC, Message +from can.util import len2dlc, dlc2len from .exceptions import VectorError # Define Module Logger @@ -49,7 +50,7 @@ class VectorBus(BusABC): def __init__(self, channel, can_filters=None, poll_interval=0.01, receive_own_messages=False, - bitrate=None, rx_queue_size=256, app_name="CANalyzer", **config): + bitrate=None, rx_queue_size=2**14, app_name="CANalyzer", fd=False, data_bitrate=None, sjwAbr=2, tseg1Abr=6, tseg2Abr=3, sjwDbr=2, tseg1Dbr=6, tseg2Dbr=3, **config): """ :param list channel: The channel indexes to create this bus with. @@ -59,9 +60,16 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, :param int bitrate: Bitrate in bits/s. :param int rx_queue_size: - Number of messages in receive queue. + Number of messages in receive queue (power of 2). + CAN: range 16…32768 + CAN-FD: range 8192…524288 :param str app_name: Name of application in Hardware Config. + :param bool fd: + If CAN-FD frames should be supported. + :param int data_bitrate: + Which bitrate to use for data phase in CAN FD. + Defaults to arbitration bitrate. """ if vxlapi is None: raise ImportError("The Vector API has not been loaded") @@ -80,6 +88,7 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, vxlapi.xlOpenDriver() self.port_handle = vxlapi.XLportHandle(vxlapi.XL_INVALID_PORTHANDLE) self.mask = 0 + self.fd = fd # Get channels masks for channel in self.channels: hw_type = ctypes.c_uint(0) @@ -96,18 +105,48 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, permission_mask = vxlapi.XLaccess() # Set mask to request channel init permission if needed - if bitrate: + if bitrate or fd: permission_mask.value = self.mask - vxlapi.xlOpenPort(self.port_handle, self._app_name, self.mask, - permission_mask, rx_queue_size, - vxlapi.XL_INTERFACE_VERSION, vxlapi.XL_BUS_TYPE_CAN) + if fd: + vxlapi.xlOpenPort(self.port_handle, self._app_name, self.mask, + permission_mask, rx_queue_size, + vxlapi.XL_INTERFACE_VERSION_V4, vxlapi.XL_BUS_TYPE_CAN) + else: + vxlapi.xlOpenPort(self.port_handle, self._app_name, self.mask, + permission_mask, rx_queue_size, + vxlapi.XL_INTERFACE_VERSION, vxlapi.XL_BUS_TYPE_CAN) LOG.debug( 'Open Port: PortHandle: %d, PermissionMask: 0x%X', self.port_handle.value, permission_mask.value) - if bitrate: - if permission_mask.value != self.mask: - LOG.info('Can not set bitrate since no init access') - vxlapi.xlCanSetChannelBitrate(self.port_handle, permission_mask, bitrate) + + if permission_mask.value == self.mask: + if fd: + self.canFdConf = vxlapi.XLcanFdConf() + if bitrate: + self.canFdConf.arbitrationBitRate = ctypes.c_uint(bitrate) + else: + self.canFdConf.arbitrationBitRate = ctypes.c_uint(500000) + self.canFdConf.sjwAbr = ctypes.c_uint(sjwAbr) + self.canFdConf.tseg1Abr = ctypes.c_uint(tseg1Abr) + self.canFdConf.tseg2Abr = ctypes.c_uint(tseg2Abr) + if data_bitrate: + self.canFdConf.dataBitRate = ctypes.c_uint(data_bitrate) + else: + self.canFdConf.dataBitRate = self.canFdConf.arbitrationBitRate + self.canFdConf.sjwDbr = ctypes.c_uint(sjwDbr) + self.canFdConf.tseg1Dbr = ctypes.c_uint(tseg1Dbr) + self.canFdConf.tseg2Dbr = ctypes.c_uint(tseg2Dbr) + + vxlapi.xlCanFdSetConfiguration(self.port_handle, self.mask, self.canFdConf) + LOG.info('SetFdConfig.: ABaudr.=%u, DBaudr.=%u', self.canFdConf.arbitrationBitRate, self.canFdConf.dataBitRate) + LOG.info('SetFdConfig.: sjwAbr=%u, tseg1Abr=%u, tseg2Abr=%u', self.canFdConf.sjwAbr, self.canFdConf.tseg1Abr, self.canFdConf.tseg2Abr) + LOG.info('SetFdConfig.: sjwDbr=%u, tseg1Dbr=%u, tseg2Dbr=%u', self.canFdConf.sjwDbr, self.canFdConf.tseg1Dbr, self.canFdConf.tseg2Dbr) + else: + if bitrate: + vxlapi.xlCanSetChannelBitrate(self.port_handle, permission_mask, bitrate) + LOG.info('SetChannelBitrate: baudr.=%u',bitrate) + else: + LOG.info('No init access!') # Enable/disable TX receipts tx_receipts = 1 if receive_own_messages else 0 @@ -154,31 +193,63 @@ def set_filters(self, can_filters=None): def recv(self, timeout=None): end_time = time.time() + timeout if timeout is not None else None - event = vxlapi.XLevent(0) - event_count = ctypes.c_uint() + + if self.fd: + event = vxlapi.XLcanRxEvent() + else: + event = vxlapi.XLevent() + event_count = ctypes.c_uint() + while True: - event_count.value = 1 - try: - vxlapi.xlReceive(self.port_handle, event_count, event) - except VectorError as exc: - if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY: - raise + if self.fd: + try: + vxlapi.xlCanReceive(self.port_handle, event) + except VectorError as exc: + if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY: + raise + else: + if event.tag == vxlapi.XL_CAN_EV_TAG_RX_OK or event.tag == vxlapi.XL_CAN_EV_TAG_TX_OK: + msg_id = event.tagData.canRxOkMsg.canId + dlc = dlc2len(event.tagData.canRxOkMsg.dlc) + flags = event.tagData.canRxOkMsg.msgFlags + timestamp = event.timeStamp * 1e-9 + msg = Message( + timestamp=timestamp + self._time_offset, + arbitration_id=msg_id & 0x1FFFFFFF, + extended_id=bool(msg_id & vxlapi.XL_CAN_EXT_MSG_ID), + is_remote_frame=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_RTR), + is_error_frame=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_EF), + is_fd=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_EDL), + error_state_indicator=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_ESI), + bitrate_switch=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_BRS), + dlc=dlc, + data=event.tagData.canRxOkMsg.data[:dlc], + channel=event.chanIndex) + return msg else: - if event.tag == vxlapi.XL_RECEIVE_MSG: - msg_id = event.tagData.msg.id - dlc = event.tagData.msg.dlc - flags = event.tagData.msg.flags - timestamp = event.timeStamp * 1e-9 - msg = Message( - timestamp=timestamp + self._time_offset, - arbitration_id=msg_id & 0x1FFFFFFF, - extended_id=bool(msg_id & vxlapi.XL_CAN_EXT_MSG_ID), - is_remote_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME), - is_error_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_ERROR_FRAME), - dlc=dlc, - data=event.tagData.msg.data[:dlc], - channel=event.chanIndex) - return msg + event_count.value = 1 + try: + vxlapi.xlReceive(self.port_handle, event_count, event) + except VectorError as exc: + if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY: + raise + else: + if event.tag == vxlapi.XL_RECEIVE_MSG: + msg_id = event.tagData.msg.id + dlc = event.tagData.msg.dlc + flags = event.tagData.msg.flags + timestamp = event.timeStamp * 1e-9 + msg = Message( + timestamp=timestamp + self._time_offset, + arbitration_id=msg_id & 0x1FFFFFFF, + extended_id=bool(msg_id & vxlapi.XL_CAN_EXT_MSG_ID), + is_remote_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME), + is_error_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_ERROR_FRAME), + is_fd=False, + dlc=dlc, + data=event.tagData.msg.data[:dlc], + channel=event.chanIndex) + return msg if end_time is not None and time.time() > end_time: return None @@ -196,22 +267,52 @@ def recv(self, timeout=None): time.sleep(self.poll_interval) def send(self, msg, timeout=None): - message_count = ctypes.c_uint(1) msg_id = msg.arbitration_id + if msg.id_type: msg_id |= vxlapi.XL_CAN_EXT_MSG_ID + flags = 0 - if msg.is_remote_frame: - flags |= vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME - xl_event = vxlapi.XLevent() - xl_event.tag = vxlapi.XL_TRANSMIT_MSG - xl_event.tagData.msg.id = msg_id - xl_event.tagData.msg.dlc = msg.dlc - xl_event.tagData.msg.flags = flags - for idx, value in enumerate(msg.data): - xl_event.tagData.msg.data[idx] = value - vxlapi.xlCanTransmit(self.port_handle, self.mask, message_count, xl_event) + if self.fd: + if msg.is_fd: + flags |= vxlapi.XL_CAN_TXMSG_FLAG_EDL + if msg.bitrate_switch: + flags |= vxlapi.XL_CAN_TXMSG_FLAG_BRS + if msg.is_remote_frame: + flags |= vxlapi.XL_CAN_TXMSG_FLAG_RTR + + message_count = 1 + MsgCntSent = ctypes.c_uint(1) + + XLcanTxEvent = vxlapi.XLcanTxEvent() + XLcanTxEvent.tag = vxlapi.XL_CAN_EV_TAG_TX_MSG + XLcanTxEvent.transId = 0xffff + + XLcanTxEvent.tagData.canMsg.canId = msg_id + XLcanTxEvent.tagData.canMsg.msgFlags = flags + XLcanTxEvent.tagData.canMsg.dlc = len2dlc(msg.dlc) + for idx, value in enumerate(msg.data): + XLcanTxEvent.tagData.canMsg.data[idx] = value + vxlapi.xlCanTransmitEx(self.port_handle, self.mask, message_count, MsgCntSent, XLcanTxEvent) + + else: + if msg.is_remote_frame: + flags |= vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME + + message_count = ctypes.c_uint(1) + + xl_event = vxlapi.XLevent() + xl_event.tag = vxlapi.XL_TRANSMIT_MSG + + xl_event.tagData.msg.id = msg_id + xl_event.tagData.msg.dlc = msg.dlc + xl_event.tagData.msg.flags = flags + for idx, value in enumerate(msg.data): + xl_event.tagData.msg.data[idx] = value + vxlapi.xlCanTransmit(self.port_handle, self.mask, message_count, xl_event) + + def flush_tx_buffer(self): vxlapi.xlCanFlushTransmitQueue(self.port_handle, self.mask) @@ -224,3 +325,4 @@ def reset(self): vxlapi.xlDeactivateChannel(self.port_handle, self.mask) vxlapi.xlActivateChannel(self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0) + diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index 6612351b3..187dd9dd5 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -29,13 +29,25 @@ XL_ERR_QUEUE_IS_EMPTY = 10 XL_RECEIVE_MSG = 1 +XL_CAN_EV_TAG_RX_OK = 1024 +XL_CAN_EV_TAG_TX_OK = 1028 XL_TRANSMIT_MSG = 10 +XL_CAN_EV_TAG_TX_MSG = 1088 XL_CAN_EXT_MSG_ID = 0x80000000 XL_CAN_MSG_FLAG_ERROR_FRAME = 0x01 XL_CAN_MSG_FLAG_REMOTE_FRAME = 0x10 XL_CAN_MSG_FLAG_TX_COMPLETED = 0x40 +XL_CAN_TXMSG_FLAG_EDL = 0x0001 +XL_CAN_TXMSG_FLAG_BRS = 0x0002 +XL_CAN_TXMSG_FLAG_RTR = 0x0010 +XL_CAN_RXMSG_FLAG_EDL = 0x0001 +XL_CAN_RXMSG_FLAG_BRS = 0x0002 +XL_CAN_RXMSG_FLAG_ESI = 0x0004 +XL_CAN_RXMSG_FLAG_RTR = 0x0010 +XL_CAN_RXMSG_FLAG_EF = 0x0200 + XL_CAN_STD = 1 XL_CAN_EXT = 2 @@ -45,8 +57,13 @@ MAX_MSG_LEN = 8 +XL_CAN_MAX_DATA_LEN = 64 + # current version XL_INTERFACE_VERSION = 3 +XL_INTERFACE_VERSION_V4 = 4 + +XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT = 0x80000000 # structure for XL_RECEIVE_MSG, XL_TRANSMIT_MSG class s_xl_can_msg(ctypes.Structure): @@ -54,11 +71,50 @@ class s_xl_can_msg(ctypes.Structure): ('dlc', ctypes.c_ushort), ('res1', XLuint64), ('data', ctypes.c_ubyte * MAX_MSG_LEN), ('res2', XLuint64)] + + +class s_xl_can_ev_error(ctypes.Structure): + _fields_ = [('errorCode', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte * 95)] + +class s_xl_can_ev_chip_state(ctypes.Structure): + _fields_ = [('busStatus', ctypes.c_ubyte), ('txErrorCounter', ctypes.c_ubyte), + ('rxErrorCounter', ctypes.c_ubyte),('reserved', ctypes.c_ubyte), + ('reserved0', ctypes.c_uint)] + +class s_xl_can_ev_sync_pulse(ctypes.Structure): + _fields_ = [('triggerSource', ctypes.c_uint), ('reserved', ctypes.c_uint), + ('time', XLuint64)] + # BASIC bus message structure class s_xl_tag_data(ctypes.Union): _fields_ = [('msg', s_xl_can_msg)] - +# CAN FD messages +class s_xl_can_ev_rx_msg(ctypes.Structure): + _fields_ = [('canId', ctypes.c_uint), ('msgFlags', ctypes.c_uint), + ('crc', ctypes.c_uint), ('reserved1', ctypes.c_ubyte * 12), + ('totalBitCnt', ctypes.c_ushort), ('dlc', ctypes.c_ubyte), + ('reserved', ctypes.c_ubyte * 5), ('data', ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN)] + +class s_xl_can_ev_tx_request(ctypes.Structure): + _fields_ = [('canId', ctypes.c_uint), ('msgFlags', ctypes.c_uint), + ('dlc', ctypes.c_ubyte),('txAttemptConf', ctypes.c_ubyte), + ('reserved', ctypes.c_ushort), ('data', ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN)] + +class s_xl_can_tx_msg(ctypes.Structure): + _fields_ = [('canId', ctypes.c_uint), ('msgFlags', ctypes.c_uint), + ('dlc', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte * 7), + ('data', ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN)] + +class s_rxTagData(ctypes.Union): + _fields_ = [('canRxOkMsg', s_xl_can_ev_rx_msg), ('canTxOkMsg', s_xl_can_ev_rx_msg), + ('canTxRequest', s_xl_can_ev_tx_request),('canError', s_xl_can_ev_error), + ('canChipState', s_xl_can_ev_chip_state),('canSyncPulse', s_xl_can_ev_sync_pulse)] + +class s_txTagData(ctypes.Union): + _fields_ = [('canMsg', s_xl_can_tx_msg)] + +# BASIC events XLeventTag = ctypes.c_ubyte class XLevent(ctypes.Structure): @@ -67,6 +123,27 @@ class XLevent(ctypes.Structure): ('flags', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte), ('timeStamp', XLuint64), ('tagData', s_xl_tag_data)] +# CAN FD events +class XLcanRxEvent(ctypes.Structure): + _fields_ = [('size',ctypes.c_int),('tag', ctypes.c_ushort), + ('chanIndex', ctypes.c_ubyte),('reserved', ctypes.c_ubyte), + ('userHandle', ctypes.c_int),('flagsChip', ctypes.c_ushort), + ('reserved0', ctypes.c_ushort),('reserved1', XLuint64), + ('timeStamp', XLuint64),('tagData', s_rxTagData)] + +class XLcanTxEvent(ctypes.Structure): + _fields_ = [('tag', ctypes.c_ushort), ('transId', ctypes.c_ushort), + ('chanIndex', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte * 3), + ('tagData', s_txTagData)] + +# CAN FD configuration structure +class XLcanFdConf(ctypes.Structure): + _fields_ = [('arbitrationBitRate', ctypes.c_uint), ('sjwAbr', ctypes.c_uint), + ('tseg1Abr', ctypes.c_uint), ('tseg2Abr', ctypes.c_uint), + ('dataBitRate', ctypes.c_uint), ('sjwDbr', ctypes.c_uint), + ('tseg1Dbr', ctypes.c_uint), ('tseg2Dbr', ctypes.c_uint), + ('reserved', ctypes.c_uint * 2)] + # driver status XLstatus = ctypes.c_short @@ -150,6 +227,11 @@ def check_status(result, function, arguments): xlDeactivateChannel.restype = XLstatus xlDeactivateChannel.errcheck = check_status +xlCanFdSetConfiguration = _xlapi_dll.xlCanFdSetConfiguration +xlCanFdSetConfiguration.argtypes = [XLportHandle, XLaccess, ctypes.POINTER(XLcanFdConf)] +xlCanFdSetConfiguration.restype = XLstatus +xlCanFdSetConfiguration.errcheck = check_status + xlReceive = _xlapi_dll.xlReceive xlReceive.argtypes = [ XLportHandle, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLevent) @@ -157,6 +239,13 @@ def check_status(result, function, arguments): xlReceive.restype = XLstatus xlReceive.errcheck = check_status +xlCanReceive = _xlapi_dll.xlCanReceive +xlCanReceive.argtypes = [ + XLportHandle, ctypes.POINTER(XLcanRxEvent) +] +xlCanReceive.restype = XLstatus +xlCanReceive.errcheck = check_status + xlGetErrorString = _xlapi_dll.xlGetErrorString xlGetErrorString.argtypes = [XLstatus] xlGetErrorString.restype = ctypes.c_char_p @@ -173,6 +262,13 @@ def check_status(result, function, arguments): xlCanTransmit.restype = XLstatus xlCanTransmit.errcheck = check_status +xlCanTransmitEx = _xlapi_dll.xlCanTransmitEx +xlCanTransmitEx.argtypes = [ + XLportHandle, XLaccess, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLcanTxEvent) +] +xlCanTransmitEx.restype = XLstatus +xlCanTransmitEx.errcheck = check_status + xlCanFlushTransmitQueue = _xlapi_dll.xlCanFlushTransmitQueue xlCanFlushTransmitQueue.argtypes = [XLportHandle, XLaccess] xlCanFlushTransmitQueue.restype = XLstatus