aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz bla Fortuna <[email protected]>2024-01-14 09:47:26 +0100
committerCorey Schuhen <[email protected]>2024-01-31 05:40:05 +1000
commit03ba45065e529284d41ebca5a8e716633107fc48 (patch)
treef4c82233706c381bb4d1f57306927691193f47bf
parenta91a7a8557ae8998de7b08d753e7a55172fb43c8 (diff)
Add FDCAN clock registers to G4 RCC.
Author: Adam Morgan <[email protected]> Break definitions out of bxcan that can be used innm fdcan. Typo
-rw-r--r--embassy-stm32/build.rs2
-rw-r--r--embassy-stm32/src/can/bxcan.rs124
-rw-r--r--embassy-stm32/src/can/enums.rs30
-rw-r--r--embassy-stm32/src/can/util.rs117
-rw-r--r--embassy-stm32/src/rcc/g4.rs7
-rw-r--r--embassy-stm32/src/rcc/h.rs8
6 files changed, 173 insertions, 115 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 948ce3aff..414723573 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -449,7 +449,7 @@ fn main() {
449 // ======== 449 // ========
450 // Generate RccPeripheral impls 450 // Generate RccPeripheral impls
451 451
452 let refcounted_peripherals = HashSet::from(["usart", "adc"]); 452 let refcounted_peripherals = HashSet::from(["usart", "adc", "can"]);
453 let mut refcount_statics = BTreeSet::new(); 453 let mut refcount_statics = BTreeSet::new();
454 454
455 for p in METADATA.peripherals { 455 for p in METADATA.peripherals {
diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs
index cc87b2565..7e00eca6f 100644
--- a/embassy-stm32/src/can/bxcan.rs
+++ b/embassy-stm32/src/can/bxcan.rs
@@ -13,9 +13,12 @@ use crate::gpio::sealed::AFType;
13use crate::interrupt::typelevel::Interrupt; 13use crate::interrupt::typelevel::Interrupt;
14use crate::pac::can::vals::{Ide, Lec}; 14use crate::pac::can::vals::{Ide, Lec};
15use crate::rcc::RccPeripheral; 15use crate::rcc::RccPeripheral;
16use crate::time::Hertz;
17use crate::{interrupt, peripherals, Peripheral}; 16use crate::{interrupt, peripherals, Peripheral};
18 17
18pub mod enums;
19use enums::*;
20pub mod util;
21
19/// Contains CAN frame and additional metadata. 22/// Contains CAN frame and additional metadata.
20/// 23///
21/// Timestamp is available if `time` feature is enabled. 24/// Timestamp is available if `time` feature is enabled.
@@ -93,23 +96,6 @@ pub struct Can<'d, T: Instance> {
93 can: bxcan::Can<BxcanInstance<'d, T>>, 96 can: bxcan::Can<BxcanInstance<'d, T>>,
94} 97}
95 98
96/// CAN bus error
97#[allow(missing_docs)]
98#[derive(Debug)]
99#[cfg_attr(feature = "defmt", derive(defmt::Format))]
100pub enum BusError {
101 Stuff,
102 Form,
103 Acknowledge,
104 BitRecessive,
105 BitDominant,
106 Crc,
107 Software,
108 BusOff,
109 BusPassive,
110 BusWarning,
111}
112
113/// Error returned by `try_read` 99/// Error returned by `try_read`
114#[derive(Debug)] 100#[derive(Debug)]
115#[cfg_attr(feature = "defmt", derive(defmt::Format))] 101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -186,8 +172,15 @@ impl<'d, T: Instance> Can<'d, T> {
186 172
187 /// Set CAN bit rate. 173 /// Set CAN bit rate.
188 pub fn set_bitrate(&mut self, bitrate: u32) { 174 pub fn set_bitrate(&mut self, bitrate: u32) {
189 let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); 175 let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap();
190 self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); 176 let sjw = u8::from(bit_timing.sync_jump_width) as u32;
177 let seg1 = u8::from(bit_timing.seg1) as u32;
178 let seg2 = u8::from(bit_timing.seg2) as u32;
179 let prescaler = u16::from(bit_timing.prescaler) as u32;
180 self.can
181 .modify_config()
182 .set_bit_timing((sjw - 1) << 24 | (seg1 - 1) << 16 | (seg2 - 1) << 20 | (prescaler - 1))
183 .leave_disabled();
191 } 184 }
192 185
193 /// Enables the peripheral and synchronizes with the bus. 186 /// Enables the peripheral and synchronizes with the bus.
@@ -302,97 +295,6 @@ impl<'d, T: Instance> Can<'d, T> {
302 } 295 }
303 } 296 }
304 297
305 const fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option<u32> {
306 const BS1_MAX: u8 = 16;
307 const BS2_MAX: u8 = 8;
308 const MAX_SAMPLE_POINT_PERMILL: u16 = 900;
309
310 let periph_clock = periph_clock.0;
311
312 if can_bitrate < 1000 {
313 return None;
314 }
315
316 // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG
317 // CAN in Automation, 2003
318 //
319 // According to the source, optimal quanta per bit are:
320 // Bitrate Optimal Maximum
321 // 1000 kbps 8 10
322 // 500 kbps 16 17
323 // 250 kbps 16 17
324 // 125 kbps 16 17
325 let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 };
326
327 // Computing (prescaler * BS):
328 // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual
329 // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified
330 // let:
331 // BS = 1 + BS1 + BS2 -- Number of time quanta per bit
332 // PRESCALER_BS = PRESCALER * BS
333 // ==>
334 // PRESCALER_BS = PCLK / BITRATE
335 let prescaler_bs = periph_clock / can_bitrate;
336
337 // Searching for such prescaler value so that the number of quanta per bit is highest.
338 let mut bs1_bs2_sum = max_quanta_per_bit - 1;
339 while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 {
340 if bs1_bs2_sum <= 2 {
341 return None; // No solution
342 }
343 bs1_bs2_sum -= 1;
344 }
345
346 let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32;
347 if (prescaler < 1) || (prescaler > 1024) {
348 return None; // No solution
349 }
350
351 // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
352 // We need to find such values so that the sample point is as close as possible to the optimal value,
353 // which is 87.5%, which is 7/8.
354 //
355 // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *)
356 // {{bs2 -> (1 + bs1)/7}}
357 //
358 // Hence:
359 // bs2 = (1 + bs1) / 7
360 // bs1 = (7 * bs1_bs2_sum - 1) / 8
361 //
362 // Sample point location can be computed as follows:
363 // Sample point location = (1 + bs1) / (1 + bs1 + bs2)
364 //
365 // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one:
366 // - With rounding to nearest
367 // - With rounding to zero
368 let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first
369 let mut bs2 = bs1_bs2_sum - bs1;
370 core::assert!(bs1_bs2_sum > bs1);
371
372 let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16;
373 if sample_point_permill > MAX_SAMPLE_POINT_PERMILL {
374 // Nope, too far; now rounding to zero
375 bs1 = (7 * bs1_bs2_sum - 1) / 8;
376 bs2 = bs1_bs2_sum - bs1;
377 }
378
379 // Check is BS1 and BS2 are in range
380 if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) {
381 return None;
382 }
383
384 // Check if final bitrate matches the requested
385 if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) {
386 return None;
387 }
388
389 // One is recommended by DS-015, CANOpen, and DeviceNet
390 let sjw = 1;
391
392 // Pack into BTR register values
393 Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler - 1))
394 }
395
396 /// Split the CAN driver into transmit and receive halves. 298 /// Split the CAN driver into transmit and receive halves.
397 /// 299 ///
398 /// Useful for doing separate transmit/receive tasks. 300 /// Useful for doing separate transmit/receive tasks.
diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs
new file mode 100644
index 000000000..36139a45c
--- /dev/null
+++ b/embassy-stm32/src/can/enums.rs
@@ -0,0 +1,30 @@
1//! Enums shared between CAN controller types.
2
3/// Bus error
4#[derive(Debug)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum BusError {
7 /// Bit stuffing error - more than 5 equal bits
8 Stuff,
9 /// Form error - A fixed format part of a received message has wrong format
10 Form,
11 /// The message transmitted by the FDCAN was not acknowledged by another node.
12 Acknowledge,
13 /// Bit0Error: During the transmission of a message the device wanted to send a dominant level
14 /// but the monitored bus value was recessive.
15 BitRecessive,
16 /// Bit1Error: During the transmission of a message the device wanted to send a recessive level
17 /// but the monitored bus value was dominant.
18 BitDominant,
19 /// The CRC check sum of a received message was incorrect. The CRC of an
20 /// incoming message does not match with the CRC calculated from the received data.
21 Crc,
22 /// A software error occured
23 Software,
24 /// The FDCAN is in Bus_Off state.
25 BusOff,
26 /// The FDCAN is in the Error_Passive state.
27 BusPassive,
28 /// At least one of error counter has reached the Error_Warning limit of 96.
29 BusWarning,
30}
diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs
new file mode 100644
index 000000000..fcdbbad62
--- /dev/null
+++ b/embassy-stm32/src/can/util.rs
@@ -0,0 +1,117 @@
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}
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs
index fca364c21..891f0490b 100644
--- a/embassy-stm32/src/rcc/g4.rs
+++ b/embassy-stm32/src/rcc/g4.rs
@@ -3,8 +3,8 @@ use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw};
3use stm32_metapac::FLASH; 3use stm32_metapac::FLASH;
4 4
5pub use crate::pac::rcc::vals::{ 5pub use crate::pac::rcc::vals::{
6 Adcsel as AdcClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, Pllp as PllP, Pllq as PllQ, 6 Adcsel as AdcClockSource, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN,
7 Pllr as PllR, Ppre as APBPrescaler, 7 Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler,
8}; 8};
9use crate::pac::{PWR, RCC}; 9use crate::pac::{PWR, RCC};
10use crate::rcc::{set_freqs, Clocks}; 10use crate::rcc::{set_freqs, Clocks};
@@ -87,6 +87,7 @@ pub struct Config {
87 pub clock_48mhz_src: Option<Clock48MhzSrc>, 87 pub clock_48mhz_src: Option<Clock48MhzSrc>,
88 pub adc12_clock_source: AdcClockSource, 88 pub adc12_clock_source: AdcClockSource,
89 pub adc345_clock_source: AdcClockSource, 89 pub adc345_clock_source: AdcClockSource,
90 pub fdcan_clock_source: FdCanClockSource,
90 91
91 pub ls: super::LsConfig, 92 pub ls: super::LsConfig,
92} 93}
@@ -104,6 +105,7 @@ impl Default for Config {
104 clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())), 105 clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())),
105 adc12_clock_source: Adcsel::DISABLE, 106 adc12_clock_source: Adcsel::DISABLE,
106 adc345_clock_source: Adcsel::DISABLE, 107 adc345_clock_source: Adcsel::DISABLE,
108 fdcan_clock_source: FdCanClockSource::PCLK1,
107 ls: Default::default(), 109 ls: Default::default(),
108 } 110 }
109 } 111 }
@@ -282,6 +284,7 @@ pub(crate) unsafe fn init(config: Config) {
282 284
283 RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); 285 RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source));
284 RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); 286 RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source));
287 RCC.ccipr().modify(|w| w.set_fdcansel(config.fdcan_clock_source));
285 288
286 let adc12_ck = match config.adc12_clock_source { 289 let adc12_ck = match config.adc12_clock_source {
287 AdcClockSource::DISABLE => None, 290 AdcClockSource::DISABLE => None,
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs
index 15b51a398..85e12637e 100644
--- a/embassy-stm32/src/rcc/h.rs
+++ b/embassy-stm32/src/rcc/h.rs
@@ -6,6 +6,7 @@ use crate::pac::pwr::vals::Vos;
6pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; 6pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource;
7#[cfg(stm32h7)] 7#[cfg(stm32h7)]
8pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; 8pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
9pub use crate::pac::rcc::vals::Fdcansel as FdCanClockSource;
9pub use crate::pac::rcc::vals::{ 10pub use crate::pac::rcc::vals::{
10 Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, 11 Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
11 Pllsrc as PllSource, Sw as Sysclk, 12 Pllsrc as PllSource, Sw as Sysclk,
@@ -212,6 +213,8 @@ pub struct Config {
212 213
213 pub per_clock_source: PerClockSource, 214 pub per_clock_source: PerClockSource,
214 pub adc_clock_source: AdcClockSource, 215 pub adc_clock_source: AdcClockSource,
216 pub fdcan_clock_source: FdCanClockSource,
217
215 pub timer_prescaler: TimerPrescaler, 218 pub timer_prescaler: TimerPrescaler,
216 pub voltage_scale: VoltageScale, 219 pub voltage_scale: VoltageScale,
217 pub ls: super::LsConfig, 220 pub ls: super::LsConfig,
@@ -248,6 +251,8 @@ impl Default for Config {
248 #[cfg(stm32h7)] 251 #[cfg(stm32h7)]
249 adc_clock_source: AdcClockSource::PER, 252 adc_clock_source: AdcClockSource::PER,
250 253
254 fdcan_clock_source: FdCanClockSource::from_bits(0), // HSE
255
251 timer_prescaler: TimerPrescaler::DefaultX2, 256 timer_prescaler: TimerPrescaler::DefaultX2,
252 voltage_scale: VoltageScale::Scale0, 257 voltage_scale: VoltageScale::Scale0,
253 ls: Default::default(), 258 ls: Default::default(),
@@ -585,7 +590,8 @@ pub(crate) unsafe fn init(config: Config) {
585 590
586 RCC.ccipr5().modify(|w| { 591 RCC.ccipr5().modify(|w| {
587 w.set_ckpersel(config.per_clock_source); 592 w.set_ckpersel(config.per_clock_source);
588 w.set_adcdacsel(config.adc_clock_source) 593 w.set_adcdacsel(config.adc_clock_source);
594 w.set_fdcan1sel(config.fdcan_clock_source)
589 }); 595 });
590 } 596 }
591 597