1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
from argparse import ArgumentParser
from math import floor, ceil
MAX_BRP = 2**6
MIN_TQ_PER_BIT = 8
MAX_TQ_PER_BIT = 25
MIN_PROP_SEG = 1
MAX_PROP_SEG = 8
MIN_PS1 = 1
MAX_PS1 = 8
MIN_PS2 = 2
MAX_PS2 = 8
MIN_SJW = 1
MAX_SJW = 4
CNF2_BTLMODE = 1 << 7
def prescaler(f_osc: int, bitrate: int, tq_per_bit: int) -> int | None:
brp: float = f_osc / (2.0 * bitrate * tq_per_bit)
if brp.is_integer():
return int(brp)
else:
return None
def is_valid_tq_per_bit(f_osc: int, bitrate: int, tq_per_bit: int) -> bool:
brp = prescaler(f_osc, bitrate, tq_per_bit)
return (brp is not None) and (brp <= MAX_BRP)
def valid_tqs_per_bit(f_osc: int, bitrate: int) -> list[int]:
return list(filter(
lambda tq_per_bit: is_valid_tq_per_bit(f_osc, bitrate, tq_per_bit),
range(MIN_TQ_PER_BIT, MAX_TQ_PER_BIT+1)))
def prop_plus_ps1(tq_per_bit: int, samplepoint: float) -> float:
sync: int = 1
return samplepoint * tq_per_bit - sync
def prop_ps1(tq_per_bit, samplepoint: float) -> (int, int):
sync: int = 1
prop_plus_ps1: float = samplepoint * tq_per_bit - sync
prop: int = floor(prop_plus_ps1 / 2)
ps1: int = ceil(prop_plus_ps1 / 2)
return (prop, ps1)
def ps2(tq_per_bit: int, prop: int, ps1: int) -> int:
sync: int = 1
return tq_per_bit - sync - prop - ps1
def parse_int(s: str) -> int:
n: float = float(s)
if n.is_integer():
return int(n)
else:
raise argparse.ArgumentTypeError(f"invalid int: {s}")
def unzip(lst: list[tuple[any, any]]) -> (list[any], list[any]):
return zip(*lst)
def fmt_freq(f: int) -> str:
if f > 1e6:
return f"{f/1e6}MHz"
elif f > 1e3:
return f"{f/1e3}kHz"
else:
return f"{f}Hz"
def fmt_bitrate(br: int) -> str:
if br > 1e6:
return f"{br/1e6}Mbps"
elif br > 1e3:
return f"{br/1e3}kbps"
else:
return f"{br}bps"
class Bittiming:
sync: int = 1
def __init__(self, fosc: int, bitrate: int, prop: int, ps1: int, ps2: int):
self.fosc = fosc
self.bitrate = bitrate
self.prop = prop
self.ps1 = ps1
self.ps2 = ps2
def samplepoint(self) -> float:
return (self.sync + self.prop + self.ps1) / self.tq_per_bit()
def tq_per_bit(self) -> int:
return self.sync + self.prop + self.ps1 + self.ps2
def prescaler(self) -> int:
brp = self.fosc / (2 * self.bitrate * self.tq_per_bit())
assert brp.is_integer()
return int(brp)
def max_sjw(self) -> int:
return min(MAX_SJW, self.ps1, self.ps2)
def cnf1(self) -> int:
return (((self.max_sjw()-1) & 0x3) << 6) | ((self.prescaler()-1) & 0x3F)
def cnf2(self) -> int:
phseg: int = (self.ps1-1) & 0x7
prseg: int = (self.prop-1) & 0x7
return CNF2_BTLMODE | (phseg << 3) | prseg
def cnf3(self) -> int:
return (self.ps2-1) & 0x7
def __str__(self) -> str:
return f"fosc={fmt_freq(self.fosc)}, bitrate={fmt_bitrate(self.bitrate)}, Tq/bit={self.tq_per_bit()}, BRP={self.prescaler()}, sync={self.sync}, prop={self.prop}, PS1={self.ps1}, PS2={self.ps2}, SP={self.samplepoint()*100.0:.1f}%, SJW_max={self.max_sjw()}, (CNF0, CNF1, CNF2)=(0x{self.cnf1():02X}, 0x{self.cnf2():02X}, 0x{self.cnf3():02X})"
if __name__ == "__main__":
argparser = ArgumentParser(description="Calculate CAN bit-timing parameters for the MCP2515")
argparser.add_argument("-fosc", type=parse_int, required=True, help="Oscillator frequency (Hz)")
argparser.add_argument("-bitrate", type=parse_int, required=True, help="Bitrate (bps)")
argparser.add_argument("-samplepoint", type=float, required=True, help="Sample point (%)")
args = argparser.parse_args()
# Find valid numbers of time quanta per bit
tqs_per_bit: list[int] = valid_tqs_per_bit(args.fosc, args.bitrate)
# Calculate Propogation Segment and Phase Segment 1 for each Tq/bit value
props: list[int]
ps1s: list[int]
props, ps1s = unzip(map(
lambda tq_per_bit: prop_ps1(tq_per_bit, args.samplepoint/100.0),
tqs_per_bit))
# Calculate Phase Segment 2 for each Tq/bit value
ps2s: list[int] = list(map(
lambda tq_per_bit, prop, ps1: ps2(tq_per_bit, prop, ps1),
tqs_per_bit, props, ps1s))
# Print valid bit-timings and MCP2515 config register values
bittimings: list[Bittiming] = list(map(
lambda prop, ps1, ps2: Bittiming(args.fosc, args.bitrate, prop, ps1, ps2),
props, ps1s, ps2s))
for bt in bittimings:
print(bt)
|