Skip to content

Commit 0a31628

Browse files
authored
Merge branch 'develop' into read-can-fd-msg-64
2 parents ccab928 + 9ab5e35 commit 0a31628

11 files changed

Lines changed: 757 additions & 0 deletions

File tree

can/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class CanError(IOError):
3737
from .interfaces import VALID_INTERFACES
3838
from . import interface
3939
from .interface import Bus, detect_available_configs
40+
from .bit_timing import BitTiming
4041

4142
from .broadcastmanager import (
4243
CyclicSendTaskABC,

can/bit_timing.py

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
from typing import Union
2+
3+
4+
class BitTiming:
5+
"""Representation of a bit timing configuration.
6+
7+
The class can be constructed in various ways, depending on the information
8+
available or the capabilities of the interfaces that need to be supported.
9+
10+
The preferred way is using bitrate, CAN clock frequency, TSEG1, TSEG2, SJW::
11+
12+
can.BitTiming(bitrate=1000000, f_clock=8000000, tseg1=5, tseg2=1, sjw=1)
13+
14+
If the clock frequency is unknown it may be omitted but some interfaces may
15+
require it.
16+
17+
Alternatively the BRP can be given instead of bitrate and clock frequency but this
18+
will limit the number of supported interfaces.
19+
20+
It is also possible specify BTR registers directly,
21+
but will not work for all interfaces::
22+
23+
can.BitTiming(btr0=0x00, btr1=0x14)
24+
"""
25+
26+
sync_seg = 1
27+
28+
def __init__(
29+
self,
30+
bitrate: int = None,
31+
f_clock: int = None,
32+
brp: int = None,
33+
tseg1: int = None,
34+
tseg2: int = None,
35+
sjw: int = None,
36+
nof_samples: int = 1,
37+
btr0: int = None,
38+
btr1: int = None,
39+
):
40+
"""
41+
:param int bitrate:
42+
Bitrate in bits/s.
43+
:param int f_clock:
44+
The CAN system clock frequency in Hz.
45+
Usually the oscillator frequency divided by 2.
46+
:param int brp:
47+
Bit Rate Prescaler. Prefer to use bitrate and f_clock instead.
48+
:param int tseg1:
49+
Time segment 1, that is, the number of quanta from (but not including)
50+
the Sync Segment to the sampling point.
51+
:param int tseg2:
52+
Time segment 2, that is, the number of quanta from the sampling
53+
point to the end of the bit.
54+
:param int sjw:
55+
The Synchronization Jump Width. Decides the maximum number of time quanta
56+
that the controller can resynchronize every bit.
57+
:param int nof_samples:
58+
Either 1 or 3. Some CAN controllers can also sample each bit three times.
59+
In this case, the bit will be sampled three quanta in a row,
60+
with the last sample being taken in the edge between TSEG1 and TSEG2.
61+
Three samples should only be used for relatively slow baudrates.
62+
:param int btr0:
63+
The BTR0 register value used by many CAN controllers.
64+
:param int btr1:
65+
The BTR1 register value used by many CAN controllers.
66+
"""
67+
self._bitrate = bitrate
68+
self._brp = brp
69+
self._sjw = sjw
70+
self._tseg1 = tseg1
71+
self._tseg2 = tseg2
72+
self._nof_samples = nof_samples
73+
self._f_clock = f_clock
74+
75+
if btr0 is not None:
76+
self._brp = (btr0 & 0x3F) + 1
77+
self._sjw = (btr0 >> 6) + 1
78+
if btr1 is not None:
79+
self._tseg1 = (btr1 & 0xF) + 1
80+
self._tseg2 = ((btr1 >> 4) & 0x7) + 1
81+
self._nof_samples = 3 if btr1 & 0x80 else 1
82+
83+
if nof_samples not in (1, 3):
84+
raise ValueError("nof_samples must be 1 or 3")
85+
86+
@property
87+
def nbt(self) -> int:
88+
"""Nominal Bit Time."""
89+
return self.sync_seg + self.tseg1 + self.tseg2
90+
91+
@property
92+
def bitrate(self) -> Union[int, float]:
93+
"""Bitrate in bits/s."""
94+
if self._bitrate:
95+
return self._bitrate
96+
if self._f_clock and self._brp:
97+
return self._f_clock / (self._brp * self.nbt)
98+
raise ValueError("bitrate must be specified")
99+
100+
@property
101+
def brp(self) -> int:
102+
"""Bit Rate Prescaler."""
103+
if self._brp:
104+
return self._brp
105+
if self._f_clock and self._bitrate:
106+
return round(self._f_clock / (self._bitrate * self.nbt))
107+
raise ValueError("Either bitrate and f_clock or brp must be specified")
108+
109+
@property
110+
def sjw(self) -> int:
111+
"""Synchronization Jump Width."""
112+
if not self._sjw:
113+
raise ValueError("sjw must be specified")
114+
return self._sjw
115+
116+
@property
117+
def tseg1(self) -> int:
118+
"""Time segment 1.
119+
120+
The number of quanta from (but not including) the Sync Segment to the sampling point.
121+
"""
122+
if not self._tseg1:
123+
raise ValueError("tseg1 must be specified")
124+
return self._tseg1
125+
126+
@property
127+
def tseg2(self) -> int:
128+
"""Time segment 2.
129+
130+
The number of quanta from the sampling point to the end of the bit.
131+
"""
132+
if not self._tseg2:
133+
raise ValueError("tseg2 must be specified")
134+
return self._tseg2
135+
136+
@property
137+
def nof_samples(self) -> int:
138+
"""Number of samples (1 or 3)."""
139+
if not self._nof_samples:
140+
raise ValueError("nof_samples must be specified")
141+
return self._nof_samples
142+
143+
@property
144+
def f_clock(self) -> int:
145+
"""The CAN system clock frequency in Hz.
146+
147+
Usually the oscillator frequency divided by 2.
148+
"""
149+
if not self._f_clock:
150+
raise ValueError("f_clock must be specified")
151+
return self._f_clock
152+
153+
@property
154+
def sample_point(self) -> float:
155+
"""Sample point in percent."""
156+
return 100.0 * (self.nbt - self.tseg2) / self.nbt
157+
158+
@property
159+
def btr0(self) -> int:
160+
sjw = self.sjw
161+
brp = self.brp
162+
163+
if brp < 1 or brp > 64:
164+
raise ValueError("brp must be 1 - 64")
165+
if sjw < 1 or sjw > 4:
166+
raise ValueError("sjw must be 1 - 4")
167+
168+
return (sjw - 1) << 6 | brp - 1
169+
170+
@property
171+
def btr1(self) -> int:
172+
sam = 1 if self.nof_samples == 3 else 0
173+
tseg1 = self.tseg1
174+
tseg2 = self.tseg2
175+
176+
if tseg1 < 1 or tseg1 > 16:
177+
raise ValueError("tseg1 must be 1 - 16")
178+
if tseg2 < 1 or tseg2 > 8:
179+
raise ValueError("tseg2 must be 1 - 8")
180+
181+
return sam << 7 | (tseg2 - 1) << 4 | tseg1 - 1
182+
183+
def __str__(self) -> str:
184+
segments = []
185+
try:
186+
segments.append(f"{self.bitrate} bits/s")
187+
except ValueError:
188+
pass
189+
try:
190+
segments.append(f"sample point: {self.sample_point:.2f}%")
191+
except ValueError:
192+
pass
193+
try:
194+
segments.append(f"BRP: {self.brp}")
195+
except ValueError:
196+
pass
197+
try:
198+
segments.append(f"TSEG1: {self.tseg1}")
199+
except ValueError:
200+
pass
201+
try:
202+
segments.append(f"TSEG2: {self.tseg2}")
203+
except ValueError:
204+
pass
205+
try:
206+
segments.append(f"SJW: {self.sjw}")
207+
except ValueError:
208+
pass
209+
try:
210+
segments.append(f"BTR: {self.btr0:02X}{self.btr1:02X}h")
211+
except ValueError:
212+
pass
213+
return ", ".join(segments)
214+
215+
def __repr__(self) -> str:
216+
kwargs = {}
217+
if self._f_clock:
218+
kwargs["f_clock"] = self._f_clock
219+
if self._bitrate:
220+
kwargs["bitrate"] = self._bitrate
221+
if self._brp:
222+
kwargs["brp"] = self._brp
223+
if self._tseg1:
224+
kwargs["tseg1"] = self._tseg1
225+
if self._tseg2:
226+
kwargs["tseg2"] = self._tseg2
227+
if self._sjw:
228+
kwargs["sjw"] = self._sjw
229+
if self._nof_samples != 1:
230+
kwargs["nof_samples"] = self._nof_samples
231+
args = ", ".join(f"{key}={value}" for key, value in kwargs.items())
232+
return f"can.BitTiming({args})"

can/interfaces/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"slcan": ("can.interfaces.slcan", "slcanBus"),
2525
"canalystii": ("can.interfaces.canalystii", "CANalystIIBus"),
2626
"systec": ("can.interfaces.systec", "UcanBus"),
27+
"seeedstudio": ("can.interfaces.seeedstudio", "SeeedBus"),
2728
}
2829

2930
BACKENDS.update(
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# coding: utf-8
2+
3+
"""
4+
"""
5+
6+
from can.interfaces.seeedstudio.seeedstudio import SeeedBus

0 commit comments

Comments
 (0)