Skip to content

Commit 7a9ef00

Browse files
author
spencer
committed
add FD support to slcan according to CANable 2.0 impementation
1 parent 2bd4758 commit 7a9ef00

File tree

1 file changed

+62
-6
lines changed

1 file changed

+62
-6
lines changed

can/interfaces/slcan.py

+62-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
CanOperationError,
1717
error_check,
1818
)
19-
from can.util import check_or_adjust_timing_clock, deprecated_args_alias
19+
from can.util import CAN_FD_DLC, check_or_adjust_timing_clock, deprecated_args_alias
2020

2121
logger = 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

Comments
 (0)