5
5
import time
6
6
from datetime import datetime
7
7
import platform
8
- from typing import Optional , List , Tuple
8
+ from typing import Optional , List , Tuple , Union , Any
9
9
10
10
from packaging import version
11
11
12
12
from can import (
13
13
BusABC ,
14
14
BusState ,
15
+ BitTiming ,
16
+ BitTimingFd ,
17
+ Message ,
15
18
CanError ,
16
19
CanOperationError ,
17
20
CanInitializationError ,
18
- Message ,
19
21
)
20
- from can .util import len2dlc , dlc2len
21
-
22
+ from can .util import check_or_adjust_timing_clock , dlc2len , len2dlc
22
23
from .basic import (
23
24
PCAN_BITRATES ,
24
25
PCAN_FD_PARAMETER_LIST ,
61
62
FEATURE_FD_CAPABLE ,
62
63
PCAN_DICT_STATUS ,
63
64
PCAN_BUSOFF_AUTORESET ,
65
+ TPCANBaudrate ,
64
66
PCAN_ATTACHED_CHANNELS ,
65
67
TPCANChannelInformation ,
68
+ VALID_PCAN_FD_CLOCKS ,
69
+ VALID_PCAN_CAN_CLOCKS ,
66
70
)
67
71
68
72
112
116
class PcanBus (BusABC ):
113
117
def __init__ (
114
118
self ,
115
- channel = "PCAN_USBBUS1" ,
116
- device_id = None ,
117
- state = BusState .ACTIVE ,
118
- bitrate = 500000 ,
119
- * args ,
120
- ** kwargs ,
119
+ channel : str = "PCAN_USBBUS1" ,
120
+ device_id : Optional [ int ] = None ,
121
+ state : BusState = BusState .ACTIVE ,
122
+ timing : Optional [ Union [ BitTiming , BitTimingFd ]] = None ,
123
+ bitrate : int = 500000 ,
124
+ ** kwargs : Any ,
121
125
):
122
126
"""A PCAN USB interface to CAN.
123
127
@@ -142,6 +146,18 @@ def __init__(
142
146
BusState of the channel.
143
147
Default is ACTIVE
144
148
149
+ :param timing:
150
+ An instance of :class:`~can.BitTiming` or :class:`~can.BitTimingFd`
151
+ to specify the bit timing parameters for the PCAN interface. If this parameter
152
+ is provided, it takes precedence over all other timing-related parameters.
153
+ If this parameter is not provided, the bit timing parameters can be specified
154
+ using the `bitrate` parameter for standard CAN or the `fd`, `f_clock`,
155
+ `f_clock_mhz`, `nom_brp`, `nom_tseg1`, `nom_tseg2`, `nom_sjw`, `data_brp`,
156
+ `data_tseg1`, `data_tseg2`, and `data_sjw` parameters for CAN FD.
157
+ Note that the `f_clock` value of the `timing` instance must be 8_000_000
158
+ for standard CAN or any of the following values for CAN FD: 20_000_000,
159
+ 24_000_000, 30_000_000, 40_000_000, 60_000_000, 80_000_000.
160
+
145
161
:param int bitrate:
146
162
Bitrate of channel in bit/s.
147
163
Default is 500 kbit/s.
@@ -231,8 +247,7 @@ def __init__(
231
247
raise ValueError (err_msg )
232
248
233
249
self .channel_info = str (channel )
234
- self .fd = kwargs .get ("fd" , False )
235
- pcan_bitrate = PCAN_BITRATES .get (bitrate , PCAN_BAUD_500K )
250
+ self .fd = isinstance (timing , BitTimingFd ) if timing else kwargs .get ("fd" , False )
236
251
237
252
hwtype = PCAN_TYPE_ISA
238
253
ioport = 0x02A0
@@ -245,30 +260,41 @@ def __init__(
245
260
246
261
self .check_api_version ()
247
262
248
- if state is BusState .ACTIVE or state is BusState .PASSIVE :
263
+ if state in [ BusState .ACTIVE , BusState .PASSIVE ] :
249
264
self .state = state
250
265
else :
251
266
raise ValueError ("BusState must be Active or Passive" )
252
267
253
- if self .fd :
254
- f_clock_val = kwargs .get ("f_clock" , None )
255
- if f_clock_val is None :
256
- f_clock = "{}={}" .format ("f_clock_mhz" , kwargs .get ("f_clock_mhz" , None ))
257
- else :
258
- f_clock = "{}={}" .format ("f_clock" , kwargs .get ("f_clock" , None ))
259
-
260
- fd_parameters_values = [f_clock ] + [
261
- f"{ key } ={ kwargs .get (key , None )} "
262
- for key in PCAN_FD_PARAMETER_LIST
263
- if kwargs .get (key , None ) is not None
268
+ if isinstance (timing , BitTiming ):
269
+ timing = check_or_adjust_timing_clock (timing , VALID_PCAN_CAN_CLOCKS )
270
+ pcan_bitrate = TPCANBaudrate (timing .btr0 << 8 | timing .btr1 )
271
+ result = self .m_objPCANBasic .Initialize (
272
+ self .m_PcanHandle , pcan_bitrate , hwtype , ioport , interrupt
273
+ )
274
+ elif self .fd :
275
+ if isinstance (timing , BitTimingFd ):
276
+ timing = check_or_adjust_timing_clock (
277
+ timing , sorted (VALID_PCAN_FD_CLOCKS , reverse = True )
278
+ )
279
+ # We dump the timing parameters into the kwargs because they have equal names
280
+ # as the kwargs parameters and this saves us one additional code path
281
+ kwargs .update (timing )
282
+
283
+ clock_param = "f_clock" if "f_clock" in kwargs else "f_clock_mhz"
284
+ fd_parameters_values = [
285
+ f"{ key } ={ kwargs [key ]} "
286
+ for key in (clock_param ,) + PCAN_FD_PARAMETER_LIST
287
+ if key in kwargs
264
288
]
265
289
266
- self .fd_bitrate = " , " .join (fd_parameters_values ).encode ("ascii" )
290
+ self .fd_bitrate = ", " .join (fd_parameters_values ).encode ("ascii" )
267
291
268
292
result = self .m_objPCANBasic .InitializeFD (
269
293
self .m_PcanHandle , self .fd_bitrate
270
294
)
295
+
271
296
else :
297
+ pcan_bitrate = PCAN_BITRATES .get (bitrate , PCAN_BAUD_500K )
272
298
result = self .m_objPCANBasic .Initialize (
273
299
self .m_PcanHandle , pcan_bitrate , hwtype , ioport , interrupt
274
300
)
@@ -312,7 +338,7 @@ def __init__(
312
338
if result != PCAN_ERROR_OK :
313
339
raise PcanCanInitializationError (self ._get_formatted_error (result ))
314
340
315
- super ().__init__ (channel = channel , state = state , bitrate = bitrate , * args , * *kwargs )
341
+ super ().__init__ (channel = channel , state = state , bitrate = bitrate , ** kwargs )
316
342
317
343
def _find_channel_by_dev_id (self , device_id ):
318
344
"""
0 commit comments