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}