embassy_stm32/can/util.rs
1//! Utility functions shared between CAN controller types.
2
3use core::num::{NonZeroU16, NonZeroU8};
4
5/// Shared struct to represent bit timings used by calc_can_timings.
6#[derive(Clone, Copy, Debug)]
7pub 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
20pub 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}