Skip to content

Commit 8b6bfa9

Browse files
authored
Vector: refactor bit timing setup (#1516)
* add xlCanSetChannelParamsC200 * refactor VectorBus * move check into separate method * improve error message * use Optional instead of |
1 parent 9cd7b49 commit 8b6bfa9

File tree

3 files changed

+221
-211
lines changed

3 files changed

+221
-211
lines changed

can/interfaces/vector/canlib.py

Lines changed: 133 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ def __init__(
120120
:param timing:
121121
An instance of :class:`~can.BitTiming` or :class:`~can.BitTimingFd`
122122
to specify the bit timing parameters for the VectorBus interface. The
123-
`f_clock` value of the timing instance must be set to 16.000.000 (16MHz)
124-
for standard CAN or 80.000.000 (80MHz) for CAN FD. If this parameter is provided,
125-
it takes precedence over all other timing-related parameters.
123+
`f_clock` value of the timing instance must be set to 8_000_000 (8MHz)
124+
or 16_000_000 (16MHz) for CAN 2.0 or 80_000_000 (80MHz) for CAN FD.
125+
If this parameter is provided, it takes precedence over all other
126+
timing-related parameters.
126127
Otherwise, the bit timing can be specified using the following parameters:
127128
`bitrate` for standard CAN or `fd`, `data_bitrate`, `sjw_abr`, `tseg1_abr`,
128129
`tseg2_abr`, `sjw_dbr`, `tseg1_dbr`, and `tseg2_dbr` for CAN FD.
@@ -252,42 +253,34 @@ def __init__(
252253
# set CAN settings
253254
for channel in self.channels:
254255
if isinstance(timing, BitTiming):
255-
timing = check_or_adjust_timing_clock(timing, [16_000_000])
256-
self._set_bitrate_can(
256+
timing = check_or_adjust_timing_clock(timing, [16_000_000, 8_000_000])
257+
self._set_bit_timing(
257258
channel=channel,
258-
bitrate=timing.bitrate,
259-
sjw=timing.sjw,
260-
tseg1=timing.tseg1,
261-
tseg2=timing.tseg2,
262-
sam=timing.nof_samples,
259+
timing=timing,
263260
)
264261
elif isinstance(timing, BitTimingFd):
265262
timing = check_or_adjust_timing_clock(timing, [80_000_000])
266-
self._set_bitrate_canfd(
263+
self._set_bit_timing_fd(
267264
channel=channel,
268-
bitrate=timing.nom_bitrate,
269-
data_bitrate=timing.data_bitrate,
270-
sjw_abr=timing.nom_sjw,
271-
tseg1_abr=timing.nom_tseg1,
272-
tseg2_abr=timing.nom_tseg2,
273-
sjw_dbr=timing.data_sjw,
274-
tseg1_dbr=timing.data_tseg1,
275-
tseg2_dbr=timing.data_tseg2,
265+
timing=timing,
276266
)
277267
elif fd:
278-
self._set_bitrate_canfd(
268+
self._set_bit_timing_fd(
279269
channel=channel,
280-
bitrate=bitrate,
281-
data_bitrate=data_bitrate,
282-
sjw_abr=sjw_abr,
283-
tseg1_abr=tseg1_abr,
284-
tseg2_abr=tseg2_abr,
285-
sjw_dbr=sjw_dbr,
286-
tseg1_dbr=tseg1_dbr,
287-
tseg2_dbr=tseg2_dbr,
270+
timing=BitTimingFd.from_bitrate_and_segments(
271+
f_clock=80_000_000,
272+
nom_bitrate=bitrate or 500_000,
273+
nom_tseg1=tseg1_abr,
274+
nom_tseg2=tseg2_abr,
275+
nom_sjw=sjw_abr,
276+
data_bitrate=data_bitrate or bitrate or 500_000,
277+
data_tseg1=tseg1_dbr,
278+
data_tseg2=tseg2_dbr,
279+
data_sjw=sjw_dbr,
280+
),
288281
)
289282
elif bitrate:
290-
self._set_bitrate_can(channel=channel, bitrate=bitrate)
283+
self._set_bitrate(channel=channel, bitrate=bitrate)
291284

292285
# Enable/disable TX receipts
293286
tx_receipts = 1 if receive_own_messages else 0
@@ -404,30 +397,44 @@ def _read_bus_params(self, channel: int) -> "VectorBusParams":
404397
f"Channel configuration for channel {channel} not found."
405398
)
406399

407-
def _set_bitrate_can(
408-
self,
409-
channel: int,
410-
bitrate: int,
411-
sjw: Optional[int] = None,
412-
tseg1: Optional[int] = None,
413-
tseg2: Optional[int] = None,
414-
sam: int = 1,
415-
) -> None:
416-
kwargs = [sjw, tseg1, tseg2]
417-
if any(kwargs) and not all(kwargs):
418-
raise ValueError(
419-
f"Either all of sjw, tseg1, tseg2 must be set or none of them."
400+
def _set_bitrate(self, channel: int, bitrate: int) -> None:
401+
# set parameters if channel has init access
402+
if self._has_init_access(channel):
403+
self.xldriver.xlCanSetChannelBitrate(
404+
self.port_handle,
405+
self.channel_masks[channel],
406+
bitrate,
420407
)
408+
LOG.info("xlCanSetChannelBitrate: baudr.=%u", bitrate)
421409

410+
if not self.__testing:
411+
self._check_can_settings(
412+
channel=channel,
413+
bitrate=bitrate,
414+
)
415+
416+
def _set_bit_timing(self, channel: int, timing: BitTiming) -> None:
422417
# set parameters if channel has init access
423418
if self._has_init_access(channel):
424-
if any(kwargs):
419+
if timing.f_clock == 8_000_000:
420+
self.xldriver.xlCanSetChannelParamsC200(
421+
self.port_handle,
422+
self.channel_masks[channel],
423+
timing.btr0,
424+
timing.btr1,
425+
)
426+
LOG.info(
427+
"xlCanSetChannelParamsC200: BTR0=%#02x, BTR1=%#02x",
428+
timing.btr0,
429+
timing.btr1,
430+
)
431+
elif timing.f_clock == 16_000_000:
425432
chip_params = xlclass.XLchipParams()
426-
chip_params.bitRate = bitrate
427-
chip_params.sjw = sjw
428-
chip_params.tseg1 = tseg1
429-
chip_params.tseg2 = tseg2
430-
chip_params.sam = sam
433+
chip_params.bitRate = timing.bitrate
434+
chip_params.sjw = timing.sjw
435+
chip_params.tseg1 = timing.tseg1
436+
chip_params.tseg2 = timing.tseg2
437+
chip_params.sam = timing.nof_samples
431438
self.xldriver.xlCanSetChannelParams(
432439
self.port_handle,
433440
self.channel_masks[channel],
@@ -441,94 +448,33 @@ def _set_bitrate_can(
441448
chip_params.tseg2,
442449
)
443450
else:
444-
self.xldriver.xlCanSetChannelBitrate(
445-
self.port_handle,
446-
self.channel_masks[channel],
447-
bitrate,
451+
raise CanInitializationError(
452+
f"timing.f_clock must be 8_000_000 or 16_000_000 (is {timing.f_clock})"
448453
)
449-
LOG.info("xlCanSetChannelBitrate: baudr.=%u", bitrate)
450-
451-
if self.__testing:
452-
return
453454

454-
# Compare requested CAN settings to active settings
455-
bus_params = self._read_bus_params(channel)
456-
settings_acceptable = True
457-
458-
# check bus type
459-
settings_acceptable &= (
460-
bus_params.bus_type is xldefine.XL_BusTypes.XL_BUS_TYPE_CAN
461-
)
462-
463-
# check CAN operation mode. For CANcaseXL can_op_mode remains 0
464-
if bus_params.can.can_op_mode != 0:
465-
settings_acceptable &= bool(
466-
bus_params.can.can_op_mode
467-
& xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CAN20
468-
)
469-
470-
# check bitrate
471-
settings_acceptable &= abs(bus_params.can.bitrate - bitrate) < bitrate / 256
472-
473-
# check sample point
474-
if all(kwargs):
475-
requested_sample_point = (
476-
100
477-
* (1 + tseg1) # type: ignore[operator]
478-
/ (1 + tseg1 + tseg2) # type: ignore[operator]
479-
)
480-
actual_sample_point = (
481-
100
482-
* (1 + bus_params.can.tseg1)
483-
/ (1 + bus_params.can.tseg1 + bus_params.can.tseg2)
484-
)
485-
settings_acceptable &= (
486-
abs(actual_sample_point - requested_sample_point)
487-
< 1.0 # 1 percent threshold
488-
)
489-
490-
if not settings_acceptable:
491-
active_settings = ", ".join(
492-
[
493-
f"{key}: {getattr(val, 'name', val)}" # print int or Enum/Flag name
494-
for key, val in bus_params.can._asdict().items()
495-
]
496-
)
497-
raise CanInitializationError(
498-
f"The requested CAN settings could not be set for channel {channel}. "
499-
f"Another application might have set incompatible settings. "
500-
f"These are the currently active settings: {active_settings}"
455+
if not self.__testing:
456+
self._check_can_settings(
457+
channel=channel,
458+
bitrate=timing.bitrate,
459+
sample_point=timing.sample_point,
501460
)
502461

503-
def _set_bitrate_canfd(
462+
def _set_bit_timing_fd(
504463
self,
505464
channel: int,
506-
bitrate: Optional[int] = None,
507-
data_bitrate: Optional[int] = None,
508-
sjw_abr: int = 2,
509-
tseg1_abr: int = 6,
510-
tseg2_abr: int = 3,
511-
sjw_dbr: int = 2,
512-
tseg1_dbr: int = 6,
513-
tseg2_dbr: int = 3,
465+
timing: BitTimingFd,
514466
) -> None:
515467
# set parameters if channel has init access
516468
if self._has_init_access(channel):
517469
canfd_conf = xlclass.XLcanFdConf()
518-
if bitrate:
519-
canfd_conf.arbitrationBitRate = int(bitrate)
520-
else:
521-
canfd_conf.arbitrationBitRate = 500_000
522-
canfd_conf.sjwAbr = int(sjw_abr)
523-
canfd_conf.tseg1Abr = int(tseg1_abr)
524-
canfd_conf.tseg2Abr = int(tseg2_abr)
525-
if data_bitrate:
526-
canfd_conf.dataBitRate = int(data_bitrate)
527-
else:
528-
canfd_conf.dataBitRate = int(canfd_conf.arbitrationBitRate)
529-
canfd_conf.sjwDbr = int(sjw_dbr)
530-
canfd_conf.tseg1Dbr = int(tseg1_dbr)
531-
canfd_conf.tseg2Dbr = int(tseg2_dbr)
470+
canfd_conf.arbitrationBitRate = timing.nom_bitrate
471+
canfd_conf.sjwAbr = timing.nom_sjw
472+
canfd_conf.tseg1Abr = timing.nom_tseg1
473+
canfd_conf.tseg2Abr = timing.nom_tseg2
474+
canfd_conf.dataBitRate = timing.data_bitrate
475+
canfd_conf.sjwDbr = timing.data_sjw
476+
canfd_conf.tseg1Dbr = timing.data_tseg1
477+
canfd_conf.tseg2Dbr = timing.data_tseg2
532478
self.xldriver.xlCanFdSetConfiguration(
533479
self.port_handle, self.channel_masks[channel], canfd_conf
534480
)
@@ -550,11 +496,29 @@ def _set_bitrate_canfd(
550496
canfd_conf.tseg2Dbr,
551497
)
552498

553-
if self.__testing:
554-
return
499+
if not self.__testing:
500+
self._check_can_settings(
501+
channel=channel,
502+
bitrate=timing.nom_bitrate,
503+
sample_point=timing.nom_sample_point,
504+
fd=True,
505+
data_bitrate=timing.data_bitrate,
506+
data_sample_point=timing.data_sample_point,
507+
)
555508

556-
# Compare requested CAN settings to active settings
509+
def _check_can_settings(
510+
self,
511+
channel: int,
512+
bitrate: int,
513+
sample_point: Optional[float] = None,
514+
fd: bool = False,
515+
data_bitrate: Optional[int] = None,
516+
data_sample_point: Optional[float] = None,
517+
) -> None:
518+
"""Compare requested CAN settings to active settings in driver."""
557519
bus_params = self._read_bus_params(channel)
520+
# use canfd even if fd==False, bus_params.can and bus_params.canfd are a C union
521+
bus_params_data = bus_params.canfd
558522
settings_acceptable = True
559523

560524
# check bus type
@@ -563,60 +527,68 @@ def _set_bitrate_canfd(
563527
)
564528

565529
# check CAN operation mode
566-
settings_acceptable &= bool(
567-
bus_params.canfd.can_op_mode
568-
& xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CANFD
569-
)
530+
if fd:
531+
settings_acceptable &= bool(
532+
bus_params_data.can_op_mode
533+
& xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CANFD
534+
)
535+
elif bus_params_data.can_op_mode != 0: # can_op_mode is always 0 for cancaseXL
536+
settings_acceptable &= bool(
537+
bus_params_data.can_op_mode
538+
& xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CAN20
539+
)
570540

571541
# check bitrates
572542
if bitrate:
573543
settings_acceptable &= (
574-
abs(bus_params.canfd.bitrate - bitrate) < bitrate / 256
544+
abs(bus_params_data.bitrate - bitrate) < bitrate / 256
575545
)
576-
if data_bitrate:
546+
if fd and data_bitrate:
577547
settings_acceptable &= (
578-
abs(bus_params.canfd.data_bitrate - data_bitrate) < data_bitrate / 256
548+
abs(bus_params_data.data_bitrate - data_bitrate) < data_bitrate / 256
579549
)
580550

581551
# check sample points
582-
if bitrate:
583-
requested_nom_sample_point = (
584-
100 * (1 + tseg1_abr) / (1 + tseg1_abr + tseg2_abr)
585-
)
586-
actual_nom_sample_point = (
552+
if sample_point:
553+
nom_sample_point_act = (
587554
100
588-
* (1 + bus_params.canfd.tseg1_abr)
589-
/ (1 + bus_params.canfd.tseg1_abr + bus_params.canfd.tseg2_abr)
555+
* (1 + bus_params_data.tseg1_abr)
556+
/ (1 + bus_params_data.tseg1_abr + bus_params_data.tseg2_abr)
590557
)
591558
settings_acceptable &= (
592-
abs(actual_nom_sample_point - requested_nom_sample_point)
593-
< 1.0 # 1 percent threshold
594-
)
595-
if data_bitrate:
596-
requested_data_sample_point = (
597-
100 * (1 + tseg1_dbr) / (1 + tseg1_dbr + tseg2_dbr)
559+
abs(nom_sample_point_act - sample_point) < 2.0 # 2 percent tolerance
598560
)
599-
actual_data_sample_point = (
561+
if fd and data_sample_point:
562+
data_sample_point_act = (
600563
100
601-
* (1 + bus_params.canfd.tseg1_dbr)
602-
/ (1 + bus_params.canfd.tseg1_dbr + bus_params.canfd.tseg2_dbr)
564+
* (1 + bus_params_data.tseg1_dbr)
565+
/ (1 + bus_params_data.tseg1_dbr + bus_params_data.tseg2_dbr)
603566
)
604567
settings_acceptable &= (
605-
abs(actual_data_sample_point - requested_data_sample_point)
606-
< 1.0 # 1 percent threshold
568+
abs(data_sample_point_act - data_sample_point)
569+
< 2.0 # 2 percent tolerance
607570
)
608571

609572
if not settings_acceptable:
610-
active_settings = ", ".join(
611-
[
612-
f"{key}: {getattr(val, 'name', val)}" # print int or Enum/Flag name
613-
for key, val in bus_params.canfd._asdict().items()
614-
]
573+
# The error message depends on the currently active CAN settings.
574+
# If the active operation mode is CAN FD, show the active CAN FD timings,
575+
# otherwise show CAN 2.0 timings.
576+
if bool(
577+
bus_params_data.can_op_mode
578+
& xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CANFD
579+
):
580+
active_settings = bus_params.canfd._asdict()
581+
active_settings["can_op_mode"] = "CAN FD"
582+
else:
583+
active_settings = bus_params.can._asdict()
584+
active_settings["can_op_mode"] = "CAN 2.0"
585+
settings_string = ", ".join(
586+
[f"{key}: {val}" for key, val in active_settings.items()]
615587
)
616588
raise CanInitializationError(
617-
f"The requested CAN FD settings could not be set for channel {channel}. "
589+
f"The requested settings could not be set for channel {channel}. "
618590
f"Another application might have set incompatible settings. "
619-
f"These are the currently active settings: {active_settings}."
591+
f"These are the currently active settings: {settings_string}."
620592
)
621593

622594
def _apply_filters(self, filters: Optional[CanFilters]) -> None:

0 commit comments

Comments
 (0)