Skip to content

Commit 1b85a33

Browse files
Add CAN-FD support
Initial support for socketcan_native and kvaser Experimental support for BLF logger Only warn on DLC too long since some loggers support long data lengths but not FD frames
1 parent cfc431a commit 1b85a33

File tree

11 files changed

+286
-76
lines changed

11 files changed

+286
-76
lines changed

can/interfaces/ixxat/canlib.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,14 +442,13 @@ def recv(self, timeout=None):
442442
# The _message.dwTime is a 32bit tick value and will overrun,
443443
# so expect to see the value restarting from 0
444444
rx_msg = Message(
445-
self._message.dwTime / self._tick_resolution, # Relative time in s
446-
True if self._message.uMsgInfo.Bits.rtr else False,
447-
True if self._message.uMsgInfo.Bits.ext else False,
448-
False,
449-
self._message.dwMsgId,
450-
self._message.uMsgInfo.Bits.dlc,
451-
self._message.abData[:self._message.uMsgInfo.Bits.dlc],
452-
self.channel
445+
timestamp=self._message.dwTime / self._tick_resolution, # Relative time in s
446+
is_remote_frame=True if self._message.uMsgInfo.Bits.rtr else False,
447+
extended_id=True if self._message.uMsgInfo.Bits.ext else False,
448+
arbitration_id=self._message.dwMsgId,
449+
dlc=self._message.uMsgInfo.Bits.dlc,
450+
data=self._message.abData[:self._message.uMsgInfo.Bits.dlc],
451+
channel=self.channel
453452
)
454453

455454
log.debug('Recv()ed message %s', rx_msg)

can/interfaces/kvaser/canlib.py

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ def __check_bus_handle_validity(handle, function, arguments):
169169
restype=canstat.c_canStatus,
170170
errcheck=__check_status)
171171

172+
canSetBusParamsFd = __get_canlib_function("canSetBusParamsFd",
173+
argtypes=[c_canHandle, ctypes.c_long,
174+
ctypes.c_uint, ctypes.c_uint,
175+
ctypes.c_uint],
176+
restype=canstat.c_canStatus,
177+
errcheck=__check_status)
172178

