1
+ import math
1
2
from typing import List , Mapping , Iterator , cast
2
3
3
4
from can .typechecking import BitTimingFdDict , BitTimingDict
@@ -139,6 +140,13 @@ def from_sample_point(
139
140
) -> "BitTiming" :
140
141
"""Create a BitTiming instance for a sample point.
141
142
143
+ This function tries to find bit timings, which are close to the requested
144
+ sample point. It does not take physical bus properties into account, so the
145
+ calculated bus timings might not work properly for you.
146
+
147
+ The :func:`oscillator_tolerance` function might be helpful to evaluate the
148
+ bus timings.
149
+
142
150
:param int f_clock:
143
151
The CAN system clock frequency in Hz.
144
152
:param int bitrate:
@@ -161,8 +169,10 @@ def from_sample_point(
161
169
continue
162
170
163
171
tseg1 = int (round (sample_point / 100 * nbt )) - 1
164
- tseg2 = nbt - tseg1 - 1
172
+ # limit tseg1, so tseg2 is at least 1 TQ
173
+ tseg1 = min (tseg1 , nbt - 2 )
165
174
175
+ tseg2 = nbt - tseg1 - 1
166
176
sjw = min (tseg2 , 4 )
167
177
168
178
try :
@@ -173,17 +183,23 @@ def from_sample_point(
173
183
tseg2 = tseg2 ,
174
184
sjw = sjw ,
175
185
)
176
- if abs (bt .sample_point - sample_point ) < 1 :
177
- possible_solutions .append (bt )
186
+ possible_solutions .append (bt )
178
187
except ValueError :
179
188
continue
180
189
181
190
if not possible_solutions :
182
191
raise ValueError ("No suitable bit timings found." )
183
192
184
- return sorted (
185
- possible_solutions , key = lambda x : x .oscillator_tolerance , reverse = True
186
- )[0 ]
193
+ # sort solutions
194
+ for key , reverse in (
195
+ # prefer low prescaler
196
+ (lambda x : x .brp , False ),
197
+ # prefer low sample point deviation from requested values
198
+ (lambda x : abs (x .sample_point - sample_point ), False ),
199
+ ):
200
+ possible_solutions .sort (key = key , reverse = reverse )
201
+
202
+ return possible_solutions [0 ]
187
203
188
204
@property
189
205
def nbt (self ) -> int :
@@ -200,6 +216,11 @@ def brp(self) -> int:
200
216
"""Bit Rate Prescaler."""
201
217
return int (round (self .f_clock / (self .bitrate * self .nbt )))
202
218
219
+ @property
220
+ def tq (self ) -> int :
221
+ """Time quantum in nanoseconds"""
222
+ return int (round (self .brp / self .f_clock * 1e9 ))
223
+
203
224
@property
204
225
def sjw (self ) -> int :
205
226
"""Synchronization Jump Width."""
@@ -236,14 +257,35 @@ def sample_point(self) -> float:
236
257
"""Sample point in percent."""
237
258
return 100.0 * (1 + self .tseg1 ) / (1 + self .tseg1 + self .tseg2 )
238
259
239
- @property
240
- def oscillator_tolerance (self ) -> float :
241
- """Oscillator tolerance in percent."""
260
+ def oscillator_tolerance (
261
+ self ,
262
+ node_loop_delay_ns : float = 250.0 ,
263
+ bus_length_m : float = 10.0 ,
264
+ ) -> float :
265
+ """Oscillator tolerance in percent according to ISO 11898-1.
266
+
267
+ :param node_loop_delay_ns:
268
+ Transceiver loop delay in nanoseconds.
269
+ :param bus_length_m:
270
+ Bus length in meters.
271
+ """
272
+ delay_per_meter = 5
273
+ bidirectional_propagation_delay_ns = 2 * (
274
+ node_loop_delay_ns + delay_per_meter * bus_length_m
275
+ )
276
+
277
+ prop_seg = math .ceil (bidirectional_propagation_delay_ns / self .tq )
278
+ nom_phase_seg1 = self .tseg1 - prop_seg
279
+ nom_phase_seg2 = self .tseg2
242
280
df_clock_list = [
243
281
_oscillator_tolerance_condition_1 (nom_sjw = self .sjw , nbt = self .nbt ),
244
- _oscillator_tolerance_condition_2 (nbt = self .nbt , nom_tseg2 = self .tseg2 ),
282
+ _oscillator_tolerance_condition_2 (
283
+ nbt = self .nbt ,
284
+ nom_phase_seg1 = nom_phase_seg1 ,
285
+ nom_phase_seg2 = nom_phase_seg2 ,
286
+ ),
245
287
]
246
- return min (df_clock_list ) * 100
288
+ return max ( 0.0 , min (df_clock_list ) * 100 )
247
289
248
290
@property
249
291
def btr0 (self ) -> int :
@@ -290,10 +332,6 @@ def __str__(self) -> str:
290
332
segments .append (f"f_clock: { self .f_clock / 1e6 :.0f} MHz" )
291
333
except ValueError :
292
334
pass
293
- try :
294
- segments .append (f"df_clock: { self .oscillator_tolerance :.2f} %" )
295
- except ValueError :
296
- pass
297
335
return ", " .join (segments )
298
336
299
337
def __repr__ (self ) -> str :
@@ -509,6 +547,13 @@ def from_sample_point(
509
547
) -> "BitTimingFd" :
510
548
"""Create a BitTimingFd instance for a given nominal/data sample point pair.
511
549
550
+ This function tries to find bit timings, which are close to the requested
551
+ sample points. It does not take physical bus properties into account, so the
552
+ calculated bus timings might not work properly for you.
553
+
554
+ The :func:`oscillator_tolerance` function might be helpful to evaluate the
555
+ bus timings.
556
+
512
557
:param int f_clock:
513
558
The CAN system clock frequency in Hz.
514
559
:param int nom_bitrate:
@@ -542,6 +587,8 @@ def from_sample_point(
542
587
continue
543
588
544
589
nom_tseg1 = int (round (nom_sample_point / 100 * nbt )) - 1
590
+ # limit tseg1, so tseg2 is at least 1 TQ
591
+ nom_tseg1 = min (nom_tseg1 , nbt - 2 )
545
592
nom_tseg2 = nbt - nom_tseg1 - 1
546
593
547
594
nom_sjw = min (nom_tseg2 , 128 )
@@ -556,28 +603,25 @@ def from_sample_point(
556
603
continue
557
604
558
605
data_tseg1 = int (round (data_sample_point / 100 * dbt )) - 1
606
+ # limit tseg1, so tseg2 is at least 1 TQ
607
+ data_tseg1 = min (data_tseg1 , dbt - 2 )
559
608
data_tseg2 = dbt - data_tseg1 - 1
560
609
561
610
data_sjw = min (data_tseg2 , 16 )
562
611
563
- bit_timings = {
564
- "f_clock" : f_clock ,
565
- "nom_bitrate" : nom_bitrate ,
566
- "nom_tseg1" : nom_tseg1 ,
567
- "nom_tseg2" : nom_tseg2 ,
568
- "nom_sjw" : nom_sjw ,
569
- "data_bitrate" : data_bitrate ,
570
- "data_tseg1" : data_tseg1 ,
571
- "data_tseg2" : data_tseg2 ,
572
- "data_sjw" : data_sjw ,
573
- }
574
612
try :
575
- bt = BitTimingFd (** bit_timings )
576
- if (
577
- abs (bt .nom_sample_point - nom_sample_point ) < 1
578
- and abs (bt .data_sample_point - bt .data_sample_point ) < 1
579
- ):
580
- possible_solutions .append (bt )
613
+ bt = BitTimingFd (
614
+ f_clock = f_clock ,
615
+ nom_bitrate = nom_bitrate ,
616
+ nom_tseg1 = nom_tseg1 ,
617
+ nom_tseg2 = nom_tseg2 ,
618
+ nom_sjw = nom_sjw ,
619
+ data_bitrate = data_bitrate ,
620
+ data_tseg1 = data_tseg1 ,
621
+ data_tseg2 = data_tseg2 ,
622
+ data_sjw = data_sjw ,
623
+ )
624
+ possible_solutions .append (bt )
581
625
except ValueError :
582
626
continue
583
627
@@ -591,11 +635,20 @@ def from_sample_point(
591
635
if same_prescaler :
592
636
possible_solutions = same_prescaler
593
637
594
- # sort solutions: prefer high tolerance, low prescaler and high sjw
638
+ # sort solutions
595
639
for key , reverse in (
596
- (lambda x : x .data_brp , False ),
597
- (lambda x : x .nom_brp , False ),
598
- (lambda x : x .oscillator_tolerance , True ),
640
+ # prefer low prescaler
641
+ (lambda x : x .nom_brp + x .data_brp , False ),
642
+ # prefer same prescaler for arbitration and data
643
+ (lambda x : abs (x .nom_brp - x .data_brp ), False ),
644
+ # prefer low sample point deviation from requested values
645
+ (
646
+ lambda x : (
647
+ abs (x .nom_sample_point - nom_sample_point )
648
+ + abs (x .data_sample_point - data_sample_point )
649
+ ),
650
+ False ,
651
+ ),
599
652
):
600
653
possible_solutions .sort (key = key , reverse = reverse )
601
654
@@ -611,6 +664,11 @@ def nom_brp(self) -> int:
611
664
"""Prescaler value for the arbitration phase."""
612
665
return int (round (self .f_clock / (self .nom_bitrate * self .nbt )))
613
666
667
+ @property
668
+ def nom_tq (self ) -> int :
669
+ """Nominal time quantum in nanoseconds"""
670
+ return int (round (self .nom_brp / self .f_clock * 1e9 ))
671
+
614
672
@property
615
673
def nbt (self ) -> int :
616
674
"""Number of time quanta in a bit of the arbitration phase."""
@@ -652,6 +710,11 @@ def data_brp(self) -> int:
652
710
"""Prescaler value for the data phase."""
653
711
return int (round (self .f_clock / (self .data_bitrate * self .dbt )))
654
712
713
+ @property
714
+ def data_tq (self ) -> int :
715
+ """Data time quantum in nanoseconds"""
716
+ return int (round (self .data_brp / self .f_clock * 1e9 ))
717
+
655
718
@property
656
719
def dbt (self ) -> int :
657
720
"""Number of time quanta in a bit of the data phase."""
@@ -688,25 +751,52 @@ def f_clock(self) -> int:
688
751
"""The CAN system clock frequency in Hz."""
689
752
return self ["f_clock" ]
690
753
691
- @property
692
- def oscillator_tolerance (self ) -> float :
693
- """Oscillator tolerance in percent."""
754
+ def oscillator_tolerance (
755
+ self ,
756
+ node_loop_delay_ns : float = 250.0 ,
757
+ bus_length_m : float = 10.0 ,
758
+ ) -> float :
759
+ """Oscillator tolerance in percent according to ISO 11898-1.
760
+
761
+ :param node_loop_delay_ns:
762
+ Transceiver loop delay in nanoseconds.
763
+ :param bus_length_m:
764
+ Bus length in meters.
765
+ """
766
+ delay_per_meter = 5
767
+ bidirectional_propagation_delay_ns = 2 * (
768
+ node_loop_delay_ns + delay_per_meter * bus_length_m
769
+ )
770
+
771
+ prop_seg = math .ceil (bidirectional_propagation_delay_ns / self .nom_tq )
772
+ nom_phase_seg1 = self .nom_tseg1 - prop_seg
773
+ nom_phase_seg2 = self .nom_tseg2
774
+
775
+ data_phase_seg2 = self .data_tseg2
776
+
694
777
df_clock_list = [
695
778
_oscillator_tolerance_condition_1 (nom_sjw = self .nom_sjw , nbt = self .nbt ),
696
- _oscillator_tolerance_condition_2 (nbt = self .nbt , nom_tseg2 = self .nom_tseg2 ),
779
+ _oscillator_tolerance_condition_2 (
780
+ nbt = self .nbt ,
781
+ nom_phase_seg1 = nom_phase_seg1 ,
782
+ nom_phase_seg2 = nom_phase_seg2 ,
783
+ ),
697
784
_oscillator_tolerance_condition_3 (data_sjw = self .data_sjw , dbt = self .dbt ),
698
785
_oscillator_tolerance_condition_4 (
699
- data_tseg2 = self .data_tseg2 ,
786
+ nom_phase_seg1 = nom_phase_seg1 ,
787
+ nom_phase_seg2 = nom_phase_seg2 ,
788
+ data_phase_seg2 = data_phase_seg2 ,
700
789
nbt = self .nbt ,
790
+ dbt = self .dbt ,
701
791
data_brp = self .data_brp ,
702
792
nom_brp = self .nom_brp ,
703
793
),
704
794
_oscillator_tolerance_condition_5 (
705
795
data_sjw = self .data_sjw ,
706
796
data_brp = self .data_brp ,
707
797
nom_brp = self .nom_brp ,
708
- data_tseg2 = self . data_tseg2 ,
709
- nom_tseg2 = self . nom_tseg2 ,
798
+ data_phase_seg2 = data_phase_seg2 ,
799
+ nom_phase_seg2 = nom_phase_seg2 ,
710
800
nbt = self .nbt ,
711
801
dbt = self .dbt ,
712
802
),
@@ -767,10 +857,6 @@ def __str__(self) -> str:
767
857
segments .append (f"f_clock: { self .f_clock / 1e6 :.0f} MHz" )
768
858
except ValueError :
769
859
pass
770
- try :
771
- segments .append (f"df_clock: { self .oscillator_tolerance :.2f} %" )
772
- except ValueError :
773
- pass
774
860
return ", " .join (segments )
775
861
776
862
def __repr__ (self ) -> str :
@@ -798,9 +884,11 @@ def _oscillator_tolerance_condition_1(nom_sjw: int, nbt: int) -> float:
798
884
return nom_sjw / (2 * 10 * nbt )
799
885
800
886
801
- def _oscillator_tolerance_condition_2 (nbt : int , nom_tseg2 : int ) -> float :
887
+ def _oscillator_tolerance_condition_2 (
888
+ nbt : int , nom_phase_seg1 : int , nom_phase_seg2 : int
889
+ ) -> float :
802
890
"""Arbitration phase - sampling of bit after error flag"""
803
- return nom_tseg2 / (2 * (13 * nbt - nom_tseg2 ))
891
+ return min ( nom_phase_seg1 , nom_phase_seg2 ) / (2 * (13 * nbt - nom_phase_seg2 ))
804
892
805
893
806
894
def _oscillator_tolerance_condition_3 (data_sjw : int , dbt : int ) -> float :
@@ -809,24 +897,32 @@ def _oscillator_tolerance_condition_3(data_sjw: int, dbt: int) -> float:
809
897
810
898
811
899
def _oscillator_tolerance_condition_4 (
812
- data_tseg2 : int , nbt : int , data_brp : int , nom_brp : int
900
+ nom_phase_seg1 : int ,
901
+ nom_phase_seg2 : int ,
902
+ data_phase_seg2 : int ,
903
+ nbt : int ,
904
+ dbt : int ,
905
+ data_brp : int ,
906
+ nom_brp : int ,
813
907
) -> float :
814
908
"""Data phase - sampling of bit after error flag"""
815
- return data_tseg2 / (2 * ((6 * nbt - data_tseg2 ) * data_brp / nom_brp + 7 * nbt ))
909
+ return min (nom_phase_seg1 , nom_phase_seg2 ) / (
910
+ 2 * ((6 * dbt - data_phase_seg2 ) * data_brp / nom_brp + 7 * nbt )
911
+ )
816
912
817
913
818
914
def _oscillator_tolerance_condition_5 (
819
915
data_sjw : int ,
820
916
data_brp : int ,
821
917
nom_brp : int ,
822
- nom_tseg2 : int ,
823
- data_tseg2 : int ,
918
+ nom_phase_seg2 : int ,
919
+ data_phase_seg2 : int ,
824
920
nbt : int ,
825
921
dbt : int ,
826
922
) -> float :
827
923
"""Data phase - bit rate switch"""
828
924
max_correctable_phase_shift = data_sjw - max (0.0 , nom_brp / data_brp - 1 )
829
925
time_between_resync = 2 * (
830
- (2 * nbt - nom_tseg2 ) * nom_brp / data_brp + data_tseg2 + 4 * dbt
926
+ (2 * nbt - nom_phase_seg2 ) * nom_brp / data_brp + data_phase_seg2 + 4 * dbt
831
927
)
832
928
return max_correctable_phase_shift / time_between_resync
0 commit comments