1616 CanOperationError ,
1717 error_check ,
1818)
19- from can .util import check_or_adjust_timing_clock , deprecated_args_alias
19+ from can .util import check_or_adjust_timing_clock , deprecated_args_alias , CAN_FD_DLC
2020
2121logger = logging .getLogger (__name__ )
2222
@@ -48,6 +48,10 @@ class slcanBus(BusABC):
4848 1000000 : "S8" ,
4949 83300 : "S9" ,
5050 }
51+ _DATA_BITRATES = {
52+ 2000000 : "Y2" ,
53+ 5000000 : "Y5" ,
54+ }
5155
5256 _SLEEP_AFTER_SERIAL_OPEN = 2 # in seconds
5357
@@ -86,7 +90,8 @@ def __init__(
8690 If this argument is set then it overrides the bitrate and btr arguments. The
8791 `f_clock` value of the timing instance must be set to 8_000_000 (8MHz)
8892 for standard CAN.
89- CAN FD and the :class:`~can.BitTimingFd` class are not supported.
93+ CAN FD and the :class:`~can.BitTimingFd` class have partial support according to the non-standard
94+ slcan protocol implementation in the CANABLE 2.0 firmware: currently only data rates of 2M and 5M.
9095 :param poll_interval:
9196 Poll interval in seconds when reading messages
9297 :param sleep_after_open:
@@ -143,9 +148,7 @@ def __init__(
143148 timing = check_or_adjust_timing_clock (timing , valid_clocks = [8_000_000 ])
144149 self .set_bitrate_reg (f"{ timing .btr0 :02X} { timing .btr1 :02X} " )
145150 elif isinstance (timing , BitTimingFd ):
146- raise NotImplementedError (
147- f"CAN FD is not supported by { self .__class__ .__name__ } ."
148- )
151+ self .set_bitrate (timing .nom_bitrate , timing .data_bitrate )
149152 else :
150153 if bitrate is not None and btr is not None :
151154 raise ValueError ("Bitrate and btr mutually exclusive." )
@@ -157,10 +160,12 @@ def __init__(
157160
158161 super ().__init__ (channel , ** kwargs )
159162
160- def set_bitrate (self , bitrate : int ) -> None :
163+ def set_bitrate (self , bitrate : int , dbitrate : int = 0 ) -> None :
161164 """
162165 :param bitrate:
163166 Bitrate in bit/s
167+ :param dbitrate:
168+ Data Bitrate in bit/s for FD frames
164169
165170 :raise ValueError: if ``bitrate`` is not among the possible values
166171 """
@@ -169,9 +174,15 @@ def set_bitrate(self, bitrate: int) -> None:
169174 else :
170175 bitrates = ", " .join (str (k ) for k in self ._BITRATES .keys ())
171176 raise ValueError (f"Invalid bitrate, choose one of { bitrates } ." )
177+ if dbitrate in self ._DATA_BITRATES :
178+ dbitrate_code = self ._DATA_BITRATES [dbitrate ]
179+ else :
180+ dbitrates = ", " .join (str (k ) for k in self ._DATA_BITRATES .keys ())
181+ raise ValueError (f"Invalid data bitrate, choose one of { dbitrates } ." )
172182
173183 self .close ()
174184 self ._write (bitrate_code )
185+ self ._write (dbitrate_code )
175186 self .open ()
176187
177188 def set_bitrate_reg (self , btr : str ) -> None :
@@ -235,6 +246,8 @@ def _recv_internal(
235246 remote = False
236247 extended = False
237248 data = None
249+ isFd = False
250+ fdBrs = False
238251
239252 if self ._queue .qsize ():
240253 string : Optional [str ] = self ._queue .get_nowait ()
@@ -268,13 +281,43 @@ def _recv_internal(
268281 dlc = int (string [9 ])
269282 extended = True
270283 remote = True
284+ elif string [0 ] == "d" :
285+ # FD standard frame
286+ canId = int (string [1 :4 ], 16 )
287+ dlc = int (string [4 ], 16 )
288+ isFd = True
289+ data = bytearray .fromhex (string [5 : 5 + CAN_FD_DLC [dlc ] * 2 ])
290+ elif string [0 ] == "D" :
291+ # FD extended frame
292+ canId = int (string [1 :9 ], 16 )
293+ dlc = int (string [9 ], 16 )
294+ extended = True
295+ isFd = True
296+ data = bytearray .fromhex (string [10 : 10 + CAN_FD_DLC [dlc ] * 2 ])
297+ elif string [0 ] == "b" :
298+ # FD with bitrate switch
299+ canId = int (string [1 :4 ], 16 )
300+ dlc = int (string [4 ], 16 )
301+ isFd = True
302+ fdBrs = True
303+ data = bytearray .fromhex (string [5 : 5 + CAN_FD_DLC [dlc ] * 2 ])
304+ elif string [0 ] == "B" :
305+ # FD extended with bitrate switch
306+ canId = int (string [1 :9 ], 16 )
307+ dlc = int (string [9 ], 16 )
308+ extended = True
309+ isFd = True
310+ fdBrs = True
311+ data = bytearray .fromhex (string [10 : 10 + CAN_FD_DLC [dlc ] * 2 ])
271312
272313 if canId is not None :
273314 msg = Message (
274315 arbitration_id = canId ,
275316 is_extended_id = extended ,
276317 timestamp = time .time (), # Better than nothing...
277318 is_remote_frame = remote ,
319+ is_fd = isFd ,
320+ bitrate_switch = fdBrs ,
278321 dlc = dlc ,
279322 data = data ,
280323 )
@@ -289,6 +332,19 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None:
289332 sendStr = f"R{ msg .arbitration_id :08X} { msg .dlc :d} "
290333 else :
291334 sendStr = f"r{ msg .arbitration_id :03X} { msg .dlc :d} "
335+ elif msg .is_fd :
336+ if msg .bitrate_switch :
337+ if msg .is_extended_id :
338+ sendStr = f"B{ msg .arbitration_id :08X} { msg .dlc :d} "
339+ else :
340+ sendStr = f"b{ msg .arbitration_id :03X} { msg .dlc :d} "
341+ sendStr += msg .data .hex ().upper ()
342+ else :
343+ if msg .is_extended_id :
344+ sendStr = f"D{ msg .arbitration_id :08X} { msg .dlc :d} "
345+ else :
346+ sendStr = f"d{ msg .arbitration_id :03X} { msg .dlc :d} "
347+ sendStr += msg .data .hex ().upper ()
292348 else :
293349 if msg .is_extended_id :
294350 sendStr = f"T{ msg .arbitration_id :08X} { msg .dlc :d} "
0 commit comments