stm32/rcc: port U5 to new API, add all PLLs, all HSE modes.
This commit is contained in:
		
							parent
							
								
									4481c5f3cc
								
							
						
					
					
						commit
						0665e0d452
					
				| @ -1,134 +1,83 @@ | ||||
| pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange, Plldiv, Pllm, Plln, Ppre as APBPrescaler}; | ||||
| use crate::pac::rcc::vals::{Msirgsel, Pllmboost, Pllrge, Pllsrc, Sw}; | ||||
| pub use crate::pac::pwr::vals::Vos as VoltageScale; | ||||
| pub use crate::pac::rcc::vals::{ | ||||
|     Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, | ||||
|     Pllsrc as PllSource, Ppre as APBPrescaler, Sw as ClockSrc, | ||||
| }; | ||||
| use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; | ||||
| use crate::pac::{FLASH, PWR, RCC}; | ||||
| use crate::time::Hertz; | ||||
| 
 | ||||
| /// HSI speed
 | ||||
| pub const HSI_FREQ: Hertz = Hertz(16_000_000); | ||||
| 
 | ||||
| pub use crate::pac::pwr::vals::Vos as VoltageScale; | ||||
| 
 | ||||
| #[derive(Copy, Clone)] | ||||
| #[allow(non_camel_case_types)] | ||||
| pub enum ClockSrc { | ||||
|     /// Use an internal medium speed oscillator (MSIS) as the system clock.
 | ||||
|     MSI(Msirange), | ||||
|     /// Use the external high speed clock as the system clock.
 | ||||
|     ///
 | ||||
|     /// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
 | ||||
|     /// never exceed 50 MHz.
 | ||||
|     HSE(Hertz), | ||||
|     /// Use the 16 MHz internal high speed oscillator as the system clock.
 | ||||
|     HSI, | ||||
|     /// Use PLL1 as the system clock.
 | ||||
|     PLL1_R(PllConfig), | ||||
| #[derive(Clone, Copy, Eq, PartialEq)] | ||||
| pub enum HseMode { | ||||
|     /// crystal/ceramic oscillator (HSEBYP=0)
 | ||||
|     Oscillator, | ||||
|     /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
 | ||||
|     Bypass, | ||||
|     /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
 | ||||
|     BypassDigital, | ||||
| } | ||||
| 
 | ||||
| impl Default for ClockSrc { | ||||
|     fn default() -> Self { | ||||
|         // The default system clock source is MSIS @ 4 MHz, per RM0456 § 11.4.9
 | ||||
|         ClockSrc::MSI(Msirange::RANGE_4MHZ) | ||||
|     } | ||||
| #[derive(Clone, Copy, Eq, PartialEq)] | ||||
| pub struct Hse { | ||||
|     /// HSE frequency.
 | ||||
|     pub freq: Hertz, | ||||
|     /// HSE mode.
 | ||||
|     pub mode: HseMode, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Copy)] | ||||
| pub struct PllConfig { | ||||
| pub struct Pll { | ||||
|     /// The clock source for the PLL.
 | ||||
|     pub source: PllSource, | ||||
|     /// The PLL prescaler.
 | ||||
|     /// The PLL pre-divider.
 | ||||
|     ///
 | ||||
|     /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
 | ||||
|     pub m: Pllm, | ||||
|     pub prediv: PllPreDiv, | ||||
|     /// The PLL multiplier.
 | ||||
|     ///
 | ||||
|     /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544
 | ||||
|     /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
 | ||||
|     pub n: Plln, | ||||
|     pub mul: PllMul, | ||||
|     /// The divider for the P output.
 | ||||
|     ///
 | ||||
|     /// The P output is one of several options
 | ||||
|     /// that can be used to feed the SAI/MDF/ADF Clock mux's.
 | ||||
|     pub p: Plldiv, | ||||
|     pub divp: Option<PllDiv>, | ||||
|     /// The divider for the Q output.
 | ||||
|     ///
 | ||||
|     /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks
 | ||||
|     /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's.
 | ||||
|     pub q: Plldiv, | ||||
|     pub divq: Option<PllDiv>, | ||||
|     /// The divider for the R output.
 | ||||
|     ///
 | ||||
|     /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
 | ||||
|     /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
 | ||||
|     /// `Config { voltage_range }`.
 | ||||
|     pub r: Plldiv, | ||||
| } | ||||
| 
 | ||||
| impl PllConfig { | ||||
|     /// A configuration for HSI / 1 * 10 / 1 = 160 MHz
 | ||||
|     pub const fn hsi_160mhz() -> Self { | ||||
|         PllConfig { | ||||
|             source: PllSource::HSI, | ||||
|             m: Pllm::DIV1, | ||||
|             n: Plln::MUL10, | ||||
|             p: Plldiv::DIV3, | ||||
|             q: Plldiv::DIV2, | ||||
|             r: Plldiv::DIV1, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// A configuration for MSIS @ 48 MHz / 3 * 10 / 1 = 160 MHz
 | ||||
|     pub const fn msis_160mhz() -> Self { | ||||
|         PllConfig { | ||||
|             source: PllSource::MSIS(Msirange::RANGE_48MHZ), | ||||
|             m: Pllm::DIV3, | ||||
|             n: Plln::MUL10, | ||||
|             p: Plldiv::DIV3, | ||||
|             q: Plldiv::DIV2, | ||||
|             r: Plldiv::DIV1, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum PllSource { | ||||
|     /// Use an internal medium speed oscillator as the PLL source.
 | ||||
|     MSIS(Msirange), | ||||
|     /// Use the external high speed clock as the system PLL source.
 | ||||
|     ///
 | ||||
|     /// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
 | ||||
|     /// never exceed 50 MHz.
 | ||||
|     HSE(Hertz), | ||||
|     /// Use the 16 MHz internal high speed oscillator as the PLL source.
 | ||||
|     HSI, | ||||
| } | ||||
| 
 | ||||
| impl Into<Pllsrc> for PllSource { | ||||
|     fn into(self) -> Pllsrc { | ||||
|         match self { | ||||
|             PllSource::MSIS(..) => Pllsrc::MSIS, | ||||
|             PllSource::HSE(..) => Pllsrc::HSE, | ||||
|             PllSource::HSI => Pllsrc::HSI, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<Sw> for ClockSrc { | ||||
|     fn into(self) -> Sw { | ||||
|         match self { | ||||
|             ClockSrc::MSI(..) => Sw::MSIS, | ||||
|             ClockSrc::HSE(..) => Sw::HSE, | ||||
|             ClockSrc::HSI => Sw::HSI, | ||||
|             ClockSrc::PLL1_R(..) => Sw::PLL1_R, | ||||
|         } | ||||
|     } | ||||
|     pub divr: Option<PllDiv>, | ||||
| } | ||||
| 
 | ||||
| pub struct Config { | ||||
|     // base clock sources
 | ||||
|     pub msi: Option<MSIRange>, | ||||
|     pub hsi: bool, | ||||
|     pub hse: Option<Hse>, | ||||
|     pub hsi48: Option<super::Hsi48Config>, | ||||
| 
 | ||||
|     // pll
 | ||||
|     pub pll1: Option<Pll>, | ||||
|     pub pll2: Option<Pll>, | ||||
|     pub pll3: Option<Pll>, | ||||
| 
 | ||||
|     // sysclk, buses.
 | ||||
|     pub mux: ClockSrc, | ||||
|     pub ahb_pre: AHBPrescaler, | ||||
|     pub apb1_pre: APBPrescaler, | ||||
|     pub apb2_pre: APBPrescaler, | ||||
|     pub apb3_pre: APBPrescaler, | ||||
|     pub hsi48: Option<super::Hsi48Config>, | ||||
| 
 | ||||
|     /// The voltage range influences the maximum clock frequencies for different parts of the
 | ||||
|     /// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
 | ||||
|     /// exceeding 55 MHz require at least `RANGE2`.
 | ||||
| @ -138,35 +87,35 @@ pub struct Config { | ||||
|     pub ls: super::LsConfig, | ||||
| } | ||||
| 
 | ||||
| impl Config { | ||||
|     unsafe fn init_hsi(&self) -> Hertz { | ||||
|         RCC.cr().write(|w| w.set_hsion(true)); | ||||
|         while !RCC.cr().read().hsirdy() {} | ||||
| 
 | ||||
|         HSI_FREQ | ||||
|     } | ||||
| 
 | ||||
|     unsafe fn init_hse(&self, frequency: Hertz) -> Hertz { | ||||
|         // Check frequency limits per RM456 § 11.4.10
 | ||||
|         match self.voltage_range { | ||||
|             VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => { | ||||
|                 assert!(frequency.0 <= 50_000_000); | ||||
|             } | ||||
|             VoltageScale::RANGE4 => { | ||||
|                 assert!(frequency.0 <= 25_000_000); | ||||
|             } | ||||
| impl Default for Config { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             msi: Some(Msirange::RANGE_4MHZ), | ||||
|             hse: None, | ||||
|             hsi: false, | ||||
|             hsi48: Some(Default::default()), | ||||
|             pll1: None, | ||||
|             pll2: None, | ||||
|             pll3: None, | ||||
|             mux: ClockSrc::MSIS, | ||||
|             ahb_pre: AHBPrescaler::DIV1, | ||||
|             apb1_pre: APBPrescaler::DIV1, | ||||
|             apb2_pre: APBPrescaler::DIV1, | ||||
|             apb3_pre: APBPrescaler::DIV1, | ||||
|             voltage_range: VoltageScale::RANGE1, | ||||
|             ls: Default::default(), | ||||
|         } | ||||
| 
 | ||||
|         // Enable HSE, and wait for it to stabilize
 | ||||
|         RCC.cr().write(|w| w.set_hseon(true)); | ||||
|         while !RCC.cr().read().hserdy() {} | ||||
| 
 | ||||
|         frequency | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     unsafe fn init_msis(&self, range: Msirange) -> Hertz { | ||||
| pub(crate) unsafe fn init(config: Config) { | ||||
|     // Set the requested power mode
 | ||||
|     PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); | ||||
|     while !PWR.vosr().read().vosrdy() {} | ||||
| 
 | ||||
|     let msi = config.msi.map(|range| { | ||||
|         // Check MSI output per RM0456 § 11.4.10
 | ||||
|         match self.voltage_range { | ||||
|         match config.voltage_range { | ||||
|             VoltageScale::RANGE4 => { | ||||
|                 assert!(msirange_to_hertz(range).0 <= 24_000_000); | ||||
|             } | ||||
| @ -191,223 +140,98 @@ impl Config { | ||||
|         }); | ||||
|         while !RCC.cr().read().msisrdy() {} | ||||
|         msirange_to_hertz(range) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Default for Config { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             mux: ClockSrc::default(), | ||||
|             ahb_pre: AHBPrescaler::DIV1, | ||||
|             apb1_pre: APBPrescaler::DIV1, | ||||
|             apb2_pre: APBPrescaler::DIV1, | ||||
|             apb3_pre: APBPrescaler::DIV1, | ||||
|             hsi48: Some(Default::default()), | ||||
|             voltage_range: VoltageScale::RANGE3, | ||||
|             ls: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(crate) unsafe fn init(config: Config) { | ||||
|     // Ensure PWR peripheral clock is enabled
 | ||||
|     RCC.ahb3enr().modify(|w| { | ||||
|         w.set_pwren(true); | ||||
|     }); | ||||
|     RCC.ahb3enr().read(); // synchronize
 | ||||
| 
 | ||||
|     // Set the requested power mode
 | ||||
|     PWR.vosr().modify(|w| { | ||||
|         w.set_vos(config.voltage_range); | ||||
|     let hsi = config.hsi.then(|| { | ||||
|         RCC.cr().write(|w| w.set_hsion(true)); | ||||
|         while !RCC.cr().read().hsirdy() {} | ||||
| 
 | ||||
|         HSI_FREQ | ||||
|     }); | ||||
|     while !PWR.vosr().read().vosrdy() {} | ||||
| 
 | ||||
|     let sys_clk = match config.mux { | ||||
|         ClockSrc::MSI(range) => config.init_msis(range), | ||||
|         ClockSrc::HSE(freq) => config.init_hse(freq), | ||||
|         ClockSrc::HSI => config.init_hsi(), | ||||
|         ClockSrc::PLL1_R(pll) => { | ||||
|             // Configure the PLL source
 | ||||
|             let source_clk = match pll.source { | ||||
|                 PllSource::MSIS(range) => config.init_msis(range), | ||||
|                 PllSource::HSE(hertz) => config.init_hse(hertz), | ||||
|                 PllSource::HSI => config.init_hsi(), | ||||
|             }; | ||||
| 
 | ||||
|             // Calculate the reference clock, which is the source divided by m
 | ||||
|             let reference_clk = source_clk / pll.m; | ||||
| 
 | ||||
|             // Check limits per RM0456 § 11.4.6
 | ||||
|             assert!(Hertz::mhz(4) <= reference_clk && reference_clk <= Hertz::mhz(16)); | ||||
| 
 | ||||
|             // Calculate the PLL1 VCO clock and PLL1 R output clock
 | ||||
|             let pll1_clk = reference_clk * pll.n; | ||||
|             let pll1r_clk = pll1_clk / pll.r; | ||||
| 
 | ||||
|             // Check system clock per RM0456 § 11.4.9
 | ||||
|             assert!(pll1r_clk <= Hertz::mhz(160)); | ||||
| 
 | ||||
|             // Check PLL clocks per RM0456 § 11.4.10
 | ||||
|             match config.voltage_range { | ||||
|                 VoltageScale::RANGE1 => { | ||||
|                     assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544)); | ||||
|                     assert!(pll1r_clk <= Hertz::mhz(208)); | ||||
|                 } | ||||
|                 VoltageScale::RANGE2 => { | ||||
|                     assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544)); | ||||
|                     assert!(pll1r_clk <= Hertz::mhz(110)); | ||||
|                 } | ||||
|                 VoltageScale::RANGE3 => { | ||||
|                     assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(330)); | ||||
|                     assert!(pll1r_clk <= Hertz::mhz(55)); | ||||
|                 } | ||||
|                 VoltageScale::RANGE4 => { | ||||
|                     panic!("PLL is unavailable in voltage range 4"); | ||||
|                 } | ||||
|     let hse = config.hse.map(|hse| { | ||||
|         // Check frequency limits per RM456 § 11.4.10
 | ||||
|         match config.voltage_range { | ||||
|             VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => { | ||||
|                 assert!(hse.freq.0 <= 50_000_000); | ||||
|             } | ||||
| 
 | ||||
|             // § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
 | ||||
|             // value that results in an output between 4 and 16 MHz for the PWR EPOD boost
 | ||||
|             let mboost = if pll1r_clk >= Hertz::mhz(55) { | ||||
|                 // source_clk can be up to 50 MHz, so there's just a few cases:
 | ||||
|                 if source_clk > Hertz::mhz(32) { | ||||
|                     // Divide by 4, giving EPOD 8-12.5 MHz
 | ||||
|                     Pllmboost::DIV4 | ||||
|                 } else if source_clk > Hertz::mhz(16) { | ||||
|                     // Divide by 2, giving EPOD 8-16 MHz
 | ||||
|                     Pllmboost::DIV2 | ||||
|                 } else { | ||||
|                     // Bypass, giving EPOD 4-16 MHz
 | ||||
|                     Pllmboost::DIV1 | ||||
|                 } | ||||
|             } else { | ||||
|                 // Nothing to do
 | ||||
|                 Pllmboost::DIV1 | ||||
|             }; | ||||
| 
 | ||||
|             // Disable the PLL, and wait for it to disable
 | ||||
|             RCC.cr().modify(|w| w.set_pllon(0, false)); | ||||
|             while RCC.cr().read().pllrdy(0) {} | ||||
| 
 | ||||
|             // Configure the PLL
 | ||||
|             RCC.pll1cfgr().write(|w| { | ||||
|                 // Configure PLL1 source and prescaler
 | ||||
|                 w.set_pllsrc(pll.source.into()); | ||||
|                 w.set_pllm(pll.m); | ||||
| 
 | ||||
|                 // Configure PLL1 input frequncy range
 | ||||
|                 let input_range = if reference_clk <= Hertz::mhz(8) { | ||||
|                     Pllrge::FREQ_4TO8MHZ | ||||
|                 } else { | ||||
|                     Pllrge::FREQ_8TO16MHZ | ||||
|                 }; | ||||
|                 w.set_pllrge(input_range); | ||||
| 
 | ||||
|                 // Set the prescaler for PWR EPOD
 | ||||
|                 w.set_pllmboost(mboost); | ||||
| 
 | ||||
|                 // Enable PLL1_R output
 | ||||
|                 w.set_pllren(true); | ||||
|             }); | ||||
| 
 | ||||
|             // Configure the PLL divisors
 | ||||
|             RCC.pll1divr().modify(|w| { | ||||
|                 // Set the VCO multiplier
 | ||||
|                 w.set_plln(pll.n); | ||||
|                 w.set_pllp(pll.p); | ||||
|                 w.set_pllq(pll.q); | ||||
|                 // Set the R output divisor
 | ||||
|                 w.set_pllr(pll.r); | ||||
|             }); | ||||
| 
 | ||||
|             // Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
 | ||||
|             if pll1r_clk >= Hertz::mhz(55) { | ||||
|                 // Enable the booster
 | ||||
|                 PWR.vosr().modify(|w| { | ||||
|                     w.set_boosten(true); | ||||
|                 }); | ||||
|                 while !PWR.vosr().read().boostrdy() {} | ||||
|             VoltageScale::RANGE4 => { | ||||
|                 assert!(hse.freq.0 <= 25_000_000); | ||||
|             } | ||||
| 
 | ||||
|             // Enable the PLL
 | ||||
|             RCC.cr().modify(|w| w.set_pllon(0, true)); | ||||
|             while !RCC.cr().read().pllrdy(0) {} | ||||
| 
 | ||||
|             pll1r_clk | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|         // Enable HSE, and wait for it to stabilize
 | ||||
|         RCC.cr().write(|w| { | ||||
|             w.set_hseon(true); | ||||
|             w.set_hsebyp(hse.mode != HseMode::Oscillator); | ||||
|             w.set_hseext(match hse.mode { | ||||
|                 HseMode::Oscillator | HseMode::Bypass => Hseext::ANALOG, | ||||
|                 HseMode::BypassDigital => Hseext::DIGITAL, | ||||
|             }); | ||||
|         }); | ||||
|         while !RCC.cr().read().hserdy() {} | ||||
| 
 | ||||
|         hse.freq | ||||
|     }); | ||||
| 
 | ||||
|     let hsi48 = config.hsi48.map(super::init_hsi48); | ||||
| 
 | ||||
|     let pll_input = PllInput { hse, hsi, msi }; | ||||
|     let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); | ||||
|     let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); | ||||
|     let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range); | ||||
| 
 | ||||
|     let sys_clk = match config.mux { | ||||
|         ClockSrc::HSE => hse.unwrap(), | ||||
|         ClockSrc::HSI => hsi.unwrap(), | ||||
|         ClockSrc::MSIS => msi.unwrap(), | ||||
|         ClockSrc::PLL1_R => pll1.r.unwrap(), | ||||
|     }; | ||||
| 
 | ||||
|     // Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
 | ||||
|     if sys_clk >= Hertz::mhz(55) { | ||||
|         // Enable the booster
 | ||||
|         PWR.vosr().modify(|w| w.set_boosten(true)); | ||||
|         while !PWR.vosr().read().boostrdy() {} | ||||
|     } | ||||
| 
 | ||||
|     // The clock source is ready
 | ||||
|     // Calculate and set the flash wait states
 | ||||
|     let wait_states = match config.voltage_range { | ||||
|         // VOS 1 range VCORE 1.26V - 1.40V
 | ||||
|         VoltageScale::RANGE1 => { | ||||
|             if sys_clk.0 < 32_000_000 { | ||||
|                 0 | ||||
|             } else if sys_clk.0 < 64_000_000 { | ||||
|                 1 | ||||
|             } else if sys_clk.0 < 96_000_000 { | ||||
|                 2 | ||||
|             } else if sys_clk.0 < 128_000_000 { | ||||
|                 3 | ||||
|             } else { | ||||
|                 4 | ||||
|             } | ||||
|         } | ||||
|         VoltageScale::RANGE1 => match sys_clk.0 { | ||||
|             ..=32_000_000 => 0, | ||||
|             ..=64_000_000 => 1, | ||||
|             ..=96_000_000 => 2, | ||||
|             ..=128_000_000 => 3, | ||||
|             _ => 4, | ||||
|         }, | ||||
|         // VOS 2 range VCORE 1.15V - 1.26V
 | ||||
|         VoltageScale::RANGE2 => { | ||||
|             if sys_clk.0 < 30_000_000 { | ||||
|                 0 | ||||
|             } else if sys_clk.0 < 60_000_000 { | ||||
|                 1 | ||||
|             } else if sys_clk.0 < 90_000_000 { | ||||
|                 2 | ||||
|             } else { | ||||
|                 3 | ||||
|             } | ||||
|         } | ||||
|         VoltageScale::RANGE2 => match sys_clk.0 { | ||||
|             ..=30_000_000 => 0, | ||||
|             ..=60_000_000 => 1, | ||||
|             ..=90_000_000 => 2, | ||||
|             _ => 3, | ||||
|         }, | ||||
|         // VOS 3 range VCORE 1.05V - 1.15V
 | ||||
|         VoltageScale::RANGE3 => { | ||||
|             if sys_clk.0 < 24_000_000 { | ||||
|                 0 | ||||
|             } else if sys_clk.0 < 48_000_000 { | ||||
|                 1 | ||||
|             } else { | ||||
|                 2 | ||||
|             } | ||||
|         } | ||||
|         VoltageScale::RANGE3 => match sys_clk.0 { | ||||
|             ..=24_000_000 => 0, | ||||
|             ..=48_000_000 => 1, | ||||
|             _ => 2, | ||||
|         }, | ||||
|         // VOS 4 range VCORE 0.95V - 1.05V
 | ||||
|         VoltageScale::RANGE4 => { | ||||
|             if sys_clk.0 < 12_000_000 { | ||||
|                 0 | ||||
|             } else { | ||||
|                 1 | ||||
|             } | ||||
|         } | ||||
|         VoltageScale::RANGE4 => match sys_clk.0 { | ||||
|             ..=12_000_000 => 0, | ||||
|             _ => 1, | ||||
|         }, | ||||
|     }; | ||||
|     FLASH.acr().modify(|w| { | ||||
|         w.set_latency(wait_states); | ||||
|     }); | ||||
| 
 | ||||
|     // Switch the system clock source
 | ||||
|     RCC.cfgr1().modify(|w| { | ||||
|         w.set_sw(config.mux.into()); | ||||
|     }); | ||||
| 
 | ||||
|     // RM0456 § 11.4.9 specifies maximum bus frequencies per voltage range, but the maximum bus
 | ||||
|     // frequency for each voltage range exactly matches the maximum permitted PLL output frequency.
 | ||||
|     // Given that:
 | ||||
|     //
 | ||||
|     //   1. Any bus frequency can never exceed the system clock frequency;
 | ||||
|     //   2. We checked the PLL output frequency if we're using it as a system clock;
 | ||||
|     //   3. The maximum HSE frequencies at each voltage range are lower than the bus limits, and
 | ||||
|     //      we checked the HSE frequency if configured as a system clock; and
 | ||||
|     //   4. The maximum frequencies from the other clock sources are lower than the lowest bus
 | ||||
|     //      frequency limit
 | ||||
|     //
 | ||||
|     // ...then we do not need to perform additional bus-related frequency checks.
 | ||||
|     RCC.cfgr1().modify(|w| w.set_sw(config.mux)); | ||||
|     while RCC.cfgr1().read().sws() != config.mux {} | ||||
| 
 | ||||
|     // Configure the bus prescalers
 | ||||
|     RCC.cfgr2().modify(|w| { | ||||
| @ -419,64 +243,52 @@ pub(crate) unsafe fn init(config: Config) { | ||||
|         w.set_ppre3(config.apb3_pre); | ||||
|     }); | ||||
| 
 | ||||
|     let ahb_freq = sys_clk / config.ahb_pre; | ||||
|     let hclk = sys_clk / config.ahb_pre; | ||||
| 
 | ||||
|     let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||||
|         APBPrescaler::DIV1 => (ahb_freq, ahb_freq), | ||||
|         pre => { | ||||
|             let freq = ahb_freq / pre; | ||||
|             (freq, freq * 2u32) | ||||
|         } | ||||
|     let hclk_max = match config.voltage_range { | ||||
|         VoltageScale::RANGE1 => Hertz::mhz(160), | ||||
|         VoltageScale::RANGE2 => Hertz::mhz(110), | ||||
|         VoltageScale::RANGE3 => Hertz::mhz(55), | ||||
|         VoltageScale::RANGE4 => Hertz::mhz(25), | ||||
|     }; | ||||
|     assert!(hclk <= hclk_max); | ||||
| 
 | ||||
|     let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||||
|         APBPrescaler::DIV1 => (ahb_freq, ahb_freq), | ||||
|         pre => { | ||||
|             let freq = ahb_freq / pre; | ||||
|             (freq, freq * 2u32) | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre { | ||||
|         APBPrescaler::DIV1 => (ahb_freq, ahb_freq), | ||||
|         pre => { | ||||
|             let freq = ahb_freq / pre; | ||||
|             (freq, freq * 2u32) | ||||
|         } | ||||
|     }; | ||||
|     let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); | ||||
|     let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); | ||||
|     let (pclk3, _) = super::util::calc_pclk(hclk, config.apb3_pre); | ||||
| 
 | ||||
|     let rtc = config.ls.init(); | ||||
| 
 | ||||
|     set_clocks!( | ||||
|         sys: Some(sys_clk), | ||||
|         hclk1: Some(ahb_freq), | ||||
|         hclk2: Some(ahb_freq), | ||||
|         hclk3: Some(ahb_freq), | ||||
|         pclk1: Some(apb1_freq), | ||||
|         pclk2: Some(apb2_freq), | ||||
|         pclk3: Some(apb3_freq), | ||||
|         pclk1_tim: Some(apb1_tim_freq), | ||||
|         pclk2_tim: Some(apb2_tim_freq), | ||||
|         hclk1: Some(hclk), | ||||
|         hclk2: Some(hclk), | ||||
|         hclk3: Some(hclk), | ||||
|         pclk1: Some(pclk1), | ||||
|         pclk2: Some(pclk2), | ||||
|         pclk3: Some(pclk3), | ||||
|         pclk1_tim: Some(pclk1_tim), | ||||
|         pclk2_tim: Some(pclk2_tim), | ||||
|         hsi48: hsi48, | ||||
|         rtc: rtc, | ||||
|         hse: hse, | ||||
|         hsi: hsi, | ||||
|         pll1_p: pll1.p, | ||||
|         pll1_q: pll1.q, | ||||
|         pll1_r: pll1.r, | ||||
|         pll2_p: pll2.p, | ||||
|         pll2_q: pll2.q, | ||||
|         pll2_r: pll2.r, | ||||
|         pll3_p: pll3.p, | ||||
|         pll3_q: pll3.q, | ||||
|         pll3_r: pll3.r, | ||||
| 
 | ||||
|         // TODO
 | ||||
|         hse: None, | ||||
|         hsi: None, | ||||
|         audioclk: None, | ||||
|         hsi48_div_2: None, | ||||
|         lse: None, | ||||
|         lsi: None, | ||||
|         msik: None, | ||||
|         pll1_p: None, | ||||
|         pll1_q: None, | ||||
|         pll1_r: None, | ||||
|         pll2_p: None, | ||||
|         pll2_q: None, | ||||
|         pll2_r: None, | ||||
|         pll3_p: None, | ||||
|         pll3_q: None, | ||||
|         pll3_r: None, | ||||
|         iclk: None, | ||||
|     ); | ||||
| } | ||||
| @ -501,3 +313,126 @@ fn msirange_to_hertz(range: Msirange) -> Hertz { | ||||
|         Msirange::RANGE_100KHZ => Hertz(100_000), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub(super) struct PllInput { | ||||
|     pub hsi: Option<Hertz>, | ||||
|     pub hse: Option<Hertz>, | ||||
|     pub msi: Option<Hertz>, | ||||
| } | ||||
| 
 | ||||
| #[allow(unused)] | ||||
| #[derive(Default)] | ||||
| pub(super) struct PllOutput { | ||||
|     pub p: Option<Hertz>, | ||||
|     pub q: Option<Hertz>, | ||||
|     pub r: Option<Hertz>, | ||||
| } | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Clone, Copy)] | ||||
| enum PllInstance { | ||||
|     Pll1 = 0, | ||||
|     Pll2 = 1, | ||||
|     Pll3 = 2, | ||||
| } | ||||
| 
 | ||||
| fn pll_enable(instance: PllInstance, enabled: bool) { | ||||
|     RCC.cr().modify(|w| w.set_pllon(instance as _, enabled)); | ||||
|     while RCC.cr().read().pllrdy(instance as _) != enabled {} | ||||
| } | ||||
| 
 | ||||
| fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput { | ||||
|     // Disable PLL
 | ||||
|     pll_enable(instance, false); | ||||
| 
 | ||||
|     let Some(pll) = config else { return PllOutput::default() }; | ||||
| 
 | ||||
|     let src_freq = match pll.source { | ||||
|         PllSource::DISABLE => panic!("must not select PLL source as DISABLE"), | ||||
|         PllSource::HSE => unwrap!(input.hse), | ||||
|         PllSource::HSI => unwrap!(input.hsi), | ||||
|         PllSource::MSIS => unwrap!(input.msi), | ||||
|     }; | ||||
| 
 | ||||
|     // Calculate the reference clock, which is the source divided by m
 | ||||
|     let ref_freq = src_freq / pll.prediv; | ||||
|     // Check limits per RM0456 § 11.4.6
 | ||||
|     assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16)); | ||||
| 
 | ||||
|     // Check PLL clocks per RM0456 § 11.4.10
 | ||||
|     let (vco_min, vco_max, out_max) = match voltage_range { | ||||
|         VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(208)), | ||||
|         VoltageScale::RANGE2 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(110)), | ||||
|         VoltageScale::RANGE3 => (Hertz::mhz(128), Hertz::mhz(330), Hertz::mhz(55)), | ||||
|         VoltageScale::RANGE4 => panic!("PLL is unavailable in voltage range 4"), | ||||
|     }; | ||||
| 
 | ||||
|     // Calculate the PLL VCO clock
 | ||||
|     let vco_freq = ref_freq * pll.mul; | ||||
|     assert!(vco_freq >= vco_min && vco_freq <= vco_max); | ||||
| 
 | ||||
|     // Calculate output clocks.
 | ||||
|     let p = pll.divp.map(|div| vco_freq / div); | ||||
|     let q = pll.divq.map(|div| vco_freq / div); | ||||
|     let r = pll.divr.map(|div| vco_freq / div); | ||||
|     for freq in [p, q, r] { | ||||
|         if let Some(freq) = freq { | ||||
|             assert!(freq <= out_max); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     let divr = match instance { | ||||
|         PllInstance::Pll1 => RCC.pll1divr(), | ||||
|         PllInstance::Pll2 => RCC.pll2divr(), | ||||
|         PllInstance::Pll3 => RCC.pll3divr(), | ||||
|     }; | ||||
|     divr.write(|w| { | ||||
|         w.set_plln(pll.mul); | ||||
|         w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1)); | ||||
|         w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1)); | ||||
|         w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1)); | ||||
|     }); | ||||
| 
 | ||||
|     let input_range = match ref_freq.0 { | ||||
|         ..=8_000_000 => Pllrge::FREQ_4TO8MHZ, | ||||
|         _ => Pllrge::FREQ_8TO16MHZ, | ||||
|     }; | ||||
| 
 | ||||
|     macro_rules! write_fields { | ||||
|         ($w:ident) => { | ||||
|             $w.set_pllpen(pll.divp.is_some()); | ||||
|             $w.set_pllqen(pll.divq.is_some()); | ||||
|             $w.set_pllren(pll.divr.is_some()); | ||||
|             $w.set_pllm(pll.prediv); | ||||
|             $w.set_pllsrc(pll.source); | ||||
|             $w.set_pllrge(input_range); | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     match instance { | ||||
|         PllInstance::Pll1 => RCC.pll1cfgr().write(|w| { | ||||
|             // § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
 | ||||
|             // value that results in an output between 4 and 16 MHz for the PWR EPOD boost
 | ||||
|             if r.unwrap() >= Hertz::mhz(55) { | ||||
|                 // source_clk can be up to 50 MHz, so there's just a few cases:
 | ||||
|                 let mboost = match src_freq.0 { | ||||
|                     ..=16_000_000 => Pllmboost::DIV1, // Bypass, giving EPOD 4-16 MHz
 | ||||
|                     ..=32_000_000 => Pllmboost::DIV2, // Divide by 2, giving EPOD 8-16 MHz
 | ||||
|                     _ => Pllmboost::DIV4,             // Divide by 4, giving EPOD 8-12.5 MHz
 | ||||
|                 }; | ||||
|                 w.set_pllmboost(mboost); | ||||
|             } | ||||
|             write_fields!(w); | ||||
|         }), | ||||
|         PllInstance::Pll2 => RCC.pll2cfgr().write(|w| { | ||||
|             write_fields!(w); | ||||
|         }), | ||||
|         PllInstance::Pll3 => RCC.pll3cfgr().write(|w| { | ||||
|             write_fields!(w); | ||||
|         }), | ||||
|     } | ||||
| 
 | ||||
|     // Enable PLL
 | ||||
|     pll_enable(instance, true); | ||||
| 
 | ||||
|     PllOutput { p, q, r } | ||||
| } | ||||
|  | ||||
| @ -4,7 +4,6 @@ | ||||
| use defmt::{panic, *}; | ||||
| use defmt_rtt as _; // global logger
 | ||||
| use embassy_executor::Spawner; | ||||
| use embassy_stm32::rcc::*; | ||||
| use embassy_stm32::usb_otg::{Driver, Instance}; | ||||
| use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; | ||||
| use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||||
| @ -22,22 +21,28 @@ async fn main(_spawner: Spawner) { | ||||
|     info!("Hello World!"); | ||||
| 
 | ||||
|     let mut config = Config::default(); | ||||
|     config.rcc.mux = ClockSrc::PLL1_R(PllConfig { | ||||
|         source: PllSource::HSI, | ||||
|         m: Pllm::DIV2, | ||||
|         n: Plln::MUL10, | ||||
|         p: Plldiv::DIV1, | ||||
|         q: Plldiv::DIV1, | ||||
|         r: Plldiv::DIV1, | ||||
|     }); | ||||
|     config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
 | ||||
|     { | ||||
|         use embassy_stm32::rcc::*; | ||||
|         config.rcc.hsi = true; | ||||
|         config.rcc.pll1 = Some(Pll { | ||||
|             source: PllSource::HSI, // 16 MHz
 | ||||
|             prediv: PllPreDiv::DIV1, | ||||
|             mul: PllMul::MUL10, | ||||
|             divp: None, | ||||
|             divq: None, | ||||
|             divr: Some(PllDiv::DIV1), // 160 MHz
 | ||||
|         }); | ||||
|         config.rcc.mux = ClockSrc::PLL1_R; | ||||
|         config.rcc.voltage_range = VoltageScale::RANGE1; | ||||
|         config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
 | ||||
|     } | ||||
| 
 | ||||
|     let p = embassy_stm32::init(config); | ||||
| 
 | ||||
|     // Create the driver, from the HAL.
 | ||||
|     let mut ep_out_buffer = [0u8; 256]; | ||||
|     let mut config = embassy_stm32::usb_otg::Config::default(); | ||||
|     config.vbus_detection = true; | ||||
|     config.vbus_detection = false; | ||||
|     let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||||
| 
 | ||||
|     // Create embassy-usb Config
 | ||||
|  | ||||
| @ -577,7 +577,18 @@ pub fn config() -> Config { | ||||
|     #[cfg(any(feature = "stm32u585ai", feature = "stm32u5a5zj"))] | ||||
|     { | ||||
|         use embassy_stm32::rcc::*; | ||||
|         config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_48MHZ); | ||||
|         config.rcc.hsi = true; | ||||
|         config.rcc.pll1 = Some(Pll { | ||||
|             source: PllSource::HSI, // 16 MHz
 | ||||
|             prediv: PllPreDiv::DIV1, | ||||
|             mul: PllMul::MUL10, | ||||
|             divp: None, | ||||
|             divq: None, | ||||
|             divr: Some(PllDiv::DIV1), // 160 MHz
 | ||||
|         }); | ||||
|         config.rcc.mux = ClockSrc::PLL1_R; | ||||
|         config.rcc.voltage_range = VoltageScale::RANGE1; | ||||
|         config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
 | ||||
|     } | ||||
| 
 | ||||
|     #[cfg(feature = "stm32wba52cg")] | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user