Skip to content

Commit 7b0955b

Browse files
author
luoja
committed
support SLCANFD
1 parent 0f23b1e commit 7b0955b

File tree

1 file changed

+113
-24
lines changed

1 file changed

+113
-24
lines changed

can/interfaces/slcan.py

Lines changed: 113 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
)
3030
serial = None
3131

32-
3332
HAS_EVENTS = False
3433

3534
try:
@@ -59,6 +58,8 @@ class slcanBus(BusABC):
5958
750000: "S7",
6059
1000000: "S8",
6160
83300: "S9",
61+
2000000: "Y2",
62+
5000000: "Y5",
6263
}
6364

6465
_SLEEP_AFTER_SERIAL_OPEN = 2 # in seconds
@@ -69,11 +70,33 @@ class slcanBus(BusABC):
6970

7071
LINE_TERMINATOR = b"\r"
7172

73+
DLC2BYTE_LEN = {
74+
0: 0,
75+
1: 1,
76+
2: 2,
77+
3: 3,
78+
4: 4,
79+
5: 5,
80+
6: 6,
81+
7: 7,
82+
8: 8,
83+
9: 12,
84+
10: 16,
85+
11: 20,
86+
12: 24,
87+
13: 32,
88+
14: 48,
89+
15: 64,
90+
}
91+
BYTE_LEN2DLC = {j: i for i, j in DLC2BYTE_LEN.items()}
92+
7293
def __init__(
7394
self,
7495
channel: typechecking.ChannelStr,
7596
ttyBaudrate: int = 115200,
7697
bitrate: Optional[int] = None,
98+
fd: bool = False,
99+
data_bitrate: Optional[int] = None,
77100
btr: Optional[str] = None,
78101
poll_interval: float = _POLL_INTERVAL,
79102
receive_own_messages: bool = False,
@@ -91,6 +114,10 @@ def __init__(
91114
baudrate of underlying serial or usb device (Ignored if set via the ``channel`` parameter)
92115
:param bitrate:
93116
Bitrate in bit/s
117+
:param bool fd:
118+
If CAN-FD frames should be supported.
119+
:param int data_bitrate:
120+
Which bitrate to use for data phase in CAN FD.
94121
:param btr:
95122
BTR register value to set custom can speed
96123
:param receive_own_messages:
@@ -144,6 +171,8 @@ def __init__(
144171
self.set_bitrate(bitrate)
145172
if btr is not None:
146173
self.set_bitrate_reg(btr)
174+
if fd and data_bitrate is not None:
175+
self.set_data_bitrate(data_bitrate)
147176
self.open()
148177

149178
self._timestamp_offset = time.time() - time.perf_counter()
@@ -176,6 +205,23 @@ def set_bitrate(self, bitrate: int) -> None:
176205
self._write(bitrate_code)
177206
self.open()
178207

208+
def set_data_bitrate(self, data_bitrate: int) -> None:
209+
"""
210+
:param data_bitrate:
211+
Bitrate in bit/s
212+
213+
:raise ValueError: if ``data_bitrate`` is not among the possible values
214+
"""
215+
if data_bitrate in self._BITRATES:
216+
data_bitrate_code = self._BITRATES[data_bitrate]
217+
else:
218+
data_bitrates = ", ".join(str(k) for k in self._BITRATES.keys())
219+
raise ValueError(f"Invalid bitrate, choose one of {data_bitrates}.")
220+
221+
self.close()
222+
self._write(data_bitrate_code)
223+
self.open()
224+
179225
def set_bitrate_reg(self, btr: str) -> None:
180226
"""
181227
:param btr:
@@ -222,12 +268,7 @@ def _read_can_thread(self, event_read):
222268
time.sleep(self.poll_interval)
223269

224270
def _read_can(self) -> List[Message]:
225-
canId = None
226-
remote = False
227-
extended = False
228-
data = None
229271
msgs = []
230-
231272
with error_check("Could not read from serial device"):
232273
# Due to accessing `serialPortOrig.in_waiting` too often will reduce the performance.
233274
# We read the `serialPortOrig.in_waiting` only once here.
@@ -242,37 +283,72 @@ def _read_can(self) -> List[Message]:
242283
if new_byte in (self._ERROR, self._OK):
243284
string = self._buffer.decode()
244285
self._buffer.clear()
245-
246286
if not string:
247-
pass
248-
elif string[0] in (
249-
"T",
250-
"x", # x is an alternative extended message identifier for CANDapter
251-
):
287+
continue
288+
canId = None
289+
remote = False
290+
extended = False
291+
brs = False
292+
fd = False
293+
data = None
294+
s0 = string[0]
295+
if s0 in ("T", "x"):
252296
# extended frame
253297
canId = int(string[1:9], 16)
254298
dlc = int(string[9])
255299
extended = True
256300
data = bytearray.fromhex(string[10 : 10 + dlc * 2])
257-
elif string[0] == "t":
301+
elif s0 == "t":
258302
# normal frame
259303
canId = int(string[1:4], 16)
260304
dlc = int(string[4])
261305
data = bytearray.fromhex(string[5 : 5 + dlc * 2])
262-
elif string[0] == "r":
306+
elif s0 == "r":
263307
# remote frame
264308
canId = int(string[1:4], 16)
265309
dlc = int(string[4])
266310
remote = True
267-
elif string[0] == "R":
311+
data = bytearray.fromhex(string[5 : 5 + dlc * 2])
312+
elif s0 == "R":
268313
# remote extended frame
269314
canId = int(string[1:9], 16)
270315
dlc = int(string[9])
271316
extended = True
272317
remote = True
318+
data = bytearray.fromhex(string[10 : 10 + dlc * 2])
319+
elif s0 == "d":
320+
# fd_frame
321+
fd = True
322+
canId = int(string[1:4], 16)
323+
dlc = self.DLC2BYTE_LEN[int(string[4])]
324+
data = bytearray.fromhex(string[5 : 5 + dlc * 2])
325+
elif s0 == "D":
326+
# extended fd_frame
327+
fd = True
328+
extended = True
329+
canId = int(string[1:9], 16)
330+
dlc = self.DLC2BYTE_LEN[int(string[9])]
331+
data = bytearray.fromhex(string[10 : 10 + dlc * 2])
332+
elif s0 == "b":
333+
# fd_frame
334+
fd = True
335+
brs = True
336+
canId = int(string[1:4], 16)
337+
dlc = self.DLC2BYTE_LEN[int(string[4])]
338+
data = bytearray.fromhex(string[5 : 5 + dlc * 2])
339+
elif s0 == "B":
340+
# extended fd_frame
341+
fd = True
342+
brs = True
343+
extended = True
344+
canId = int(string[1:9], 16)
345+
dlc = self.DLC2BYTE_LEN[int(string[9])]
346+
data = bytearray.fromhex(string[10 : 10 + dlc * 2])
273347

274348
if canId is not None:
275349
msg = Message(
350+
is_fd=fd,
351+
bitrate_switch=brs,
276352
arbitration_id=canId,
277353
is_extended_id=extended,
278354
timestamp=self._timestamp_offset
@@ -287,20 +363,33 @@ def _read_can(self) -> List[Message]:
287363
def send(self, msg: Message, timeout: Optional[float] = None) -> None:
288364
if timeout != self.serialPortOrig.write_timeout:
289365
self.serialPortOrig.write_timeout = timeout
290-
if msg.is_remote_frame:
291-
if msg.is_extended_id:
292-
sendStr = f"R{msg.arbitration_id:08X}{msg.dlc:d}"
366+
if msg.is_fd:
367+
dlc = self.BYTE_LEN2DLC[msg.dlc]
368+
if not msg.bitrate_switch:
369+
if not msg.is_extended_id:
370+
sendStr = f"d{msg.arbitration_id:03X}{dlc:x}"
371+
else:
372+
sendStr = f"D{msg.arbitration_id:08X}{dlc:x}"
293373
else:
294-
sendStr = f"r{msg.arbitration_id:03X}{msg.dlc:d}"
374+
if not msg.is_extended_id:
375+
sendStr = f"b{msg.arbitration_id:03X}{dlc:x}"
376+
else:
377+
sendStr = f"B{msg.arbitration_id:08X}{dlc:x}"
295378
else:
296-
if msg.is_extended_id:
297-
sendStr = f"T{msg.arbitration_id:08X}{msg.dlc:d}"
379+
if msg.is_remote_frame:
380+
if msg.is_extended_id:
381+
sendStr = f"R{msg.arbitration_id:08X}{msg.dlc:d}"
382+
else:
383+
sendStr = f"r{msg.arbitration_id:03X}{msg.dlc:d}"
298384
else:
299-
sendStr = f"t{msg.arbitration_id:03X}{msg.dlc:d}"
300-
sendStr += msg.data.hex().upper()
385+
if msg.is_extended_id:
386+
sendStr = f"T{msg.arbitration_id:08X}{msg.dlc:d}"
387+
else:
388+
sendStr = f"t{msg.arbitration_id:03X}{msg.dlc:d}"
389+
sendStr += msg.data.hex().upper()
301390
if self.receive_own_messages:
302391
msg.is_rx = False
303-
msg.timestamp = self._timestamp_offset + time.perf_counter() # Better than nothing...
392+
msg.timestamp = self._timestamp_offset + time.perf_counter() # Better than nothing...
304393
self.queue_read.put(msg)
305394
self._write(sendStr)
306395

0 commit comments

Comments
 (0)