1 | //! Utility functions shared between CAN controller types. |
2 | |
3 | use core::num::{NonZeroU16, NonZeroU8}; |
4 | |
5 | /// Shared struct to represent bit timings used by calc_can_timings. |
6 | #[derive (Clone, Copy, Debug)] |
7 | pub struct NominalBitTiming { |
8 | /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit |
9 | /// time is built up from a multiple of this quanta. Valid values are 1 to 512. |
10 | pub prescaler: NonZeroU16, |
11 | /// Valid values are 1 to 128. |
12 | pub seg1: NonZeroU8, |
13 | /// Valid values are 1 to 255. |
14 | pub seg2: NonZeroU8, |
15 | /// Valid values are 1 to 128. |
16 | pub sync_jump_width: NonZeroU8, |
17 | } |
18 | |
19 | /// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency |
20 | pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Option<NominalBitTiming> { |
21 | const BS1_MAX: u8 = 16; |
22 | const BS2_MAX: u8 = 8; |
23 | const MAX_SAMPLE_POINT_PERMILL: u16 = 900; |
24 | |
25 | let periph_clock = periph_clock.0; |
26 | |
27 | if can_bitrate < 1000 { |
28 | return None; |
29 | } |
30 | |
31 | // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG |
32 | // CAN in Automation, 2003 |
33 | // |
34 | // According to the source, optimal quanta per bit are: |
35 | // Bitrate Optimal Maximum |
36 | // 1000 kbps 8 10 |
37 | // 500 kbps 16 17 |
38 | // 250 kbps 16 17 |
39 | // 125 kbps 16 17 |
40 | let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 }; |
41 | |
42 | // Computing (prescaler * BS): |
43 | // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual |
44 | // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified |
45 | // let: |
46 | // BS = 1 + BS1 + BS2 -- Number of time quanta per bit |
47 | // PRESCALER_BS = PRESCALER * BS |
48 | // ==> |
49 | // PRESCALER_BS = PCLK / BITRATE |
50 | let prescaler_bs = periph_clock / can_bitrate; |
51 | |
52 | // Searching for such prescaler value so that the number of quanta per bit is highest. |
53 | let mut bs1_bs2_sum = max_quanta_per_bit - 1; |
54 | while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { |
55 | if bs1_bs2_sum <= 2 { |
56 | return None; // No solution |
57 | } |
58 | bs1_bs2_sum -= 1; |
59 | } |
60 | |
61 | let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; |
62 | if (prescaler < 1) || (prescaler > 1024) { |
63 | return None; // No solution |
64 | } |
65 | |
66 | // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. |
67 | // We need to find such values so that the sample point is as close as possible to the optimal value, |
68 | // which is 87.5%, which is 7/8. |
69 | // |
70 | // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) |
71 | // {{bs2 -> (1 + bs1)/7}} |
72 | // |
73 | // Hence: |
74 | // bs2 = (1 + bs1) / 7 |
75 | // bs1 = (7 * bs1_bs2_sum - 1) / 8 |
76 | // |
77 | // Sample point location can be computed as follows: |
78 | // Sample point location = (1 + bs1) / (1 + bs1 + bs2) |
79 | // |
80 | // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: |
81 | // - With rounding to nearest |
82 | // - With rounding to zero |
83 | let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first |
84 | let mut bs2 = bs1_bs2_sum - bs1; |
85 | core::assert!(bs1_bs2_sum > bs1); |
86 | |
87 | let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; |
88 | if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { |
89 | // Nope, too far; now rounding to zero |
90 | bs1 = (7 * bs1_bs2_sum - 1) / 8; |
91 | bs2 = bs1_bs2_sum - bs1; |
92 | } |
93 | |
94 | // Check is BS1 and BS2 are in range |
95 | if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { |
96 | return None; |
97 | } |
98 | |
99 | // Check if final bitrate matches the requested |
100 | if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { |
101 | return None; |
102 | } |
103 | |
104 | // One is recommended by DS-015, CANOpen, and DeviceNet |
105 | let sync_jump_width = core::num::NonZeroU8::new(1)?; |
106 | |
107 | let seg1 = core::num::NonZeroU8::new(bs1)?; |
108 | let seg2 = core::num::NonZeroU8::new(bs2)?; |
109 | let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16)?; |
110 | |
111 | Some(NominalBitTiming { |
112 | sync_jump_width, |
113 | prescaler: nz_prescaler, |
114 | seg1, |
115 | seg2, |
116 | }) |
117 | } |
118 | |