stm32_hal2/clocks/h.rs
1//! Clock config for STM32L, G, and W-series MCUs. Uses a `Clocks` struct to configure
2//! settings, starting with a `Default::default()` implementation. Uses the `setup` method
3//! to write changes.
4
5// Similar in from to the `baseline` clocks module, but includes notable differendes.
6
7use cfg_if::cfg_if;
8
9#[cfg(not(any(feature = "h5", feature = "h7b3", feature = "h735")))]
10use crate::pac::SYSCFG;
11use crate::{
12 MAX_ITERS,
13 clocks::RccError,
14 pac::{CRS, FLASH, PWR, RCC},
15};
16
17#[derive(Clone, Copy, PartialEq)]
18pub enum PllSrc {
19 None,
20 Csi,
21 Hsi(HsiDiv),
22 Hse(u32),
23}
24
25impl PllSrc {
26 /// Required due to numerical value on non-uniform discrim being experimental.
27 /// (ie, can't set on `Pll(Pllsrc)`.
28 /// See RCC_PLLCKSELR register, PLLSRC field.
29 pub fn bits(&self) -> u8 {
30 match self {
31 Self::Hsi(_) => 0b00,
32 Self::Csi => 0b01,
33 Self::Hse(_) => 0b10,
34 Self::None => 0b11,
35 }
36 }
37}
38
39#[derive(Clone, Copy, PartialEq)]
40#[repr(u8)]
41/// Select the system clock used when exiting Stop mode. Sets RCC_CFGR register, STOPWUCK field.
42pub enum StopWuck {
43 Hsi = 0,
44 Csi = 1,
45}
46
47#[derive(Clone, Copy, PartialEq)]
48#[repr(u8)]
49/// Selects USB clock source. Sets D2CCIP2R reg, USBSEL field.
50pub enum UsbSrc {
51 Disabled = 0b00,
52 Pll1Q = 0b01,
53 Pll3Q = 0b10,
54 Hsi48 = 0b11,
55}
56
57#[derive(Clone, Copy)]
58#[repr(u8)]
59/// Select the SYNC signal source. Sets the CRS_CFGR register, SYNCSRC field.
60pub enum CrsSyncSrc {
61 #[cfg(feature = "h735")] // todo: 735 and 743 are opposites here. QC which use which.
62 /// CRS_SYNC pin selected as SYNC signal source
63 CrsSync = 0b00,
64 #[cfg(not(feature = "h735"))]
65 /// USB2 SOF selected as SYNC signal source
66 Usb2 = 0b00,
67 /// LSE selected as SYNC signal source
68 Lse = 0b01,
69 /// OTG HS1 SOF selected as SYNC signal source
70 OtgHs = 0b10,
71}
72
73#[derive(Clone, Copy, PartialEq)]
74/// Clock input source, also known as system clock switch. Sets RCC_CFGR register, SW field.
75pub enum InputSrc {
76 Hsi(HsiDiv),
77 Csi,
78 Hse(u32), // freq in Mhz,
79 Pll1,
80}
81
82impl InputSrc {
83 /// Required due to numerical value on non-uniform discrim being experimental.
84 /// (ie, can't set on `Pll(Pllsrc)`.
85 /// See RCC_CFGR register, SW field.
86 pub fn bits(&self) -> u8 {
87 match self {
88 Self::Hsi(_) => 0b000,
89 Self::Csi => 0b001,
90 Self::Hse(_) => 0b010,
91 Self::Pll1 => 0b011,
92 }
93 }
94}
95
96/// Configures the speeds, and enable status of an individual PLL. Note that the `enable`
97/// field has no effect for PLL1.
98pub struct PllCfg {
99 pub enabled: bool,
100 // pub fractional: bool,
101 pub pllp_en: bool,
102 pub pllq_en: bool,
103 pub pllr_en: bool,
104 pub divm: u8,
105 pub divn: u16,
106 pub divp: u8,
107 pub divq: u8,
108 pub divr: u8,
109}
110
111impl Default for PllCfg {
112 // Note that this assumes VOS1. (Not full speed)
113 fn default() -> Self {
114 cfg_if! {
115 if #[cfg(feature = "h5")] {
116 let divn = 250;
117 } else if #[cfg(feature = "h7b3")] {
118 let divn = 280;
119 } else {
120 let divn = 400;
121 }
122 }
123
124 Self {
125 enabled: true,
126 // fractional: false,
127 pllp_en: true,
128 pllq_en: false,
129 pllr_en: false,
130 // todo: Getting mixed messages on if HSI on H5 is 16Mhz or 32Mhz.
131 // todo: Great as 32Mhz for now, and see if speed is twice as slow
132 // todo as it should be.
133 #[cfg(feature = "h5")]
134 divm: 32, // todo 16?
135 #[cfg(feature = "h7")]
136 divm: 32,
137 divn,
138 // We override DIVP1 to be 1, and lower DIVN1 to achieve full speed on H723 etc
139 // variants.
140 divp: 2,
141 // Div1Q = 2 Allows <150Mhz SAI clock, if it's configured for PLL1Q (which is its default).
142 // Div1Q = 8 Allows <200Mhz SPI1 clock, if it's configured for PLL1Q (which is its default).
143 // At 400Mhz, Sets SAI clock to 100Mhz. At 480Mhz, sets it to 120Mhz.
144 divq: 8,
145 divr: 2,
146 }
147 }
148}
149
150impl PllCfg {
151 pub fn disabled() -> Self {
152 Self {
153 enabled: false,
154 pllq_en: false,
155 pllp_en: false,
156 pllr_en: false,
157 ..Default::default()
158 }
159 }
160}
161
162#[derive(Clone, Copy)]
163#[repr(u8)]
164/// Division factor for the AHB clock. Also known as AHB Prescaler. See RCC_D1CFGR reg.
165pub enum HclkPrescaler {
166 Div1 = 0b0000,
167 Div2 = 0b1000,
168 Div4 = 0b1001,
169 Div8 = 0b1010,
170 Div16 = 0b1011,
171 Div64 = 0b1100,
172 Div128 = 0b1101,
173 Div256 = 0b1110,
174 Div512 = 0b1111,
175}
176
177impl HclkPrescaler {
178 pub fn value(&self) -> u16 {
179 match self {
180 Self::Div1 => 1,
181 Self::Div2 => 2,
182 Self::Div4 => 4,
183 Self::Div8 => 8,
184 Self::Div16 => 16,
185 Self::Div64 => 64,
186 Self::Div128 => 128,
187 Self::Div256 => 256,
188 Self::Div512 => 512,
189 }
190 }
191}
192
193#[derive(Clone, Copy)]
194#[repr(u8)]
195/// For use with `RCC_APBPPRE1`, and `RCC_APBPPRE2`. Ie, low-speed and high-speed prescalers respectively.
196pub enum ApbPrescaler {
197 Div1 = 0b000,
198 Div2 = 0b100,
199 Div4 = 0b101,
200 Div8 = 0b110,
201 Div16 = 0b111,
202}
203
204impl ApbPrescaler {
205 pub fn value(&self) -> u8 {
206 match self {
207 Self::Div1 => 1,
208 Self::Div2 => 2,
209 Self::Div4 => 4,
210 Self::Div8 => 8,
211 Self::Div16 => 16,
212 }
213 }
214}
215
216#[derive(Clone, Copy, PartialEq)]
217#[repr(u8)]
218/// SAI clock input source. Sets RCC_D2CCIP1R register, SAIxSEL field.
219pub enum SaiSrc {
220 Pll1Q = 0b000,
221 Pll2P = 0b001,
222 Pll3P = 0b010,
223 I2sCkin = 0b011,
224 PerClk = 0100,
225}
226
227#[derive(Clone, Copy, PartialEq)]
228#[repr(u8)]
229/// SPI clock input source. Sets RCC_D2CCIP1R register, SPI123SEL field..
230pub enum Spi123Src {
231 //note: This is the same as SaiSrc.
232 Pll1Q = 0b000, // This is PLL2
233 Pll2P = 0b001,
234 Pll3P = 0b010,
235 I2sCkin = 0b011,
236 PerClk = 0100,
237}
238
239#[derive(Clone, Copy, PartialEq)]
240#[repr(u8)]
241/// SPI clock input source. Sets RCC_D2CCIP1R register, SPI45SEL field.
242pub enum Spi45Src {
243 Apb = 0b000,
244 Pll2Q = 0b001,
245 Pll3Q = 0b010,
246 Hsi = 0b011,
247 Csi = 0100,
248 HseCk = 0b101,
249}
250
251#[derive(Clone, Copy, PartialEq)]
252#[repr(u8)]
253/// SAI clock input source. Sets RCC_D2CCIP1R register, DFSDM1SEL field.
254pub enum DfsdmSrc {
255 /// rcc_pclk2 is selected as DFSDM1 Clk kernel clock (default after reset)
256 Pclk2 = 0,
257 /// sys_ck clock is selected as DFSDM1 Clk kernel clock
258 Sysclk = 1,
259}
260
261#[derive(Clone, Copy, PartialEq)]
262#[repr(u8)]
263/// CAN clock input source. Sets RCC_D2CCIP1R register, FDCANSEL field.
264pub enum CanSrc {
265 /// hse_ck clock is selected as FDCAN kernel clock (default after reset)
266 Hse = 0b00,
267 /// PLL1Q
268 Pll1Q = 0b01,
269 /// PLL2Q
270 Pll2Q = 0b10,
271}
272
273#[derive(Clone, Copy, PartialEq)]
274#[repr(u8)]
275/// Clock divider for the HSI. See RCC_CR register, HSIDIV field.
276pub enum HsiDiv {
277 Div1 = 0b00,
278 Div2 = 0b01,
279 Div4 = 0b10,
280 Div8 = 0b11,
281}
282
283impl HsiDiv {
284 pub fn value(&self) -> u8 {
285 match self {
286 Self::Div1 => 1,
287 Self::Div2 => 2,
288 Self::Div4 => 4,
289 Self::Div8 => 8,
290 }
291 }
292}
293
294#[derive(Clone, Copy, PartialEq)]
295#[repr(u8)]
296/// Range for the VOS. See H743 RM, section 6.8.6: PWR D3 domain control register. Sets PWR_D3CR,
297/// `VOS` field.
298pub enum VosRange {
299 /// 1.26 V - 1.40 V
300 #[cfg(not(feature = "h7b3"))]
301 VOS0 = 0, // This will actually be VOS1, but note that we handle the case of VOS0 activation
302 // differntly than the others, using its special activation sequence.
303 /// 1.15 V - 1.26 V
304 VOS1 = 0b11,
305 /// 1.05 V - 1.15 V
306 VOS2 = 0b10,
307 /// 0.95 V - 1.05 V
308 VOS3 = 0b01,
309}
310
311impl VosRange {
312 /// Power regulator voltage scale.
313 /// Choose the wait states based on VSO range and hclk frequency.. See H743 RM, Table 17: FLASH,
314 /// or RM0468, table 16.
315 /// recommended number of wait states and programming delay. Returns a tuple of (number of wait states,
316 /// programming delay) (FLASH ACR_LATENCY, WRHIGHFREQ) values respectively.
317 pub fn wait_states(&self, hclk: u32) -> (u8, u8) {
318 // todo: 280 Mhz variants.
319 #[cfg(not(feature = "h735"))]
320 match self {
321 #[cfg(not(feature = "h7b3"))]
322 Self::VOS0 => match hclk {
323 0..=70_000_000 => (0, 0),
324 70_000_001..=140_000_000 => (1, 1),
325 140_000_001..=185_000_000 => (2, 1),
326 185_000_001..=210_000_000 => (2, 2),
327 210_000_001..=225_000_000 => (3, 2),
328 225_000_001..=240_000_000 => (4, 2),
329 _ => panic!(
330 "Can't set higher than 240Mhz HCLK with VSO0 range. (Try changing the `vos_range` setting)."
331 ),
332 },
333 Self::VOS1 => match hclk {
334 0..=70_000_000 => (0, 0),
335 70_000_001..=140_000_000 => (1, 1),
336 140_000_001..=185_000_000 => (2, 1),
337 185_000_001..=210_000_000 => (2, 2),
338 210_000_001..=225_000_000 => (3, 2),
339 _ => panic!(
340 "Can't set higher than 225Mhz HCLK with VOS1 range. (Try changing the `vos_range` setting)."
341 ),
342 },
343 Self::VOS2 => match hclk {
344 0..=55_000_000 => (0, 0),
345 55_000_001..=110_000_000 => (1, 1),
346 110_000_001..=165_000_000 => (2, 1),
347 165_000_001..=225_000_000 => (3, 2),
348 _ => panic!(
349 "Can't set higher than 225Mhz HCLK with VSO2 range. (Try changing the `vos_range` setting)."
350 ),
351 },
352 Self::VOS3 => match hclk {
353 0..=45_000_000 => (0, 0),
354 45_000_001..=90_000_000 => (1, 1),
355 90_000_001..=135_000_000 => (2, 1),
356 135_000_001..=180_000_000 => (3, 2),
357 180_000_001..=225_000_000 => (4, 2),
358 _ => panic!(
359 "Can't set higher than 225Mhz HCLK with VSO3 range. (Try changing the `vos_range` setting)."
360 ),
361 },
362 }
363
364 #[cfg(feature = "h735")]
365 match self {
366 Self::VOS0 => match hclk {
367 0..=70_000_000 => (0, 0b00),
368 70_000_001..=140_000_000 => (1, 0b01),
369 140_000_001..=210_000_000 => (2, 0b10),
370 210_000_001..=275_000_000 => (3, 0b11),
371 _ => panic!(
372 "Can't set higher than 275Mhz HCLK with VSO0 range. (Try changing the `vos_range` setting)."
373 ),
374 },
375 Self::VOS1 => match hclk {
376 0..=67_000_000 => (0, 0b00),
377 67_000_001..=133_000_000 => (1, 0b01),
378 133_000_001..=200_000_000 => (2, 0b10),
379 _ => panic!(
380 "Can't set higher than 200Mhz HCLK with VOS1 range. (Try changing the `vos_range` setting)."
381 ),
382 },
383 Self::VOS2 => match hclk {
384 0..=50_000_000 => (0, 0b00),
385 50_000_001..=100_000_000 => (1, 0b01),
386 100_000_001..=150_000_000 => (2, 0b10),
387 _ => panic!(
388 "Can't set higher than 150Mhz HCLK with VSO2 range. (Try changing the `vos_range` setting)."
389 ),
390 },
391 Self::VOS3 => match hclk {
392 0..=35_000_000 => (0, 0b00),
393 35_000_001..=70_000_000 => (1, 0b01),
394 70_000_001..=85_000_000 => (2, 0b10),
395 _ => panic!(
396 "Can't set higher than 85Mhz HCLK with VSO3 range. (Try changing the `vos_range` setting)."
397 ),
398 },
399 }
400 }
401}
402
403/// Settings used to configure clocks. Create this struct by using its `Default::default()`
404/// implementation, then modify as required, referencing your RM's clock tree,
405/// or Stm32Cube IDE's interactive clock manager. Apply settings by running `.setup()`.
406pub struct Clocks {
407 /// The main input source
408 pub input_src: InputSrc,
409 /// The source driving all PLLs.
410 pub pll_src: PllSrc,
411 /// Enable and speed status for PLL1. Note that `input_src` controls if PLL1 is enabled, not
412 /// `pll1.enabled()`.
413 pub pll1: PllCfg,
414 /// Enable and speed status for PLL2
415 pub pll2: PllCfg,
416 /// Enable and speed status for PLL3
417 pub pll3: PllCfg,
418 #[cfg(feature = "h7")]
419 /// The prescaler between sysclk and hclk
420 pub d1_core_prescaler: HclkPrescaler,
421 /// The value to divide SYSCLK by, to get systick and peripheral clocks. Also known as AHB divider
422 pub hclk_prescaler: HclkPrescaler,
423 /// APB3 peripheral clocks
424 pub d1_prescaler: ApbPrescaler,
425 /// APB1 peripheral clocks
426 pub d2_prescaler1: ApbPrescaler,
427 // todo: D2 prescaler 2 possibly not on H5.
428 /// APB2 peripheral clocks
429 pub d2_prescaler2: ApbPrescaler,
430 #[cfg(feature = "h7")]
431 /// APB4 peripheral clocks
432 pub d3_prescaler: ApbPrescaler,
433 /// Bypass the HSE output, for use with oscillators that don't need it. Saves power, and
434 /// frees up the pin for use as GPIO.
435 pub hse_bypass: bool,
436 /// USBOTG kernel clock selection. Defaults to HSI48.
437 pub usb_src: UsbSrc,
438 pub security_system: bool,
439 pub hsi48_on: bool,
440 pub stop_wuck: StopWuck,
441 pub vos_range: VosRange,
442 /// SAI1 and DFSDM1 kernel Aclk clock source selection
443 pub sai1_src: SaiSrc,
444 #[cfg(not(feature = "h735"))]
445 /// SAI2 and SAI3 kernel clock source selection
446 pub sai23_src: SaiSrc,
447 pub sai4a_src: SaiSrc,
448 pub sai4b_src: SaiSrc,
449 pub spi123_src: Spi123Src,
450 pub spi45_src: Spi45Src,
451 /// DFSDM1 kernel clock source selection
452 pub dfsdm1_src: DfsdmSrc,
453 /// FDCAN kernel clock selection. Defaults to PLL1Q.
454 pub can_src: CanSrc,
455}
456
457impl Clocks {
458 /// Setup common and return Ok if the config is valid. Abort the setup if speeds
459 /// are invalid.
460 /// Use the STM32CubeIDE Clock Configuration tab to identify valid configs.
461 /// Use the `default()` implementation as a safe baseline.
462 /// This method also configures the PWR VOS setting, and can be used to enable VOS boost,
463 /// if `vos_range` is set to `VosRange::VOS0`.
464 pub fn setup(&self) -> Result<(), RccError> {
465 if let Err(e) = self.validate_speeds() {
466 return Err(e);
467 }
468
469 let rcc = unsafe { &(*RCC::ptr()) };
470 let flash = unsafe { &(*FLASH::ptr()) };
471 let pwr = unsafe { &(*PWR::ptr()) };
472
473 // Enable and reset System Configuration Controller, ie for interrupts.
474 // todo: Is this the right module to do this in?
475 #[cfg(feature = "h7")]
476 {
477 rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
478 rcc.apb4rstr.modify(|_, w| w.syscfgrst().set_bit());
479 rcc.apb4rstr.modify(|_, w| w.syscfgrst().clear_bit());
480 }
481
482 let mut i = 0;
483
484 macro_rules! wait_hang {
485 ($i:expr) => {
486 i += 1;
487 if i >= MAX_ITERS {
488 return Err(RccError::Hardware);
489 }
490 };
491 }
492
493 // H743 RM, sefction 6.8.6, and section 6.6.2: Voltage Scaling
494 // Voltage scaling selection according to performance
495 // These bits control the VCORE voltage level and allow to obtains the best trade-off between
496 // power consumption and performance:
497 // – When increasing the performance, the voltage scaling shall be changed before increasing
498 // the system frequency.
499 // – When decreasing performance, the system frequency shall first be decreased before
500 // changing the voltage scaling.
501 match self.vos_range {
502 // todo: Do we need this on H5?
503 #[cfg(not(any(feature = "h5", feature = "h7b3", feature = "h735")))]
504 // Note:H735 etc have VOS0, but not oden; the RM doesn't list these steps.
505 VosRange::VOS0 => {
506 let syscfg = unsafe { &(*SYSCFG::ptr()) };
507
508 // VOS0 activation/deactivation sequence: H743 HRM, section 6.6.2:
509 // The system maximum frequency can be reached by boosting the voltage scaling level to
510 // VOS0. This is done through the ODEN bit in the SYSCFG_PWRCR register.
511 // The sequence to activate the VOS0 is the following:
512 // 1. Ensure that the system voltage scaling is set to VOS1 by checking the VOS bits in
513 // PWR D3 domain control register (PWR D3 domain control register (PWR_D3CR))
514 cfg_if! {
515 if #[cfg(feature = "h7")] {
516 pwr.d3cr
517 .modify(|_, w| unsafe { w.vos().bits(VosRange::VOS1 as u8) });
518
519 i = 0;
520 while pwr.d3cr.read().vosrdy().bit_is_clear() {wait_hang!(i);}
521 } else {
522 pwr.voscr
523 .modify(|_, w| unsafe { w.vos().bits(VosRange::VOS1 as u8) });
524
525 i = 0;
526 while pwr.vossr.read().vosrdy().bit_is_clear() {wait_hang!(i);}
527 }
528 }
529
530 // 2. Enable the SYSCFG clock in the RCC by setting the SYSCFGEN bit in the
531 // RCC_APB4ENR register.
532 // (Handled above)
533
534 // 3. Enable the ODEN bit in the SYSCFG_PWRCR register.
535 syscfg.pwrcr.modify(|_, w| w.oden().set_bit());
536
537 // 4. Wait for VOSRDY to be set.
538 i = 0;
539 #[cfg(feature = "h7")]
540 while pwr.d3cr.read().vosrdy().bit_is_clear() {
541 wait_hang!(i);
542 }
543 #[cfg(feature = "h5")]
544 while pwr.vossr.read().vosrdy().bit_is_clear() {
545 wait_hang!(i);
546 }
547
548 // Once the VCORE supply has reached the required level, the system frequency can be
549 // increased. Figure 31 shows the recommended sequence for switching VCORE from VOS1 to
550 // VOS0 sequence.
551 // The sequence to deactivate the VOS0 is the following:
552 // 1. Ensure that the system frequency was decreased.
553 // 2. Ensure that the SYSCFG clock is enabled in the RCC by setting the SYSCFGEN bit set
554 // in the RCC_APB4ENR register.
555 // 3. Reset the ODEN bit in the SYSCFG_PWRCR register to disable VOS0.
556 }
557 _ => {
558 #[cfg(feature = "h7")]
559 pwr.d3cr
560 .modify(|_, w| unsafe { w.vos().bits(self.vos_range as u8) });
561 #[cfg(feature = "h5")]
562 pwr.voscr
563 .modify(|_, w| unsafe { w.vos().bits(self.vos_range as u8) });
564 }
565 }
566
567 // Adjust flash wait states according to the HCLK frequency.
568 // We need to do this before enabling PLL, or it won't enable.
569 // H742 RM, Table 17.
570 let wait_states = self.vos_range.wait_states(self.hclk());
571
572 flash.acr.modify(|_, w| unsafe {
573 w.latency().bits(wait_states.0);
574 w.wrhighfreq().bits(wait_states.1)
575 });
576
577 // Enable oscillators, and wait until ready.
578 match self.input_src {
579 InputSrc::Csi => {
580 rcc.cr.modify(|_, w| w.csion().bit(true));
581 i = 0;
582 while rcc.cr.read().csirdy().bit_is_clear() {
583 wait_hang!(i);
584 }
585 }
586 InputSrc::Hse(_) => {
587 rcc.cr.modify(|_, w| w.hseon().bit(true));
588 // Wait for the HSE to be ready.
589 i = 0;
590 while rcc.cr.read().hserdy().bit_is_clear() {
591 wait_hang!(i);
592 }
593 }
594 InputSrc::Hsi(div) => {
595 rcc.cr.modify(|_, w| unsafe {
596 w.hsidiv().bits(div as u8);
597 w.hsion().bit(true)
598 });
599 i = 0;
600 while rcc.cr.read().hsirdy().bit_is_clear() {
601 wait_hang!(i);
602 }
603 }
604 InputSrc::Pll1 => {
605 // todo: PLL setup here is DRY with the HSE, HSI, and Csi setup above.
606 match self.pll_src {
607 PllSrc::Csi => {
608 rcc.cr.modify(|_, w| w.csion().bit(true));
609 i = 0;
610 while rcc.cr.read().csirdy().bit_is_clear() {
611 wait_hang!(i);
612 }
613 }
614 PllSrc::Hse(_) => {
615 rcc.cr.modify(|_, w| w.hseon().bit(true));
616 i = 0;
617 while rcc.cr.read().hserdy().bit_is_clear() {
618 wait_hang!(i);
619 }
620 }
621 PllSrc::Hsi(div) => {
622 rcc.cr.modify(|_, w| unsafe {
623 w.hsidiv().bits(div as u8);
624 w.hsion().bit(true)
625 });
626 i = 0;
627 while rcc.cr.read().hsirdy().bit_is_clear() {
628 wait_hang!(i);
629 }
630 }
631 PllSrc::None => {}
632 }
633 }
634 }
635
636 rcc.cr.modify(|_, w| {
637 // Enable bypass mode on HSE, since we're using a ceramic oscillator.
638 w.hsebyp().bit(self.hse_bypass)
639 });
640
641 rcc.cfgr.modify(|_, w| unsafe {
642 w.sw().bits(self.input_src.bits());
643 w.stopwuck().bit(self.stop_wuck as u8 != 0)
644 });
645
646 #[cfg(feature = "h7")]
647 rcc.d1cfgr.modify(|_, w| unsafe {
648 w.d1cpre().bits(self.d1_core_prescaler as u8);
649 w.d1ppre().bits(self.d1_prescaler as u8);
650 w.hpre().bits(self.hclk_prescaler as u8)
651 });
652
653 #[cfg(feature = "h7")]
654 rcc.d2cfgr.modify(|_, w| unsafe {
655 w.d2ppre1().bits(self.d2_prescaler1 as u8);
656 w.d2ppre2().bits(self.d2_prescaler2 as u8)
657 });
658
659 #[cfg(feature = "h7")]
660 rcc.d3cfgr
661 .modify(|_, w| unsafe { w.d3ppre().bits(self.d3_prescaler as u8) });
662
663 #[cfg(feature = "h5")]
664 rcc.cfgr2.modify(|_, w| unsafe {
665 w.ppre1().bits(self.d1_prescaler as u8);
666 w.ppre2().bits(self.d2_prescaler1 as u8);
667 w.ppre3().bits(self.d2_prescaler2 as u8);
668 w.hpre().bits(self.hclk_prescaler as u8)
669 });
670
671 #[cfg(not(any(feature = "h7b3", feature = "h5")))]
672 rcc.d2ccip1r.modify(|_, w| unsafe {
673 w.sai1sel().bits(self.sai1_src as u8);
674 #[cfg(not(feature = "h735"))]
675 w.sai23sel().bits(self.sai23_src as u8);
676 w.spi123sel().bits(self.spi123_src as u8);
677 w.spi45sel().bits(self.spi45_src as u8);
678 w.dfsdm1sel().bit(self.dfsdm1_src as u8 != 0);
679 w.fdcansel().bits(self.can_src as u8)
680 });
681
682 // todo: Add config enums for these, and add them as Clocks fields.
683 #[cfg(not(any(feature = "h7b3", feature = "h5")))]
684 rcc.d2ccip2r
685 .modify(|_, w| unsafe { w.usbsel().bits(self.usb_src as u8) });
686
687 #[cfg(not(any(feature = "h7b3", feature = "h5")))]
688 rcc.d3ccipr.modify(|_, w| unsafe {
689 w.sai4asel().bits(self.sai4a_src as u8);
690 w.sai4bsel().bits(self.sai4b_src as u8)
691 });
692
693 // #[cfg(feature = "h5")]
694 // rcc.ccipr1.modify(|_, w| unsafe {
695 // });
696
697 // #[cfg(feature = "h5")]
698 // rcc.ccipr2.modify(|_, w| unsafe {
699 // });
700
701 #[cfg(feature = "h5")]
702 rcc.ccipr3.modify(|_, w| unsafe {
703 // todo: This is broken down into each spi individually on H5.
704 w.spi1sel().bits(self.spi123_src as u8);
705 w.spi2sel().bits(self.spi123_src as u8);
706 w.spi3sel().bits(self.spi45_src as u8);
707 w.spi4sel().bits(self.spi45_src as u8);
708 w.spi5sel().bits(self.spi123_src as u8)
709 // w.spi6sel().bits(self.spi123_src as u8);
710 });
711
712 #[cfg(feature = "h5")]
713 rcc.ccipr4.modify(|_, w| unsafe {
714 w.usbfssel().bits(self.usb_src as u8)
715 // Also: OctoSPI and I2C.
716 });
717
718 #[cfg(feature = "h5")]
719 rcc.ccipr5.modify(|_, w| unsafe {
720 w.sai1sel().bits(self.sai1_src as u8);
721 w.sai2sel().bits(self.sai23_src as u8);
722 w.fdcan12sel().bits(self.can_src as u8)
723 // also: ADC and DAC.
724 });
725
726 rcc.cr.modify(|_, w| w.hsecsson().bit(self.security_system));
727
728 // todo: Allow configuring the PLL in fractional mode.
729
730 #[cfg(feature = "h7")]
731 rcc.pllckselr
732 .modify(|_, w| w.pllsrc().bits(self.pll_src.bits()));
733
734 // Note that with this code setup, PLL2 and PLL3 won't work properly unless using
735 // the input source is PLL1.
736 if let InputSrc::Pll1 = self.input_src {
737 // Turn off the PLL: Required for modifying some of the settings below.
738 rcc.cr.modify(|_, w| w.pll1on().clear_bit());
739 // Wait for the PLL to no longer be ready before executing certain writes.
740 while rcc.cr.read().pll1rdy().bit_is_set() {}
741
742 // Set and reset by software to select the proper reference frequency range used for PLL1.
743 // This bit must be written before enabling the PLL1.
744 let pll1_rng_val = match self.pll_input_speed(self.pll_src, 1) {
745 1_000_000..=2_000_000 => 0b00,
746 2_000_001..=4_000_000 => 0b01,
747 4_000_001..=8_000_000 => 0b10,
748 8_000_001..=16_000_000 => 0b11,
749 _ => panic!("PLL1 input source must be between 1Mhz and 16Mhz."),
750 };
751
752 // todo: Don't enable all these pqr etc by default!
753 // todo don't enable pll2 and 3 by default!!
754 // todo: This has an impact on power consumption.
755
756 // todo: MOre DRY
757 // H743 RM:
758 // 0: Wide VCO range: 192 to 836 MHz (default after reset)
759 // 1: Medium VCO range: 150 to 420 MHz
760 let pll1_vco = match self.pll_input_speed(self.pll_src, 1) {
761 1_000_000..=2_000_000 => 1,
762 2_000_001..=16_000_000 => 0,
763 _ => panic!("PLL1 input source must be between 1Mhz and 16Mhz."),
764 };
765
766 // The user application can then configure the proper VCO: if the frequency of the reference
767 // clock is lower or equal to 2 MHz, then VCOL must be selected, otherwise VCOH must be
768 // chosen. To reduce the power consumption, it is recommended to configure the VCO output
769 // to the lowest frequency.
770
771 // The frequency of the reference clock provided to the PLLs (refx_ck) must range from 1 to
772 // 16 MHz. The user application has to program properly the DIVMx dividers of the RCC PLLs
773 // Clock Source Selection Register (RCC_PLLCKSELR) in order to match this condition. In
774 // addition, the PLLxRGE of the RCC PLLs Configuration Register (RCC_PLLCFGR) field
775 // must be set according to the reference input frequency to guarantee an optimal
776 // performance of the PLL.
777
778 // If using multiple PLLs, we do a write to `PLLCLKSELR` and `PLLCFGR` for each
779 // enabled PLL. This is unecessary, but makes the code clearer. This is worth it, given
780 // we expect the user to run `.setup()` only once.
781 #[cfg(feature = "h7")]
782 rcc.pllckselr.modify(|_, w| w.divm1().bits(self.pll1.divm));
783
784 #[cfg(feature = "h7")]
785 rcc.pllcfgr.modify(|_, w| {
786 w.pll1rge().bits(pll1_rng_val);
787 w.pll1vcosel().bit(pll1_vco != 0);
788 w.divp1en().bit(true);
789 w.divq1en().bit(self.pll1.pllq_en);
790 w.divr1en().bit(self.pll1.pllr_en)
791 });
792
793 #[cfg(feature = "h5")]
794 rcc.pll1cfgr.modify(|_, w| unsafe {
795 w.pll1src().bits(self.pll_src.bits());
796 w.divm1().bits(self.pll1.divm);
797 w.pll1rge().bits(pll1_rng_val);
798 w.pll1vcosel().bit(pll1_vco != 0);
799 w.pll1pen().bit(true);
800 w.pll1qen().bit(self.pll1.pllq_en);
801 w.pll1ren().bit(self.pll1.pllr_en)
802 });
803
804 #[cfg(feature = "h7")]
805 rcc.pll1divr.modify(|_, w| unsafe {
806 w.divn1().bits(self.pll1.divn - 1);
807 w.divp1().bits(self.pll1.divp - 1);
808 w.divq1().bits(self.pll1.divq - 1);
809 w.divr1().bits(self.pll1.divr - 1)
810 });
811
812 #[cfg(feature = "h5")]
813 rcc.pll1divr.modify(|_, w| unsafe {
814 w.pll1n().bits(self.pll1.divn - 1);
815 w.pll1p().bits(self.pll1.divp - 1);
816 w.pll1q().bits(self.pll1.divq - 1);
817 w.pll1r().bits(self.pll1.divr - 1)
818 });
819
820 // Now turn PLL back on, once we're configured things that can only be set with it off.
821 rcc.cr.modify(|_, w| w.pll1on().set_bit());
822 i = 0;
823 while rcc.cr.read().pll1rdy().bit_is_clear() {
824 wait_hang!(i);
825 }
826 }
827
828 // todo DRY
829 if self.pll2.enabled {
830 rcc.cr.modify(|_, w| w.pll2on().clear_bit());
831 i = 0;
832 while rcc.cr.read().pll2rdy().bit_is_set() {
833 wait_hang!(i);
834 }
835
836 let pll2_rng_val = match self.pll_input_speed(self.pll_src, 2) {
837 1_000_000..=2_000_000 => 0b00,
838 2_000_001..=4_000_000 => 0b01,
839 4_000_001..=8_000_000 => 0b10,
840 8_000_001..=16_000_000 => 0b11,
841 _ => panic!("PLL2 input source must be between 1Mhz and 16Mhz."),
842 };
843
844 let pll2_vco = match self.pll_input_speed(self.pll_src, 2) {
845 0..=2_000_000 => 0,
846 _ => 1,
847 };
848
849 #[cfg(feature = "h7")]
850 rcc.pllckselr.modify(|_, w| w.divm2().bits(self.pll2.divm));
851
852 #[cfg(feature = "h7")]
853 rcc.pllcfgr.modify(|_, w| {
854 w.pll2rge().bits(pll2_rng_val);
855 w.pll2vcosel().bit(pll2_vco != 0);
856 w.divp2en().bit(self.pll2.pllp_en);
857 w.divq2en().bit(self.pll2.pllq_en);
858 w.divr2en().bit(self.pll2.pllr_en)
859 });
860
861 #[cfg(feature = "h5")]
862 rcc.pll2cfgr.modify(|_, w| unsafe {
863 w.pll2src().bits(self.pll_src.bits());
864 w.pll2rge().bits(pll2_rng_val);
865 w.pll2vcosel().bit(pll2_vco != 0);
866 w.pll2pen().bit(self.pll2.pllp_en);
867 w.pll2qen().bit(self.pll2.pllq_en);
868 w.pll2ren().bit(self.pll2.pllr_en)
869 });
870
871 #[cfg(feature = "h7")]
872 rcc.pll2divr.modify(|_, w| unsafe {
873 w.divn2().bits(self.pll2.divn - 1);
874 w.divp2().bits(self.pll2.divp - 1);
875 w.divq2().bits(self.pll2.divq - 1);
876 w.divr2().bits(self.pll2.divr - 1)
877 });
878
879 #[cfg(feature = "h5")]
880 rcc.pll2divr.modify(|_, w| unsafe {
881 w.pll2n().bits(self.pll2.divn - 1);
882 w.pll2p().bits(self.pll2.divp - 1);
883 w.pll2q().bits(self.pll2.divq - 1);
884 w.pll2r().bits(self.pll2.divr - 1)
885 });
886
887 rcc.cr.modify(|_, w| w.pll2on().set_bit());
888 i = 0;
889 while rcc.cr.read().pll2rdy().bit_is_clear() {
890 wait_hang!(i);
891 }
892 }
893
894 if self.pll3.enabled {
895 rcc.cr.modify(|_, w| w.pll3on().clear_bit());
896 i = 0;
897 while rcc.cr.read().pll3rdy().bit_is_set() {
898 wait_hang!(i);
899 }
900
901 let pll3_rng_val = match self.pll_input_speed(self.pll_src, 3) {
902 1_000_000..=2_000_000 => 0b00,
903 2_000_001..=4_000_000 => 0b01,
904 4_000_001..=8_000_000 => 0b10,
905 8_000_001..=16_000_000 => 0b11,
906 _ => panic!("PLL3 input source must be between 1Mhz and 16Mhz."),
907 };
908
909 let pll3_vco = match self.pll_input_speed(self.pll_src, 3) {
910 0..=2_000_000 => 0,
911 _ => 1,
912 };
913
914 #[cfg(feature = "h7")]
915 rcc.pllckselr.modify(|_, w| w.divm3().bits(self.pll3.divm));
916
917 #[cfg(feature = "h7")]
918 rcc.pllcfgr.modify(|_, w| {
919 w.pll3rge().bits(pll3_rng_val);
920 w.pll3vcosel().bit(pll3_vco != 0);
921 w.divp3en().bit(self.pll3.pllp_en);
922 w.divq3en().bit(self.pll3.pllq_en);
923 w.divr3en().bit(self.pll3.pllr_en)
924 });
925
926 #[cfg(feature = "h5")]
927 rcc.pll3cfgr.modify(|_, w| unsafe {
928 w.pll3src().bits(self.pll_src.bits());
929 w.pll3rge().bits(pll3_rng_val);
930 w.pll3vcosel().bit(pll3_vco != 0);
931 w.pll3pen().bit(self.pll3.pllp_en);
932 w.pll3qen().bit(self.pll3.pllq_en);
933 w.pll3ren().bit(self.pll3.pllr_en)
934 });
935
936 #[cfg(feature = "h7")]
937 rcc.pll3divr.modify(|_, w| unsafe {
938 w.divn3().bits(self.pll3.divn - 1);
939 w.divp3().bits(self.pll3.divp - 1);
940 w.divq3().bits(self.pll3.divq - 1);
941 w.divr3().bits(self.pll3.divr - 1)
942 });
943
944 #[cfg(feature = "h5")]
945 rcc.pll3divr.modify(|_, w| unsafe {
946 w.pll3n().bits(self.pll3.divn - 1);
947 w.pll3p().bits(self.pll3.divp - 1);
948 w.pll3q().bits(self.pll3.divq - 1);
949 w.pll3r().bits(self.pll3.divr - 1)
950 });
951
952 rcc.cr.modify(|_, w| w.pll3on().set_bit());
953 i = 0;
954 while rcc.cr.read().pll3rdy().bit_is_clear() {
955 wait_hang!(i);
956 }
957 }
958
959 if self.hsi48_on {
960 rcc.cr.modify(|_, w| w.hsi48on().set_bit());
961 i = 0;
962 while rcc.cr.read().hsi48rdy().bit_is_clear() {
963 wait_hang!(i);
964 }
965 }
966
967 Ok(())
968 }
969
970 /// Re-select input source; used on Stop and Standby modes, where the system reverts
971 /// to HSI after wake.
972 pub fn reselect_input(&self) -> Result<(), RccError> {
973 // Re-select the input source; it will revert to HSI during `Stop` or `Standby` mode.
974
975 let rcc = unsafe { &(*RCC::ptr()) };
976
977 let mut i = 0;
978 macro_rules! wait_hang {
979 ($i:expr) => {
980 i += 1;
981 if i >= MAX_ITERS {
982 return Err(RccError::Hardware);
983 }
984 };
985 }
986
987 // Note: It would save code repetition to pass the `Clocks` struct in and re-run setup
988 // todo: But this saves a few reg writes.
989 match self.input_src {
990 InputSrc::Hse(_) => {
991 rcc.cr.modify(|_, w| w.hseon().set_bit());
992 i = 0;
993 while rcc.cr.read().hserdy().bit_is_clear() {
994 wait_hang!(i);
995 }
996
997 rcc.cfgr
998 .modify(|_, w| unsafe { w.sw().bits(self.input_src.bits()) });
999 }
1000 InputSrc::Pll1 => {
1001 // todo: DRY with above.
1002 match self.pll_src {
1003 PllSrc::Hse(_) => {
1004 rcc.cr.modify(|_, w| w.hseon().set_bit());
1005 i = 0;
1006 while rcc.cr.read().hserdy().bit_is_clear() {
1007 wait_hang!(i);
1008 }
1009 }
1010 PllSrc::Hsi(div) => {
1011 // Generally reverts to Csi (see note below)
1012 rcc.cr.modify(|_, w| unsafe {
1013 w.hsidiv().bits(div as u8); // todo: Do we need to reset the HSI div after low power?
1014 w.hsion().bit(true)
1015 });
1016 i = 0;
1017 while rcc.cr.read().hsirdy().bit_is_clear() {
1018 wait_hang!(i);
1019 }
1020 }
1021 PllSrc::Csi => (), // todo
1022 PllSrc::None => (),
1023 }
1024
1025 // todo: PLL 2 and 3?
1026 rcc.cr.modify(|_, w| w.pll1on().clear_bit());
1027 i = 0;
1028 while rcc.cr.read().pll1rdy().bit_is_set() {
1029 wait_hang!(i);
1030 }
1031
1032 rcc.cfgr
1033 .modify(|_, w| unsafe { w.sw().bits(self.input_src.bits()) });
1034
1035 rcc.cr.modify(|_, w| w.pll1on().set_bit());
1036 i = 0;
1037 while rcc.cr.read().pll1rdy().bit_is_clear() {
1038 wait_hang!(i);
1039 }
1040 }
1041 InputSrc::Hsi(div) => {
1042 {
1043 // From Reference Manual, RCC_CFGR register section:
1044 // "Configured by HW to force Csi oscillator selection when exiting Standby or Shutdown mode.
1045 // Configured by HW to force Csi or HSI16 oscillator selection when exiting Stop mode or in
1046 // case of failure of the HSE oscillator, depending on STOPWUCK value."
1047 // In tests, from stop, it tends to revert to Csi.
1048 rcc.cr.modify(|_, w| unsafe {
1049 w.hsidiv().bits(div as u8); // todo: Do we need to reset the HSI div after low power?
1050 w.hsion().bit(true)
1051 });
1052 i = 0;
1053 while rcc.cr.read().hsirdy().bit_is_clear() {
1054 wait_hang!(i);
1055 }
1056 }
1057 }
1058 InputSrc::Csi => (), // ?
1059 }
1060
1061 Ok(())
1062 }
1063
1064 /// Calculate the input speed to the PLL. This must be between 1 and 16 Mhz. Called `refx_ck`
1065 /// in the RM.
1066 pub fn pll_input_speed(&self, pll_src: PllSrc, pll_num: u8) -> u32 {
1067 let input_freq = match pll_src {
1068 PllSrc::Csi => 4_000_000,
1069 // todo: QC if H5 HSI is 64 or 32M.
1070 PllSrc::Hsi(div) => 64_000_000 / (div.value() as u32),
1071 PllSrc::Hse(freq) => freq,
1072 PllSrc::None => 0,
1073 };
1074
1075 match pll_num {
1076 1 => input_freq / (self.pll1.divm as u32),
1077 2 => input_freq / (self.pll2.divm as u32),
1078 3 => input_freq / (self.pll3.divm as u32),
1079 _ => panic!("Pll num must be between 1 and 3."),
1080 }
1081 }
1082
1083 /// Calculate VCO output frequency: = Fref1_ck x DIVN1
1084 pub fn vco_output_freq(&self, pll_src: PllSrc, pll_num: u8) -> u32 {
1085 let input_speed = self.pll_input_speed(pll_src, pll_num);
1086 match pll_num {
1087 1 => input_speed * self.pll1.divn as u32,
1088 2 => input_speed * self.pll2.divn as u32,
1089 3 => input_speed * self.pll3.divn as u32,
1090 _ => panic!("Pll num must be between 1 and 3."),
1091 }
1092 }
1093
1094 /// Check if the PLL is enabled. This is useful if checking whether to re-enable the PLL
1095 /// after exiting Stop or Standby modes, eg so you don't re-enable if it was already re-enabled
1096 /// in a different context. eg:
1097 /// ```
1098 /// if !clock_cfg.pll_is_enabled() {
1099 /// clock_cfg.reselect_input();
1100 ///}
1101 ///```
1102 pub fn pll_is_enabled(&self) -> bool {
1103 let rcc = unsafe { &(*RCC::ptr()) };
1104 rcc.cr.read().pll1on().bit_is_set()
1105 }
1106
1107 /// Calculate the sysclock frequency, in hz. Note that for dual core variants, this is for CPU1.
1108 /// CPU2 syclock is equal to the HCLK, so use the `hclk()` method.
1109 pub fn sysclk(&self) -> u32 {
1110 match self.input_src {
1111 InputSrc::Pll1 => {
1112 // divm1 is included in `pll_input_speed`.
1113 self.pll_input_speed(self.pll_src, 1) * self.pll1.divn as u32
1114 / self.pll1.divp as u32
1115 }
1116 InputSrc::Csi => 4_000_000,
1117 // todo: QC if H5 is 32 or 64M.
1118 InputSrc::Hsi(div) => 64_000_000 / (div.value() as u32),
1119 InputSrc::Hse(freq) => freq,
1120 }
1121 }
1122
1123 #[cfg(feature = "h7")]
1124 /// Get the Domain 1 core prescaler frequency, in hz
1125 pub fn d1cpreclk(&self) -> u32 {
1126 self.sysclk() / self.d1_core_prescaler.value() as u32
1127 }
1128
1129 /// Get the HCLK frequency, in hz
1130 pub fn hclk(&self) -> u32 {
1131 #[cfg(feature = "h7")]
1132 return self.sysclk()
1133 / self.d1_core_prescaler.value() as u32
1134 / self.hclk_prescaler.value() as u32;
1135 #[cfg(feature = "h5")]
1136 return self.sysclk() / self.hclk_prescaler.value() as u32;
1137 }
1138
1139 /// Get the systick speed. Note that for dual core variants, this is for CPU1.
1140 /// CPU2 systick is equal to the HCLK (possibly divided by 8), so use the `hclk()` method.
1141 pub fn systick(&self) -> u32 {
1142 // todo: There's an optional /8 divider we're not taking into account here.
1143 #[cfg(feature = "h7")]
1144 return self.d1cpreclk();
1145 #[cfg(feature = "h5")]
1146 return self.sysclk();
1147 }
1148
1149 /// Get the USB clock frequency, in hz
1150 pub fn usb(&self) -> u32 {
1151 // let (input_freq, _) = sysclock(self.input_src, self.divm1, self.divn1, self.divp1);
1152 // (input_freq * 1_000_000) as u32 / self.divm1 as u32 * self.pll_sai1_mul as u32 / 2
1153 0 // todo
1154 }
1155
1156 pub fn apb1(&self) -> u32 {
1157 self.hclk() / self.d2_prescaler1.value() as u32
1158 }
1159
1160 /// Get the frequency used by APB1 timers, in hz
1161 pub fn apb1_timer(&self) -> u32 {
1162 if let ApbPrescaler::Div1 = self.d2_prescaler1 {
1163 self.apb1()
1164 } else {
1165 self.apb1() * 2
1166 }
1167 }
1168
1169 pub fn apb2(&self) -> u32 {
1170 self.hclk() / self.d2_prescaler2.value() as u32
1171 }
1172
1173 /// Get the frequency used by APB2 timers, in hz
1174 pub fn apb2_timer(&self) -> u32 {
1175 if let ApbPrescaler::Div1 = self.d2_prescaler2 {
1176 self.apb2()
1177 } else {
1178 self.apb2() * 2
1179 }
1180 }
1181
1182 /// Get the SAI1 audio clock frequency, in hz
1183 pub fn sai1_speed(&self) -> u32 {
1184 let pll_src = match self.input_src {
1185 InputSrc::Pll1 => self.pll_src,
1186 InputSrc::Csi => PllSrc::Csi,
1187 InputSrc::Hsi(div) => PllSrc::Hsi(div),
1188 InputSrc::Hse(freq) => PllSrc::Hse(freq),
1189 };
1190
1191 match self.sai1_src {
1192 SaiSrc::Pll1Q => {
1193 self.pll_input_speed(pll_src, 1) * self.pll1.divn as u32 / self.pll1.divq as u32
1194 }
1195 SaiSrc::Pll2P => {
1196 self.pll_input_speed(pll_src, 1) * self.pll2.divn as u32 / self.pll2.divp as u32
1197 }
1198 SaiSrc::Pll3P => {
1199 self.pll_input_speed(pll_src, 1) * self.pll3.divn as u32 / self.pll3.divp as u32
1200 }
1201 SaiSrc::I2sCkin => unimplemented!(),
1202 SaiSrc::PerClk => unimplemented!(),
1203 }
1204 }
1205
1206 pub fn validate_speeds(&self) -> Result<(), RccError> {
1207 cfg_if! {
1208 if #[cfg(feature = "h735")] {
1209 let max_sysclk = 480_000_000;
1210 } else {
1211 let max_sysclk = 550_000_000;
1212 }
1213 }
1214 // #[cfg(feature = "h743")]
1215 let max_hclk = 240_000_000;
1216 // #[cfg(feature = "h743")]
1217 let max_apb = 120_000_000; // todo: Different depending on apb
1218
1219 // todo: PLL input (Post DIVM1, pre DIVN1) must be between 1Mhz and 16 Mhz
1220
1221 // todo after pool today: FIgure out what other bit needs to be set to reflect this.
1222
1223 // todo: Are these valid for all H7 configs?
1224 if self.pll1.divm > 63
1225 || self.pll1.divm > 63
1226 || self.pll3.divm > 63
1227 || self.pll1.divn > 512
1228 || self.pll2.divn > 512
1229 || self.pll3.divn > 512
1230 || self.pll1.divp > 128
1231 || self.pll2.divp > 128
1232 || self.pll3.divp > 128
1233 || self.pll1.divp < 2
1234 || self.pll2.divp < 2
1235 || self.pll3.divp < 2
1236 // todo: R and Q
1237 {
1238 return Err(RccError::Speed);
1239 }
1240
1241 if let InputSrc::Pll1 = self.input_src {
1242 let pll_input_speed = self.pll_input_speed(self.pll_src, 1);
1243 if pll_input_speed < 1_000_000 || pll_input_speed > 16_000_000 {
1244 return Err(RccError::Speed);
1245 }
1246 // VCO0: Wide VCO range: 192 to 836 MHz (default after reset) (VCOH)
1247 // Note: The RM appears out of date: Revision "V" supports 960_000_000Mhz
1248 // VCO speed, to allow a max core speed of 480Mhz.
1249 let vco_speed = self.vco_output_freq(self.pll_src, 1);
1250 if pll_input_speed >= 2_000_000 && (vco_speed < 192_000_000 || vco_speed > 960_000_000)
1251 {
1252 return Err(RccError::Speed);
1253 }
1254 // 1: Medium VCO range: 150 to 420 MHz. (VCOL)
1255 // Note: You may get power savings
1256 if pll_input_speed < 2_000_000 && (vco_speed < 150_000_000 || vco_speed > 420_000_000) {
1257 return Err(RccError::Speed);
1258 }
1259 }
1260
1261 // todo: Validate PLL2 and PLL3 speeds.
1262
1263 // todo: More work on this, including feature gates
1264
1265 // todo: QC these limits
1266 // todo: Note that this involves repeatedly calculating sysclk.
1267 // todo. We could work around thsi by calcing it once here.
1268 if self.sysclk() > max_sysclk {
1269 return Err(RccError::Speed);
1270 }
1271
1272 if self.hclk() > max_hclk {
1273 return Err(RccError::Speed);
1274 }
1275
1276 if self.apb1() > max_apb {
1277 return Err(RccError::Speed);
1278 }
1279
1280 if self.apb2() > max_apb {
1281 return Err(RccError::Speed);
1282 }
1283
1284 // todo: Apb3/4
1285
1286 Ok(())
1287 }
1288}
1289
1290// todo: support default for 280Mhz variants.
1291
1292impl Default for Clocks {
1293 /// This default configures clocks with the HSI, and a 400Mhz sysclock speed. (280Mhz sysclock
1294 /// on variants that only go that high). Note that H723-745 still use this default speed
1295 /// due to needing VOS0 for higher.
1296 /// Peripheral and timer clocks are set to 100Mhz or 200Mhz, depending on their limits.
1297 /// HSE output is not bypassed.
1298 fn default() -> Self {
1299 Self {
1300 /// The input source for the system and peripheral clocks. Eg HSE, HSI, PLL etc
1301 input_src: InputSrc::Pll1,
1302 pll_src: PllSrc::Hsi(HsiDiv::Div1),
1303 pll1: PllCfg::default(),
1304 pll2: PllCfg::disabled(),
1305 pll3: PllCfg::disabled(),
1306 #[cfg(feature = "h7")]
1307 d1_core_prescaler: HclkPrescaler::Div1,
1308 d1_prescaler: ApbPrescaler::Div2,
1309 /// The value to divide SYSCLK by, to get systick and peripheral clocks. Also known as AHB divider
1310 #[cfg(feature = "h5")]
1311 hclk_prescaler: HclkPrescaler::Div1,
1312 #[cfg(feature = "h7")]
1313 hclk_prescaler: HclkPrescaler::Div2,
1314 #[cfg(feature = "h5")]
1315 d2_prescaler1: ApbPrescaler::Div1,
1316 #[cfg(feature = "h7")]
1317 d2_prescaler1: ApbPrescaler::Div2,
1318 #[cfg(feature = "h5")]
1319 d2_prescaler2: ApbPrescaler::Div1,
1320 #[cfg(feature = "h7")]
1321 d2_prescaler2: ApbPrescaler::Div2,
1322 #[cfg(feature = "h7")]
1323 d3_prescaler: ApbPrescaler::Div2,
1324 /// Bypass the HSE output, for use with oscillators that don't need it. Saves power, and
1325 /// frees up the pin for use as GPIO.
1326 hse_bypass: false,
1327 usb_src: UsbSrc::Hsi48,
1328 security_system: false,
1329 /// Enable the HSI48.
1330 hsi48_on: false,
1331 /// Select the input source to use after waking up from `stop` mode. Eg HSI or MSI.
1332 stop_wuck: StopWuck::Hsi,
1333 /// Note that to get full speed, we need to use VOS0. These are configured
1334 /// in the `full_speed` method.
1335 vos_range: VosRange::VOS1,
1336 sai1_src: SaiSrc::Pll1Q,
1337 #[cfg(not(feature = "h735"))]
1338 sai23_src: SaiSrc::Pll1Q,
1339 sai4a_src: SaiSrc::Pll1Q,
1340 sai4b_src: SaiSrc::Pll1Q,
1341 spi123_src: Spi123Src::Pll1Q,
1342 spi45_src: Spi45Src::Apb,
1343 dfsdm1_src: DfsdmSrc::Pclk2,
1344 can_src: CanSrc::Pll1Q,
1345 }
1346 }
1347}
1348
1349#[cfg(not(feature = "h7b3"))] // todo
1350impl Clocks {
1351 /// Full speed of 480Mhz, with VC0 range 0. Correspondingly higher periph clock speeds as well.
1352 /// (520Mhz core speed on H723-35) Note that special consideration needs to be taken
1353 /// when using low power modes (ie anything with wfe or wfi) in this mode; may need to manually
1354 /// disable and re-enable it.
1355 ///
1356 /// Note that this overwrites some fields of the pll1 config.
1357 pub fn full_speed() -> Self {
1358 // todo: 550Mhz on on H723 etc instead of 520Mhz. Need to set CPU_FREQ_BOOST as well for that.
1359 // todo: "The CPU frequency boost can be enabled through the CPUFREQ_BOOST option byte in
1360 // todo FLASH_OPTSR2_PRG register."
1361 // todo: The change is
1362 //
1363 cfg_if! {
1364 if #[cfg(feature = "h735")] {
1365 // let flash_regs = unsafe { &(*pac::FLASH::ptr()) };
1366
1367 // To modify user option bytes, follow the sequence below:
1368 // 1.Unlock FLASH_OPTCR register as described in Section 4.5.1: FLASH configuration
1369 // protection, unless the register is already unlocked.
1370 // 2. Write the desired new option byte values in the corresponding option registers
1371 // (FLASH_XXX_PRG).
1372 // flash_regs.optsr2_prg.modify(|_, w| w.cpufreq_boost().set_bit());
1373 // 3. Set the option byte start change OPTSTART bit to 1 in the FLASH_OPTCR register.
1374 // flash_regs.optcr.modify(|_, w| w.optstart().set_bit());
1375 // 4. Wait until OPT_BUSY bit is cleared.
1376 // while flash_regs.optcr.read().opt_busy().bit_is_set() { }
1377
1378 // let divn 275; // for 550Mhz
1379 let divn = 260;
1380 let divp = 1;
1381 } else {
1382 let divn = 480;
1383 let divp = 2;
1384 }
1385 }
1386 Self {
1387 pll1: PllCfg {
1388 divn,
1389 divp,
1390 ..Default::default()
1391 },
1392 vos_range: VosRange::VOS0,
1393 ..Default::default()
1394 }
1395 }
1396}
1397
1398#[cfg(not(feature = "h5"))] // todo: Come back to
1399// todo
1400/// Enable the Clock Recovery System.
1401/// "The STM32L443xx devices embed a special block which allows automatic trimming of the
1402/// internal 48 MHz oscillator to guarantee its optimal accuracy over the whole device
1403/// operational range. This automatic trimming is based on the external synchronization signal,
1404/// which could be either derived from USB SOF signalization, from LSE oscillator, from an
1405/// external signal on CRS_SYNC pin or generated by user software. For faster lock-in during
1406/// startup it is also possible to combine automatic trimming with manual trimming action."
1407/// Note: This is for HSI48 only. Note that the HSI will turn off after entering Stop or Standby.
1408pub fn enable_crs(sync_src: CrsSyncSrc) {
1409 let crs = unsafe { &(*CRS::ptr()) };
1410 let rcc = unsafe { &(*RCC::ptr()) };
1411
1412 rcc.apb1henr.modify(|_, w| w.crsen().set_bit());
1413
1414 crs.cfgr
1415 .modify(|_, w| unsafe { w.syncsrc().bits(sync_src as u8) });
1416
1417 crs.cr.modify(|_, w| {
1418 // Set autotrim enabled.
1419 w.autotrimen().set_bit();
1420 // Enable CRS
1421 w.cen().set_bit()
1422 });
1423
1424 // "The internal 48 MHz RC oscillator is mainly dedicated to provide a high precision clock to
1425 // the USB peripheral by means of a special Clock Recovery System (CRS) circuitry. The CRS
1426 // can use the USB SOF signal, the LSE or an external signal to automatically and quickly
1427 // adjust the oscillator frequency on-fly. It is disabled as soon as the system enters Stop or
1428 // Standby mode. When the CRS is not used, the HSI48 RC oscillator runs on its default
1429 // frequency which is subject to manufacturing process variations
1430}