can_utils/
timing_calculator.rs

1//! The `timing_calculator` module computes CAN interface timing parameters.
2
3use core::cmp;
4
5type BaudRatePrescalaInnerType = u32;
6
7type BitSamplePointInnerType = u16;
8
9type SegmentLengthInnerType = u8;
10
11/// HACK: once uom supports no_std replace this with a more sane approach to specifying bitrates
12pub struct BitsPerSecond(u32);
13
14/// HACK: Once uom supports no_std, replace this with a more standard frequency representation.
15pub struct MegaHertz(u32);
16
17/// CAN timing is controlled in units of Time Quanta, this codifies that.
18pub struct SegmentLength(SegmentLengthInnerType);
19
20/// This struct bundles various maximum limits all CAN interfaces have.
21pub struct CanTimingLimits {
22  /// The largest supported BRP, almost always a power of two.
23  pub max_baud_rate_prescaler: u32,
24  /// The max length of seg1, almost always a power of two.
25  pub max_segment_1_length: SegmentLength,
26  /// The max length of seg2, almost always a power of two.
27  pub max_segment_2_length: SegmentLength,
28  /// The maximum value it is possible to configure the interface to set the maximum jump to.
29  pub max_jump_width: SegmentLength,
30}
31
32/// The results of an interface timing calculation.
33pub struct CanBitTimingParameters {
34  /// The CAN interface will scale the clock frequency back by this amount.
35  pub baud_rate_prescaler: BaudRatePrescalaInnerType,
36  /// The number of time quanta before the sample point (not counting the Sync Seg Quantum).
37  pub seg1: SegmentLength,
38  /// The number of time quanta after the sample point.
39  pub seg2: SegmentLength,
40  /// The number of time quanta to adjust seg1 and seg2 by at each synchronization step.
41  pub jump_width: SegmentLength,
42}
43
44/// `BitSamplePoint` is the fraction of the way through each bit the interface takes its sample.
45pub struct BitSamplePoint {
46  /// Decipercentage of the way through the bit that the sample point should be.
47  tenths_of_a_percent: BitSamplePointInnerType,
48}
49
50impl BitSamplePoint {
51  /// The minimum sample point is 50%, per the spec.
52  pub const MINIMUM_SAMPLE_POINT: BitSamplePoint = BitSamplePoint { tenths_of_a_percent: 500 };
53  /// There is no official maximum, except that seg2 must be at least one time quantum.
54  pub const MAXIMUM_SAMPLE_POINT: BitSamplePoint = BitSamplePoint { tenths_of_a_percent: 990 };
55
56  /// Constructs a target sample point after some sanity checking.
57  pub fn new(tenths_of_a_percent: BitSamplePointInnerType) -> BitSamplePoint {
58    // TODO: add sanity checking here that the value is between 500 and 990
59    BitSamplePoint { tenths_of_a_percent }
60  }
61}
62
63/// Iterates over all timing parameter solutions.
64pub struct CanBitTimingParameterIter<'a> {
65  bit_width: u32,
66  jump_width: SegmentLengthInnerType,
67  target_sample_point: BitSamplePointInnerType,
68  last_attempted_prescaler: BaudRatePrescalaInnerType,
69
70  interface_limits: &'a CanTimingLimits,
71}
72
73impl<'a> Iterator for CanBitTimingParameterIter<'a> {
74  type Item = CanBitTimingParameters;
75
76  fn next(&mut self) -> Option<CanBitTimingParameters> {
77    let mut result: Option<CanBitTimingParameters> = None;
78
79    while result.is_none() &&
80          self.last_attempted_prescaler < self.interface_limits.max_baud_rate_prescaler {
81      self.last_attempted_prescaler += 1;
82      if self.bit_width % self.last_attempted_prescaler != 0 {
83        continue;
84      }
85      let tq = self.bit_width / self.last_attempted_prescaler;
86      if tq < 8 {  // not sure why this is 8, but that's what the can docs and calculators say
87        continue;
88      }
89      // TODO: sorry for all the casting here, clean it up once uom builds with no_std
90      let sample = tq * 1000 / (self.target_sample_point as u32);
91      let seg2 = cmp::max(1, tq - sample) as SegmentLengthInnerType;  // seg2 must be >= 1 quantum
92      let seg1 = tq as SegmentLengthInnerType - seg2 - 1;  // magic 1 here is the Sych segment.
93
94      // we dont' need to verify that seg1 >= seg2 because target_sample_point >= 50%
95      if seg1 > self.interface_limits.max_segment_1_length.0 ||
96          seg2 > self.interface_limits.max_segment_2_length.0 {
97        continue;
98      }
99      result = Some(CanBitTimingParameters {
100        baud_rate_prescaler: self.last_attempted_prescaler,
101        seg1: SegmentLength(seg1),
102        seg2: SegmentLength(seg2),
103        jump_width: SegmentLength(self.jump_width),
104      });
105    }
106    result
107  }
108}
109
110// TODO: spend a lot more time documenting this
111/// Returns an iterator that iterates over all timing solutions to the given problem.
112pub fn compute_timing_parameters<'a>(clock_speed: MegaHertz,
113                                     nominal_bitrate: BitsPerSecond,
114                                     target_sample_point: BitSamplePoint,
115                                     jump_width: SegmentLength,
116                                     limits: &'a CanTimingLimits) -> CanBitTimingParameterIter {
117  CanBitTimingParameterIter {
118    bit_width: 1_000_000 * clock_speed.0 / nominal_bitrate.0,
119    jump_width: jump_width.0,
120    target_sample_point: target_sample_point.tenths_of_a_percent,
121    last_attempted_prescaler: 1,
122    interface_limits: limits,
123  }
124}
125
126/// The recommended bit sampling points for various protocols.  Get your defaults here.
127pub mod recommended_sample_points {
128  use super::BitSamplePoint;
129  ///
130  pub const ARINC825: BitSamplePoint = BitSamplePoint { tenths_of_a_percent: 750 };
131  ///
132  pub const CANOPEN: BitSamplePoint = BitSamplePoint { tenths_of_a_percent: 875 };
133  ///
134  pub const DEVICENET: BitSamplePoint = BitSamplePoint { tenths_of_a_percent: 875 };
135  ///
136  pub const J1939: BitSamplePoint = BitSamplePoint { tenths_of_a_percent: 900 };
137  ///
138  pub const J2284: BitSamplePoint = BitSamplePoint { tenths_of_a_percent: 900 };
139  // TODO: add more of these
140}