173179
canSetBusOutputControl = __get_canlib_function("canSetBusOutputControl",
174180
argtypes=[c_canHandle,
@@ -254,15 +260,25 @@ def init_kvaser_library():
254260
DRIVER_MODE_NORMAL = True
255261

256262

257-
BITRATE_OBJS = {1000000 : canstat.canBITRATE_1M,
258-
500000 : canstat.canBITRATE_500K,
259-
250000 : canstat.canBITRATE_250K,
260-
125000 : canstat.canBITRATE_125K,
261-
100000 : canstat.canBITRATE_100K,
262-
83000 : canstat.canBITRATE_83K,
263-
62000 : canstat.canBITRATE_62K,
264-
50000 : canstat.canBITRATE_50K,
265-
10000 : canstat.canBITRATE_10K}
263+
BITRATE_OBJS = {
264+
1000000: canstat.canBITRATE_1M,
265+
500000: canstat.canBITRATE_500K,
266+
250000: canstat.canBITRATE_250K,
267+
125000: canstat.canBITRATE_125K,
268+
100000: canstat.canBITRATE_100K,
269+
83000: canstat.canBITRATE_83K,
270+
62000: canstat.canBITRATE_62K,
271+
50000: canstat.canBITRATE_50K,
272+
10000: canstat.canBITRATE_10K
273+
}
274+
275+
BITRATE_FD = {
276+
500000: canstat.canFD_BITRATE_500K_80P,
277+
1000000: canstat.canFD_BITRATE_1M_80P,
278+
2000000: canstat.canFD_BITRATE_2M_80P,
279+
4000000: canstat.canFD_BITRATE_4M_80P,
280+
8000000: canstat.canFD_BITRATE_8M_60P
281+
}
266282

267283

268284
class KvaserBus(BusABC):
@@ -285,6 +301,8 @@ def __init__(self, channel, can_filters=None, **config):
285301
286302
:param int bitrate:
287303
Bitrate of channel in bit/s
304+
:param bool accept_virtual:
305+
If virtual channels should be accepted.
288306
:param int tseg1:
289307
Time segment 1, that is, the number of quanta from (but not including)
290308
the Sync Segment to the sampling point.
@@ -313,6 +331,11 @@ def __init__(self, channel, can_filters=None, **config):
313331
Only works if single_handle is also False.
314332
If you want to receive messages from other applications on the same
315333
computer, set this to True or set single_handle to True.
334+
:param bool fd:
335+
If CAN-FD frames should be supported.
336+
:param int data_bitrate:
337+
Which bitrate to use for data phase in CAN FD.
338+
Defaults to arbitration bitrate.
316339
"""
317340
log.info("CAN Filters: {}".format(can_filters))
318341
log.info("Got configuration of: {}".format(config))
@@ -324,16 +347,16 @@ def __init__(self, channel, can_filters=None, **config):
324347
driver_mode = config.get('driver_mode', DRIVER_MODE_NORMAL)
325348
single_handle = config.get('single_handle', False)
326349
receive_own_messages = config.get('receive_own_messages', False)
350+
accept_virtual = config.get('accept_virtual', True)
351+
fd = config.get('fd', False)
352+
data_bitrate = config.get('data_bitrate', None)
327353

328354
try:
329355
channel = int(channel)
330356
except ValueError:
331357
raise ValueError('channel must be an integer')
332358
self.channel = channel
333359

334-
if 'tseg1' not in config and bitrate in BITRATE_OBJS:
335-
bitrate = BITRATE_OBJS[bitrate]
336-
337360
log.debug('Initialising bus instance')
338361
self.single_handle = single_handle
339362

@@ -348,12 +371,33 @@ def __init__(self, channel, can_filters=None, **config):
348371
if idx == channel:
349372
self.channel_info = channel_info
350373

374+
flags = 0
375+
if accept_virtual:
376+
flags |= canstat.canOPEN_ACCEPT_VIRTUAL
377+
if fd:
378+
flags |= canstat.canOPEN_CAN_FD
379+
351380
log.debug('Creating read handle to bus channel: %s' % channel)
352-
self._read_handle = canOpenChannel(channel, canstat.canOPEN_ACCEPT_VIRTUAL)
381+
self._read_handle = canOpenChannel(channel, flags)
353382
canIoCtl(self._read_handle,
354383
canstat.canIOCTL_SET_TIMER_SCALE,
355384
ctypes.byref(ctypes.c_long(TIMESTAMP_RESOLUTION)),
356385
4)
386+
387+
if fd:
388+
if 'tseg1' not in config and bitrate in BITRATE_FD:
389+
# Use predefined bitrate for arbitration
390+
bitrate = BITRATE_FD[bitrate]
391+
if data_bitrate in BITRATE_FD:
392+
# Use predefined bitrate for data
393+
data_bitrate = BITRATE_FD[data_bitrate]
394+
elif not data_bitrate:
395+
# Use same bitrate for arbitration and data phase
396+
data_bitrate = bitrate
397+
canSetBusParamsFd(self._read_handle, bitrate, tseg1, tseg2, sjw)
398+
else:
399+
if 'tseg1' not in config and bitrate in BITRATE_OBJS:
400+
bitrate = BITRATE_OBJS[bitrate]
357401
canSetBusParams(self._read_handle, bitrate, tseg1, tseg2, sjw, no_samp, 0)
358402

359403
# By default, use local echo if single handle is used (see #160)
@@ -370,7 +414,7 @@ def __init__(self, channel, can_filters=None, **config):
370414
self._write_handle = self._read_handle
371415
else:
372416
log.debug('Creating separate handle for TX on channel: %s' % channel)
373-
self._write_handle = canOpenChannel(channel, canstat.canOPEN_ACCEPT_VIRTUAL)
417+
self._write_handle = canOpenChannel(channel, flags)
374418
canBusOn(self._read_handle)
375419

376420
self.set_filters(can_filters)
@@ -437,7 +481,7 @@ def recv(self, timeout=None):
437481
Read a message from kvaser device.
438482
"""
439483
arb_id = ctypes.c_long(0)
440-
data = ctypes.create_string_buffer(8)
484+
data = ctypes.create_string_buffer(64)
441485
dlc = ctypes.c_uint(0)
442486
flags = ctypes.c_uint(0)
443487
timestamp = ctypes.c_ulong(0)
@@ -467,13 +511,19 @@ def recv(self, timeout=None):
467511
is_extended = bool(flags & canstat.canMSG_EXT)
468512
is_remote_frame = bool(flags & canstat.canMSG_RTR)
469513
is_error_frame = bool(flags & canstat.canMSG_ERROR_FRAME)
514+
is_fd = bool(flags & canstat.canFDMSG_FDF)
515+
bitrate_switch = bool(flags & canstat.canFDMSG_BRS)
516+
error_state_indicator = bool(flags & canstat.canFDMSG_ESI)
470517
msg_timestamp = timestamp.value * TIMESTAMP_FACTOR
471518
rx_msg = Message(arbitration_id=arb_id.value,
472519
data=data_array[:dlc.value],
473520
dlc=dlc.value,
474521
extended_id=is_extended,
475522
is_error_frame=is_error_frame,
476523
is_remote_frame=is_remote_frame,
524+
is_fd=is_fd,
525+
bitrate_switch=bitrate_switch,
526+
error_state_indicator=error_state_indicator,
477527
channel=self.channel,
478528
timestamp=msg_timestamp + self._timestamp_offset)
479529
rx_msg.flags = flags
@@ -491,6 +541,10 @@ def send(self, msg, timeout=None):
491541
flags |= canstat.canMSG_RTR
492542
if msg.is_error_frame:
493543
flags |= canstat.canMSG_ERROR_FRAME
544+
if msg.is_fd:
545+
flags |= canstat.canFDMSG_FDF
546+
if msg.bitrate_switch:
547+
flags |= canstat.canFDMSG_BRS
494548
ArrayConstructor = ctypes.c_byte * msg.dlc
495549
buf = ArrayConstructor(*msg.data)
496550
canWrite(self._write_handle,
@@ -520,9 +574,10 @@ def shutdown(self):
520574
# Wait for transmit queue to be cleared
521575
try:
522576
canWriteSync(self._write_handle, 100)
523-
except CANLIBError as e:
524-
log.warning("There may be messages in the transmit queue that could "
525-
"not be transmitted before going bus off (%s)", e)
577+
except CANLIBError:
578+
# Not a huge deal and it seems that we get timeout if no messages
579+
# exists in the buffer at all
580+
pass
526581
if not self.single_handle:
527582
canBusOff(self._read_handle)
528583
canClose(self._read_handle)

can/interfaces/kvaser/constants.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ def CANSTATUS_SUCCESS(status):
6161
canMSG_TXACK = 0x0040
6262
canMSG_TXRQ = 0x0080
6363

64+
canFDMSG_FDF = 0x010000
65+
canFDMSG_BRS = 0x020000
66+
canFDMSG_ESI = 0x040000
67+
6468
canMSGERR_MASK = 0xff00
6569
canMSGERR_HW_OVERRUN = 0x0200
6670
canMSGERR_SW_OVERRUN = 0x0400
@@ -159,6 +163,8 @@ def CANSTATUS_SUCCESS(status):
159163
canOPEN_REQUIRE_INIT_ACCESS = 0x0080
160164
canOPEN_NO_INIT_ACCESS = 0x0100
161165
canOPEN_ACCEPT_LARGE_DLC = 0x0200
166+
canOPEN_CAN_FD = 0x0400
167+
canOPEN_CAN_FD_NONISO = 0x0800
162168

163169
canIOCTL_GET_RX_BUFFER_LEVEL = 8
164170
canIOCTL_GET_TX_BUFFER_LEVEL = 9
@@ -230,3 +236,9 @@ def CANSTATUS_SUCCESS(status):
230236
canBITRATE_50K = -7
231237
canBITRATE_83K = -8
232238
canBITRATE_10K = -9
239+
240+
canFD_BITRATE_500K_80P = -1000
241+
canFD_BITRATE_1M_80P = -1001
242+
canFD_BITRATE_2M_80P = -1002
243+
canFD_BITRATE_4M_80P = -1003
244+
canFD_BITRATE_8M_60P = -1004

can/interfaces/socketcan/socketcan_constants.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
RX_ANNOUNCE_RESUME = 0x0100
3131
TX_RESET_MULTI_IDX = 0x0200
3232
RX_RTR_FRAME = 0x0400
33+
CAN_FD_FRAME = 0x0800
3334

3435
CAN_RAW = 1
3536
CAN_BCM = 2
@@ -58,6 +59,11 @@
5859
SKT_ERRFLG = 0x0001
5960
SKT_RTRFLG = 0x0002
6061

62+
CANFD_BRS = 0x01
63+
CANFD_ESI = 0x02
64+
65+
CANFD_MTU = 72
66+
6167
PYCAN_ERRFLG = 0x0020
6268
PYCAN_STDFLG = 0x0002
6369
PYCAN_RTRFLG = 0x0001

can/interfaces/socketcan/socketcan_ctypes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,9 @@ def _build_can_frame(message):
346346
# TODO need to understand the extended frame format
347347
frame = CAN_FRAME()
348348
frame.can_id = arbitration_id
349-
frame.can_dlc = len(message.data)
349+
frame.can_dlc = message.dlc
350350

351-
frame.data[0:frame.can_dlc] = message.data
351+
frame.data[0:len(message.data)] = message.data
352352

353353
log.debug("sizeof frame: %d", ctypes.sizeof(frame))
354354
return frame

0 commit comments

Comments
 (0)