1616 CanOperationError ,
1717 error_check ,
1818)
19- from can .util import check_or_adjust_timing_clock , deprecated_args_alias
19+ from can .util import (
20+ CAN_FD_DLC ,
21+ check_or_adjust_timing_clock ,
22+ deprecated_args_alias ,
23+ len2dlc ,
24+ )
2025
2126logger = logging .getLogger (__name__ )
2227
@@ -48,6 +53,11 @@ class slcanBus(BusABC):
4853 1000000 : "S8" ,
4954 83300 : "S9" ,
5055 }
56+ _DATA_BITRATES = {
57+ 0 : "" ,
58+ 2000000 : "Y2" ,
59+ 5000000 : "Y5" ,
60+ }
5161
5262 _SLEEP_AFTER_SERIAL_OPEN = 2 # in seconds
5363
@@ -86,7 +96,8 @@ def __init__(
8696 If this argument is set then it overrides the bitrate and btr arguments. The
8797 `f_clock` value of the timing instance must be set to 8_000_000 (8MHz)
8898 for standard CAN.
89- CAN FD and the :class:`~can.BitTimingFd` class are not supported.
99+ CAN FD and the :class:`~can.BitTimingFd` class have partial support according to the non-standard
100+ slcan protocol implementation in the CANABLE 2.0 firmware: currently only data rates of 2M and 5M.
90101 :param poll_interval:
91102 Poll interval in seconds when reading messages
92103 :param sleep_after_open:
@@ -143,9 +154,7 @@ def __init__(
143154 timing = check_or_adjust_timing_clock (timing , valid_clocks = [8_000_000 ])
144155 self .set_bitrate_reg (f"{ timing .btr0 :02X} { timing .btr1 :02X} " )
145156 elif isinstance (timing , BitTimingFd ):
146- raise NotImplementedError (
147- f"CAN FD is not supported by { self .__class__ .__name__ } ."
148- )
157+ self .set_bitrate (timing .nom_bitrate , timing .data_bitrate )
149158 else :
150159 if bitrate is not None and btr is not None :
151160 raise ValueError ("Bitrate and btr mutually exclusive." )
@@ -157,10 +166,12 @@ def __init__(
157166
158167 super ().__init__ (channel , ** kwargs )
159168
160- def set_bitrate (self , bitrate : int ) -> None :
169+ def set_bitrate (self , bitrate : int , data_bitrate : Optional [ int ] = None ) -> None :
161170 """
162171 :param bitrate:
163172 Bitrate in bit/s
173+ :param data_bitrate:
174+ Data Bitrate in bit/s for FD frames
164175
165176 :raise ValueError: if ``bitrate`` is not among the possible values
166177 """
@@ -169,9 +180,15 @@ def set_bitrate(self, bitrate: int) -> None:
169180 else :
170181 bitrates = ", " .join (str (k ) for k in self ._BITRATES .keys ())
171182 raise ValueError (f"Invalid bitrate, choose one of { bitrates } ." )
183+ if data_bitrate in self ._DATA_BITRATES :
184+ dbitrate_code = self ._DATA_BITRATES [data_bitrate ]
185+ else :
186+ dbitrates = ", " .join (str (k ) for k in self ._DATA_BITRATES .keys ())
187+ raise ValueError (f"Invalid data bitrate, choose one of { dbitrates } ." )
172188
173189 self .close ()
174190 self ._write (bitrate_code )
191+ self ._write (dbitrate_code )
175192 self .open ()
176193
177194 def set_bitrate_reg (self , btr : str ) -> None :
@@ -235,6 +252,8 @@ def _recv_internal(
235252 remote = False
236253 extended = False
237254 data = None
255+ isFd = False
256+ fdBrs = False
238257
239258 if self ._queue .qsize ():
240259 string : Optional [str ] = self ._queue .get_nowait ()
@@ -268,14 +287,44 @@ def _recv_internal(
268287 dlc = int (string [9 ])
269288 extended = True
270289 remote = True
290+ elif string [0 ] == "d" :
291+ # FD standard frame
292+ canId = int (string [1 :4 ], 16 )
293+ dlc = int (string [4 ], 16 )
294+ isFd = True
295+ data = bytearray .fromhex (string [5 : 5 + CAN_FD_DLC [dlc ] * 2 ])
296+ elif string [0 ] == "D" :
297+ # FD extended frame
298+ canId = int (string [1 :9 ], 16 )
299+ dlc = int (string [9 ], 16 )
300+ extended = True
301+ isFd = True
302+ data = bytearray .fromhex (string [10 : 10 + CAN_FD_DLC [dlc ] * 2 ])
303+ elif string [0 ] == "b" :
304+ # FD with bitrate switch
305+ canId = int (string [1 :4 ], 16 )
306+ dlc = int (string [4 ], 16 )
307+ isFd = True
308+ fdBrs = True
309+ data = bytearray .fromhex (string [5 : 5 + CAN_FD_DLC [dlc ] * 2 ])
310+ elif string [0 ] == "B" :
311+ # FD extended with bitrate switch
312+ canId = int (string [1 :9 ], 16 )
313+ dlc = int (string [9 ], 16 )
314+ extended = True
315+ isFd = True
316+ fdBrs = True
317+ data = bytearray .fromhex (string [10 : 10 + CAN_FD_DLC [dlc ] * 2 ])
271318
272319 if canId is not None :
273320 msg = Message (
274321 arbitration_id = canId ,
275322 is_extended_id = extended ,
276323 timestamp = time .time (), # Better than nothing...
277324 is_remote_frame = remote ,
278- dlc = dlc ,
325+ is_fd = isFd ,
326+ bitrate_switch = fdBrs ,
327+ dlc = CAN_FD_DLC [dlc ],
279328 data = data ,
280329 )
281330 return msg , False
@@ -289,6 +338,20 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None:
289338 sendStr = f"R{ msg .arbitration_id :08X} { msg .dlc :d} "
290339 else :
291340 sendStr = f"r{ msg .arbitration_id :03X} { msg .dlc :d} "
341+ elif msg .is_fd :
342+ fd_dlc = len2dlc (msg .dlc )
343+ if msg .bitrate_switch :
344+ if msg .is_extended_id :
345+ sendStr = f"B{ msg .arbitration_id :08X} { fd_dlc :X} "
346+ else :
347+ sendStr = f"b{ msg .arbitration_id :03X} { fd_dlc :X} "
348+ sendStr += msg .data .hex ().upper ()
349+ else :
350+ if msg .is_extended_id :
351+ sendStr = f"D{ msg .arbitration_id :08X} { fd_dlc :X} "
352+ else :
353+ sendStr = f"d{ msg .arbitration_id :03X} { fd_dlc :X} "
354+ sendStr += msg .data .hex ().upper ()
292355 else :
293356 if msg .is_extended_id :
294357 sendStr = f"T{ msg .arbitration_id :08X} { msg .dlc :d} "
0 commit comments