| 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 | |