aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreverdrone <[email protected]>2025-09-28 01:18:40 +0200
committereverdrone <[email protected]>2025-09-28 01:18:40 +0200
commit97b0afb8d000bcc72582a09e3b9c2d7c33b66e7a (patch)
tree37f59613ef658e807326188373e5326ff77c10eb
parent2674462a4d21b4901fceb32f6534160a44d1a564 (diff)
Calculate RCC frequencies
-rw-r--r--embassy-stm32/src/gpio.rs6
-rw-r--r--embassy-stm32/src/rcc/n6.rs259
2 files changed, 216 insertions, 49 deletions
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs
index ba5cf24c6..5645f71cb 100644
--- a/embassy-stm32/src/gpio.rs
+++ b/embassy-stm32/src/gpio.rs
@@ -654,7 +654,7 @@ fn set_as_af(pin_port: PinNumber, af_num: u8, af_type: AfType) {
654 let r = pin.block(); 654 let r = pin.block();
655 let n = pin._pin() as usize; 655 let n = pin._pin() as usize;
656 656
657 r.afr(n / 8).modify(|w| w.set_afr(n % 8, af_num as u8)); 657 r.afr(n / 8).modify(|w| w.set_afr(n % 8, af_num));
658 r.pupdr().modify(|w| w.set_pupdr(n, af_type.pupdr)); 658 r.pupdr().modify(|w| w.set_pupdr(n, af_type.pupdr));
659 r.otyper().modify(|w| w.set_ot(n, af_type.ot)); 659 r.otyper().modify(|w| w.set_ot(n, af_type.ot));
660 r.ospeedr().modify(|w| w.set_ospeedr(n, af_type.ospeedr)); 660 r.ospeedr().modify(|w| w.set_ospeedr(n, af_type.ospeedr));
@@ -798,6 +798,10 @@ pub(crate) trait SealedPin {
798 } 798 }
799} 799}
800 800
801/// GPIO pin number type.
802///
803/// Some chips have a total number of ports that exceeds 8, a larger integer
804/// is needed to hold the total pin number `(ports * number)`.
801#[cfg(not(stm32n6))] 805#[cfg(not(stm32n6))]
802pub type PinNumber = u8; 806pub type PinNumber = u8;
803#[cfg(stm32n6)] 807#[cfg(stm32n6)]
diff --git a/embassy-stm32/src/rcc/n6.rs b/embassy-stm32/src/rcc/n6.rs
index 51fc8cdc8..5d2003325 100644
--- a/embassy-stm32/src/rcc/n6.rs
+++ b/embassy-stm32/src/rcc/n6.rs
@@ -1,5 +1,6 @@
1use stm32_metapac::rcc::vals::{ 1use stm32_metapac::rcc::vals::{
2 Cpusw, Cpusws, Hseext, Hsitrim, Icint, Icsel, Msifreqsel, Plldivm, Pllmodssdis, Pllpdiv, Pllsel, Syssw, Syssws, 2 Cpusw, Cpusws, Hseext, Hsitrim, Icint, Icsel, Msifreqsel, Plldivm, Pllmodssdis, Pllpdiv, Pllsel, Syssw, Syssws,
3 Timpre,
3}; 4};
4pub use stm32_metapac::rcc::vals::{ 5pub use stm32_metapac::rcc::vals::{
5 Hpre as AhbPrescaler, Hsidiv as HsiPrescaler, Hsitrim as HsiCalibration, Ppre as ApbPrescaler, 6 Hpre as AhbPrescaler, Hsidiv as HsiPrescaler, Hsitrim as HsiCalibration, Ppre as ApbPrescaler,
@@ -44,7 +45,7 @@ pub enum SupplyConfig {
44#[derive(Clone, Copy, PartialEq)] 45#[derive(Clone, Copy, PartialEq)]
45pub enum CpuClk { 46pub enum CpuClk {
46 Hse, 47 Hse,
47 Pll { source: Icsel, divider: Icint }, 48 Ic1 { source: Icsel, divider: Icint },
48 Msi, 49 Msi,
49 Hsi, 50 Hsi,
50} 51}
@@ -55,7 +56,7 @@ impl CpuClk {
55 Self::Hsi => 0x0, 56 Self::Hsi => 0x0,
56 Self::Msi => 0x1, 57 Self::Msi => 0x1,
57 Self::Hse => 0x2, 58 Self::Hse => 0x2,
58 Self::Pll { .. } => 0x3, 59 Self::Ic1 { .. } => 0x3,
59 } 60 }
60 } 61 }
61} 62}
@@ -69,7 +70,7 @@ pub struct IcConfig {
69#[derive(Clone, Copy, PartialEq)] 70#[derive(Clone, Copy, PartialEq)]
70pub enum SysClk { 71pub enum SysClk {
71 Hse, 72 Hse,
72 Pll { 73 Ic2 {
73 ic2: IcConfig, 74 ic2: IcConfig,
74 ic6: IcConfig, 75 ic6: IcConfig,
75 ic11: IcConfig, 76 ic11: IcConfig,
@@ -84,7 +85,7 @@ impl SysClk {
84 Self::Hsi => 0x0, 85 Self::Hsi => 0x0,
85 Self::Msi => 0x1, 86 Self::Msi => 0x1,
86 Self::Hse => 0x2, 87 Self::Hse => 0x2,
87 Self::Pll { .. } => 0x3, 88 Self::Ic2 { .. } => 0x3,
88 } 89 }
89 } 90 }
90} 91}
@@ -150,12 +151,12 @@ impl Config {
150 lse: false, 151 lse: false,
151 sys: SysClk::Hsi, 152 sys: SysClk::Hsi,
152 cpu: CpuClk::Hsi, 153 cpu: CpuClk::Hsi,
153 pll1: None, 154 pll1: Some(Pll::Bypass { source: Pllsel::HSI }),
154 pll2: None, 155 pll2: Some(Pll::Bypass { source: Pllsel::HSI }),
155 pll3: None, 156 pll3: Some(Pll::Bypass { source: Pllsel::HSI }),
156 pll4: None, 157 pll4: Some(Pll::Bypass { source: Pllsel::HSI }),
157 158
158 ahb: AhbPrescaler::DIV1, 159 ahb: AhbPrescaler::DIV2,
159 apb1: ApbPrescaler::DIV1, 160 apb1: ApbPrescaler::DIV1,
160 apb2: ApbPrescaler::DIV1, 161 apb2: ApbPrescaler::DIV1,
161 apb4: ApbPrescaler::DIV1, 162 apb4: ApbPrescaler::DIV1,
@@ -166,7 +167,24 @@ impl Config {
166 } 167 }
167} 168}
168 169
169fn init_clocks(config: Config) { 170struct ClocksOutput {
171 cpuclk: Hertz,
172 sysclk: Hertz,
173 pclk_tim: Hertz,
174 ahb: Hertz,
175 apb1: Hertz,
176 apb2: Hertz,
177 apb4: Hertz,
178 apb5: Hertz,
179}
180
181struct ClocksInput {
182 hsi: Option<Hertz>,
183 msi: Option<Hertz>,
184 hse: Option<Hertz>,
185}
186
187fn init_clocks(config: Config, input: &ClocksInput) -> ClocksOutput {
170 // handle increasing dividers 188 // handle increasing dividers
171 debug!("configuring increasing pclk dividers"); 189 debug!("configuring increasing pclk dividers");
172 RCC.cfgr2().modify(|w| { 190 RCC.cfgr2().modify(|w| {
@@ -195,7 +213,7 @@ fn init_clocks(config: Config) {
195 debug!("configuring cpuclk"); 213 debug!("configuring cpuclk");
196 match config.cpu { 214 match config.cpu {
197 CpuClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), 215 CpuClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"),
198 CpuClk::Pll { source, divider } => { 216 CpuClk::Ic1 { source, divider } => {
199 if !pll_sources_ready(RCC.iccfgr(0).read().icsel().to_bits(), source.to_bits()) { 217 if !pll_sources_ready(RCC.iccfgr(0).read().icsel().to_bits(), source.to_bits()) {
200 panic!("ICx clock switch requires both origin and destination clock source to be active") 218 panic!("ICx clock switch requires both origin and destination clock source to be active")
201 } 219 }
@@ -220,7 +238,7 @@ fn init_clocks(config: Config) {
220 debug!("configuring sysclk"); 238 debug!("configuring sysclk");
221 match config.sys { 239 match config.sys {
222 SysClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"), 240 SysClk::Hse if !RCC.sr().read().hserdy() => panic!("HSE is not ready to be selected as CPU clock source"),
223 SysClk::Pll { ic2, ic6, ic11 } => { 241 SysClk::Ic2 { ic2, ic6, ic11 } => {
224 if !pll_sources_ready(RCC.iccfgr(1).read().icsel().to_bits(), ic2.source.to_bits()) { 242 if !pll_sources_ready(RCC.iccfgr(1).read().icsel().to_bits(), ic2.source.to_bits()) {
225 panic!("IC2 clock switch requires both origin and destination clock source to be active") 243 panic!("IC2 clock switch requires both origin and destination clock source to be active")
226 } 244 }
@@ -283,6 +301,57 @@ fn init_clocks(config: Config) {
283 w.set_ppre5(config.apb5); 301 w.set_ppre5(config.apb5);
284 } 302 }
285 }); 303 });
304
305 let cpuclk = match config.cpu {
306 CpuClk::Hsi => unwrap!(input.hsi),
307 CpuClk::Msi => unwrap!(input.msi),
308 CpuClk::Hse => unwrap!(input.hse),
309 CpuClk::Ic1 { .. } => todo!(),
310 };
311
312 let sysclk = match config.sys {
313 SysClk::Hsi => unwrap!(input.hsi),
314 SysClk::Msi => unwrap!(input.msi),
315 SysClk::Hse => unwrap!(input.hse),
316 SysClk::Ic2 { .. } => todo!(),
317 };
318
319 let timpre: u32 = match RCC.cfgr2().read().timpre() {
320 Timpre::DIV1 => 1,
321 Timpre::DIV2 => 2,
322 Timpre::DIV4 => 4,
323 Timpre::_RESERVED_3 => 8,
324 };
325
326 let hpre = periph_prescaler_to_value(config.ahb.to_bits());
327 let ppre1 = periph_prescaler_to_value(config.apb1.to_bits());
328 let ppre2 = periph_prescaler_to_value(config.apb2.to_bits());
329 let ppre4 = periph_prescaler_to_value(config.apb4.to_bits());
330 let ppre5 = periph_prescaler_to_value(config.apb5.to_bits());
331
332 ClocksOutput {
333 sysclk,
334 cpuclk,
335 pclk_tim: sysclk / timpre,
336 ahb: Hertz(sysclk.0 / hpre as u32),
337 apb1: sysclk / hpre / ppre1,
338 apb2: sysclk / hpre / ppre2,
339 apb4: sysclk / hpre / ppre4,
340 apb5: sysclk / hpre / ppre5,
341 }
342}
343
344const fn periph_prescaler_to_value(bits: u8) -> u8 {
345 match bits {
346 0 => 1,
347 1 => 2,
348 2 => 4,
349 3 => 8,
350 4 => 16,
351 5 => 32,
352 6 => 64,
353 7.. => 128,
354 }
286} 355}
287 356
288fn pll_source_ready(source: u8) -> bool { 357fn pll_source_ready(source: u8) -> bool {
@@ -318,7 +387,23 @@ fn power_supply_config(supply_config: SupplyConfig) {
318 while !PWR.voscr().read().actvosrdy() {} 387 while !PWR.voscr().read().actvosrdy() {}
319} 388}
320 389
321fn init_pll(pll_config: Option<Pll>, pll_index: usize) { 390struct PllInput {
391 hsi: Option<Hertz>,
392 msi: Option<Hertz>,
393 hse: Option<Hertz>,
394 i2s_ckin: Option<Hertz>,
395}
396
397#[derive(Clone, Copy, Default)]
398struct PllOutput {
399 divm: Option<Hertz>,
400 divn: Option<Hertz>,
401 divp1: Option<Hertz>,
402 divp2: Option<Hertz>,
403 output: Option<Hertz>,
404}
405
406fn init_pll(pll_config: Option<Pll>, pll_index: usize, input: &PllInput) -> PllOutput {
322 let cfgr1 = RCC.pllcfgr1(pll_index); 407 let cfgr1 = RCC.pllcfgr1(pll_index);
323 let cfgr2 = RCC.pllcfgr2(pll_index); 408 let cfgr2 = RCC.pllcfgr2(pll_index);
324 let cfgr3 = RCC.pllcfgr3(pll_index); 409 let cfgr3 = RCC.pllcfgr3(pll_index);
@@ -336,7 +421,7 @@ fn init_pll(pll_config: Option<Pll>, pll_index: usize) {
336 RCC.ccr().write(|w| w.set_pllonc(pll_index, true)); 421 RCC.ccr().write(|w| w.set_pllonc(pll_index, true));
337 while RCC.sr().read().pllrdy(pll_index) {} 422 while RCC.sr().read().pllrdy(pll_index) {}
338 423
339 // ensure PLLxMODSSDIS=1 424 // ensure PLLxMODSSDIS=1 to work in fractional mode
340 cfgr3.modify(|w| w.set_pllmodssdis(Pllmodssdis::FRACTIONAL_DIVIDE)); 425 cfgr3.modify(|w| w.set_pllmodssdis(Pllmodssdis::FRACTIONAL_DIVIDE));
341 // clear bypass mode 426 // clear bypass mode
342 cfgr1.modify(|w| w.set_pllbyp(false)); 427 cfgr1.modify(|w| w.set_pllbyp(false));
@@ -346,10 +431,26 @@ fn init_pll(pll_config: Option<Pll>, pll_index: usize) {
346 w.set_plldivm(divm); 431 w.set_plldivm(divm);
347 w.set_plldivn(divn); 432 w.set_plldivn(divn);
348 }); 433 });
434
435 let in_clk = match source {
436 Pllsel::HSI => unwrap!(input.hsi),
437 Pllsel::MSI => unwrap!(input.msi),
438 Pllsel::HSE => unwrap!(input.hse),
439 Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin),
440 _ => panic!("reserved PLL source not allowed"),
441 };
442
443 let m = divm.to_bits() as u32;
444 let n = divn as u32;
445
349 cfgr3.modify(|w| { 446 cfgr3.modify(|w| {
350 w.set_pllpdiv1(divp1); 447 w.set_pllpdiv1(divp1);
351 w.set_pllpdiv2(divp2); 448 w.set_pllpdiv2(divp2);
352 }); 449 });
450
451 let p1 = divp1.to_bits() as u32;
452 let p2 = divp2.to_bits() as u32;
453
353 // configure pll divnfrac 454 // configure pll divnfrac
354 cfgr2.modify(|w| w.set_plldivnfrac(fractional)); 455 cfgr2.modify(|w| w.set_plldivnfrac(fractional));
355 // clear pllxmoddsen 456 // clear pllxmoddsen
@@ -370,6 +471,14 @@ fn init_pll(pll_config: Option<Pll>, pll_index: usize) {
370 RCC.csr().write(|w| w.pllons(pll_index)); 471 RCC.csr().write(|w| w.pllons(pll_index));
371 // wait until ready 472 // wait until ready
372 while RCC.sr().read().pllrdy(pll_index) {} 473 while RCC.sr().read().pllrdy(pll_index) {}
474
475 PllOutput {
476 divm: Some(Hertz(m)),
477 divn: Some(Hertz(n)),
478 divp1: Some(Hertz(p1)),
479 divp2: Some(Hertz(p2)),
480 output: Some(Hertz(in_clk.0 / m / n / p1 / p2)),
481 }
373 } 482 }
374 Some(Pll::Bypass { source }) => { 483 Some(Pll::Bypass { source }) => {
375 // check if source is ready 484 // check if source is ready
@@ -384,7 +493,20 @@ fn init_pll(pll_config: Option<Pll>, pll_index: usize) {
384 cfgr1.modify(|w| { 493 cfgr1.modify(|w| {
385 w.set_pllbyp(true); 494 w.set_pllbyp(true);
386 w.set_pllsel(source); 495 w.set_pllsel(source);
387 }) 496 });
497
498 let in_clk = match source {
499 Pllsel::HSI => unwrap!(input.hsi),
500 Pllsel::MSI => unwrap!(input.msi),
501 Pllsel::HSE => unwrap!(input.hse),
502 Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin),
503 _ => panic!("reserved PLL source not allowed"),
504 };
505
506 PllOutput {
507 output: Some(in_clk),
508 ..Default::default()
509 }
388 } 510 }
389 None => { 511 None => {
390 cfgr3.modify(|w| w.set_pllpdiven(false)); 512 cfgr3.modify(|w| w.set_pllpdiven(false));
@@ -394,11 +516,29 @@ fn init_pll(pll_config: Option<Pll>, pll_index: usize) {
394 516
395 // clear bypass mode 517 // clear bypass mode
396 cfgr1.modify(|w| w.set_pllbyp(false)); 518 cfgr1.modify(|w| w.set_pllbyp(false));
519
520 PllOutput::default()
397 } 521 }
398 } 522 }
399} 523}
400 524
401fn init_osc(config: Config) { 525struct OscOutput {
526 hsi: Option<Hertz>,
527 hse: Option<Hertz>,
528 msi: Option<Hertz>,
529 lsi: Option<Hertz>,
530 lse: Option<Hertz>,
531 pll1: Option<Hertz>,
532 pll2: Option<Hertz>,
533 pll3: Option<Hertz>,
534 pll4: Option<Hertz>,
535 ic1sel: Icsel,
536 ic2sel: Icsel,
537 ic6sel: Icsel,
538 ic11sel: Icsel,
539}
540
541fn init_osc(config: Config) -> OscOutput {
402 let (cpu_src, sys_src) = { 542 let (cpu_src, sys_src) = {
403 let reg = RCC.cfgr().read(); 543 let reg = RCC.cfgr().read();
404 (reg.cpusws(), reg.syssws()) 544 (reg.cpusws(), reg.syssws())
@@ -542,18 +682,27 @@ fn init_osc(config: Config) {
542 None 682 None
543 }; 683 };
544 684
685 let pll_input = PllInput {
686 hse,
687 msi,
688 hsi,
689 i2s_ckin: None,
690 };
691
545 // pll1,2,3,4 config 692 // pll1,2,3,4 config
546 let pll_configs = [config.pll1, config.pll2, config.pll3, config.pll4]; 693 let pll_configs = [config.pll1, config.pll2, config.pll3, config.pll4];
547 for (n, &pll) in pll_configs.iter().enumerate() { 694 let mut pll_outputs: [PllOutput; 4] = [PllOutput::default(); 4];
695
696 let ic1_src = RCC.iccfgr(0).read().icsel();
697 let ic2_src = RCC.iccfgr(1).read().icsel();
698 let ic6_src = RCC.iccfgr(5).read().icsel();
699 let ic11_src = RCC.iccfgr(10).read().icsel();
700
701 for (n, (&pll, out)) in pll_configs.iter().zip(pll_outputs.iter_mut()).enumerate() {
548 debug!("configuring PLL{}", n + 1); 702 debug!("configuring PLL{}", n + 1);
549 let pll_ready = RCC.sr().read().pllrdy(n); 703 let pll_ready = RCC.sr().read().pllrdy(n);
550 704
551 if is_new_pll_config(pll, 0) { 705 if is_new_pll_config(pll, 0) {
552 let ic1_src = RCC.iccfgr(0).read().icsel();
553 let ic2_src = RCC.iccfgr(1).read().icsel();
554 let ic6_src = RCC.iccfgr(5).read().icsel();
555 let ic11_src = RCC.iccfgr(10).read().icsel();
556
557 let this_pll = Icsel::from_bits(n as u8); 706 let this_pll = Icsel::from_bits(n as u8);
558 707
559 if cpu_src == Cpusws::IC1 && ic1_src == this_pll { 708 if cpu_src == Cpusws::IC1 && ic1_src == this_pll {
@@ -564,13 +713,28 @@ fn init_osc(config: Config) {
564 panic!("PLL should not be disabled / reconfigured if used for IC2, IC6 or IC11 (sysclksrc)") 713 panic!("PLL should not be disabled / reconfigured if used for IC2, IC6 or IC11 (sysclksrc)")
565 } 714 }
566 715
567 init_pll(pll, 0); 716 *out = init_pll(pll, 0, &pll_input);
568 } else if pll.is_some() && !pll_ready { 717 } else if pll.is_some() && !pll_ready {
569 debug!("PLL{} off", n + 1);
570 RCC.csr().write(|w| w.pllons(n)); 718 RCC.csr().write(|w| w.pllons(n));
571 while !RCC.sr().read().pllrdy(n) {} 719 while !RCC.sr().read().pllrdy(n) {}
572 } 720 }
573 } 721 }
722
723 OscOutput {
724 hsi,
725 hse,
726 msi,
727 lsi,
728 lse,
729 pll1: pll_outputs[0].output,
730 pll2: pll_outputs[1].output,
731 pll3: pll_outputs[2].output,
732 pll4: pll_outputs[3].output,
733 ic1sel: ic1_src,
734 ic2sel: ic2_src,
735 ic6sel: ic6_src,
736 ic11sel: ic11_src,
737 }
574} 738}
575 739
576fn is_new_pll_config(pll: Option<Pll>, pll_index: usize) -> bool { 740fn is_new_pll_config(pll: Option<Pll>, pll_index: usize) -> bool {
@@ -618,7 +782,7 @@ pub(crate) unsafe fn init(config: Config) {
618 // system configuration setup 782 // system configuration setup
619 RCC.apb4hensr().write(|w| w.set_syscfgens(true)); 783 RCC.apb4hensr().write(|w| w.set_syscfgens(true));
620 // delay after RCC peripheral clock enabling 784 // delay after RCC peripheral clock enabling
621 core::ptr::read_volatile(RCC.apb4hensr().as_ptr()); 785 RCC.apb4hensr().read();
622 786
623 debug!("setting VTOR"); 787 debug!("setting VTOR");
624 788
@@ -630,7 +794,7 @@ pub(crate) unsafe fn init(config: Config) {
630 // set default vector table location after reset or standby 794 // set default vector table location after reset or standby
631 SYSCFG.initsvtorcr().write(|w| w.set_svtor_addr(vtor)); 795 SYSCFG.initsvtorcr().write(|w| w.set_svtor_addr(vtor));
632 // read back the value to ensure it is written before deactivating SYSCFG 796 // read back the value to ensure it is written before deactivating SYSCFG
633 core::ptr::read_volatile(SYSCFG.initsvtorcr().as_ptr()); 797 SYSCFG.initsvtorcr().read();
634 798
635 debug!("deactivating SYSCFG"); 799 debug!("deactivating SYSCFG");
636 800
@@ -649,36 +813,35 @@ pub(crate) unsafe fn init(config: Config) {
649 813
650 power_supply_config(config.supply_config); 814 power_supply_config(config.supply_config);
651 815
652 init_osc(config); 816 let osc = init_osc(config);
653 init_clocks(config); 817 let clock_inputs = ClocksInput {
654 818 hsi: osc.hsi,
655 let sys = match config.sys { 819 msi: osc.msi,
656 SysClk::Hse => todo!(), 820 hse: osc.hse,
657 SysClk::Hsi => Hertz(64_000_000),
658 SysClk::Msi => todo!(),
659 SysClk::Pll { .. } => todo!(),
660 }; 821 };
822 let clocks = init_clocks(config, &clock_inputs);
661 823
662 // TODO: sysb, sysc, sysd must have the same clock source 824 // TODO: sysb, sysc, sysd must have the same clock source
663 825
664 set_clocks!( 826 set_clocks!(
665 sys: Some(sys), 827 sys: Some(clocks.sysclk),
666 hsi: None, 828 hsi: osc.hsi,
667 hsi_div: None, 829 hsi_div: None,
668 hse: None, 830 hse: osc.hse,
669 hclk1: None, 831 msi: osc.msi,
670 hclk2: None, 832 hclk1: Some(clocks.ahb),
671 hclk3: None, 833 hclk2: Some(clocks.ahb),
672 hclk4: None, 834 hclk3: Some(clocks.ahb),
673 hclk5: None, 835 hclk4: Some(clocks.ahb),
674 pclk1: None, 836 hclk5: Some(clocks.ahb),
675 pclk2: None, 837 pclk1: Some(clocks.apb1),
676 pclk2_tim: Some(Hertz(1_000_000)), // FIXME: what is this?? 838 pclk2: Some(clocks.apb2),
677 pclk4: None, 839 pclk1_tim: Some(clocks.pclk_tim),
678 pclk5: None, 840 pclk2_tim: Some(clocks.pclk_tim),
841 pclk4: Some(clocks.apb4),
842 pclk5: Some(clocks.apb5),
679 per: None, 843 per: None,
680 rtc: None, 844 rtc: None,
681 msi: None,
682 i2s_ckin: None, 845 i2s_ckin: None,
683 ic8: None, 846 ic8: None,
684 ic9: None, 847 ic9: None,