diff options
| author | Gerzain Mata <[email protected]> | 2025-07-25 18:57:27 -0700 |
|---|---|---|
| committer | Gerzain Mata <[email protected]> | 2025-07-25 18:59:10 -0700 |
| commit | a5e8891fe315e2ee84992d94bd7f7d5b7710cce6 (patch) | |
| tree | 84de7eac12d3b6abeb920f5bd0f040bbe7501157 /embassy-stm32 | |
| parent | 996974e313fa5ec2c7c2d9dd0998fab244c0a180 (diff) | |
Added support for PLL as a clock source on STM32WBA
- PLL multiplier and dividers work
- Added timer example
Diffstat (limited to 'embassy-stm32')
| -rw-r--r-- | embassy-stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/wba.rs | 247 |
2 files changed, 239 insertions, 12 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 02e75733e..520443466 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -81,7 +81,7 @@ futures-util = { version = "0.3.30", default-features = false } | |||
| 81 | sdio-host = "0.9.0" | 81 | sdio-host = "0.9.0" |
| 82 | critical-section = "1.1" | 82 | critical-section = "1.1" |
| 83 | #stm32-metapac = { version = "16" } | 83 | #stm32-metapac = { version = "16" } |
| 84 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3" } | 84 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-df3f5212f2dd70955a6b3d0137e7b4457c6047bf" } |
| 85 | 85 | ||
| 86 | vcell = "0.1.3" | 86 | vcell = "0.1.3" |
| 87 | nb = "1.0.0" | 87 | nb = "1.0.0" |
| @@ -110,7 +110,7 @@ proc-macro2 = "1.0.36" | |||
| 110 | quote = "1.0.15" | 110 | quote = "1.0.15" |
| 111 | 111 | ||
| 112 | #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} | 112 | #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} |
| 113 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9fc86ca7b3a8bc05182bf1ce3045602df1f5dce3", default-features = false, features = ["metadata"] } | 113 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-df3f5212f2dd70955a6b3d0137e7b4457c6047bf", default-features = false, features = ["metadata"] } |
| 114 | 114 | ||
| 115 | [features] | 115 | [features] |
| 116 | default = ["rt"] | 116 | default = ["rt"] |
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index b494997b3..0025d2a51 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs | |||
| @@ -1,19 +1,81 @@ | |||
| 1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 2 | use crate::pac::rcc::regs::Cfgr1; | 2 | use crate::pac::rcc::regs::Cfgr1; |
| 3 | pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsepre as HsePrescaler, Ppre as APBPrescaler, Sw as Sysclk}; | 3 | use core::ops::Div; |
| 4 | pub use crate::pac::rcc::vals::{ | ||
| 5 | Hpre as AHBPrescaler, Hsepre as HsePrescaler, Ppre as APBPrescaler, Sw as Sysclk, Pllsrc as PllSource, | ||
| 6 | Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Hpre5 as AHB5Prescaler, Hdiv5, | ||
| 7 | }; | ||
| 8 | use crate::pac::rcc::vals::Pllrge; | ||
| 4 | use crate::pac::{FLASH, RCC}; | 9 | use crate::pac::{FLASH, RCC}; |
| 10 | use crate::rcc::LSI_FREQ; | ||
| 5 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 6 | 12 | ||
| 13 | #[cfg(all(peri_usb_otg_hs))] | ||
| 14 | pub use crate::pac::rcc::vals::Otghssel; | ||
| 15 | |||
| 16 | #[cfg(all(peri_usb_otg_hs))] | ||
| 17 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 18 | |||
| 7 | /// HSI speed | 19 | /// HSI speed |
| 8 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 20 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 9 | // HSE speed | 21 | // HSE speed |
| 10 | pub const HSE_FREQ: Hertz = Hertz(32_000_000); | 22 | pub const HSE_FREQ: Hertz = Hertz(32_000_000); |
| 11 | 23 | ||
| 24 | // Allow dividing a Hertz value by an AHB5 prescaler directly | ||
| 25 | impl Div<AHB5Prescaler> for Hertz { | ||
| 26 | type Output = Hertz; | ||
| 27 | fn div(self, rhs: AHB5Prescaler) -> Hertz { | ||
| 28 | // Map the prescaler enum to its integer divisor | ||
| 29 | let divisor = match rhs { | ||
| 30 | AHB5Prescaler::DIV1 => 1, | ||
| 31 | AHB5Prescaler::DIV2 => 2, | ||
| 32 | AHB5Prescaler::DIV3 => 3, | ||
| 33 | AHB5Prescaler::DIV4 => 4, | ||
| 34 | AHB5Prescaler::DIV6 => 6, | ||
| 35 | _ => unreachable!("Invalid AHB5 prescaler: {:?}", rhs), | ||
| 36 | }; | ||
| 37 | Hertz(self.0 / divisor) | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 12 | #[derive(Clone, Copy, Eq, PartialEq)] | 41 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 13 | pub struct Hse { | 42 | pub struct Hse { |
| 14 | pub prescaler: HsePrescaler, | 43 | pub prescaler: HsePrescaler, |
| 15 | } | 44 | } |
| 16 | 45 | ||
| 46 | #[derive(Clone, Copy)] | ||
| 47 | pub struct Pll { | ||
| 48 | /// The clock source for the PLL. | ||
| 49 | pub source: PllSource, | ||
| 50 | /// The PLL pre-divider. | ||
| 51 | /// | ||
| 52 | /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz. | ||
| 53 | pub prediv: PllPreDiv, | ||
| 54 | /// The PLL multiplier. | ||
| 55 | /// | ||
| 56 | /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544 | ||
| 57 | /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`. | ||
| 58 | pub mul: PllMul, | ||
| 59 | /// The divider for the P output. | ||
| 60 | /// | ||
| 61 | /// The P output is one of several options | ||
| 62 | /// that can be used to feed the SAI/MDF/ADF Clock mux's. | ||
| 63 | pub divp: Option<PllDiv>, | ||
| 64 | /// The divider for the Q output. | ||
| 65 | /// | ||
| 66 | /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks | ||
| 67 | /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's. | ||
| 68 | pub divq: Option<PllDiv>, | ||
| 69 | /// The divider for the R output. | ||
| 70 | /// | ||
| 71 | /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r` | ||
| 72 | /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default | ||
| 73 | /// `Config { voltage_range }`. | ||
| 74 | pub divr: Option<PllDiv>, | ||
| 75 | |||
| 76 | pub frac: Option<u16>, | ||
| 77 | } | ||
| 78 | |||
| 17 | /// Clocks configuration | 79 | /// Clocks configuration |
| 18 | #[derive(Clone, Copy)] | 80 | #[derive(Clone, Copy)] |
| 19 | pub struct Config { | 81 | pub struct Config { |
| @@ -21,15 +83,20 @@ pub struct Config { | |||
| 21 | pub hsi: bool, | 83 | pub hsi: bool, |
| 22 | pub hse: Option<Hse>, | 84 | pub hse: Option<Hse>, |
| 23 | 85 | ||
| 86 | // pll | ||
| 87 | pub pll1: Option<Pll>, | ||
| 88 | |||
| 24 | // sysclk, buses. | 89 | // sysclk, buses. |
| 25 | pub sys: Sysclk, | 90 | pub sys: Sysclk, |
| 26 | pub ahb_pre: AHBPrescaler, | 91 | pub ahb_pre: AHBPrescaler, |
| 92 | pub ahb5_pre: AHB5Prescaler, | ||
| 27 | pub apb1_pre: APBPrescaler, | 93 | pub apb1_pre: APBPrescaler, |
| 28 | pub apb2_pre: APBPrescaler, | 94 | pub apb2_pre: APBPrescaler, |
| 29 | pub apb7_pre: APBPrescaler, | 95 | pub apb7_pre: APBPrescaler, |
| 30 | 96 | ||
| 31 | // low speed LSI/LSE/RTC | 97 | // low speed LSI/LSE/RTC |
| 32 | pub ls: super::LsConfig, | 98 | pub lsi: super::LsConfig, |
| 99 | // pub lsi2: super::LsConfig, | ||
| 33 | 100 | ||
| 34 | pub voltage_scale: VoltageScale, | 101 | pub voltage_scale: VoltageScale, |
| 35 | 102 | ||
| @@ -40,14 +107,17 @@ pub struct Config { | |||
| 40 | impl Config { | 107 | impl Config { |
| 41 | pub const fn new() -> Self { | 108 | pub const fn new() -> Self { |
| 42 | Config { | 109 | Config { |
| 43 | hse: None, | ||
| 44 | hsi: true, | 110 | hsi: true, |
| 111 | hse: None, | ||
| 112 | pll1: None, | ||
| 45 | sys: Sysclk::HSI, | 113 | sys: Sysclk::HSI, |
| 46 | ahb_pre: AHBPrescaler::DIV1, | 114 | ahb_pre: AHBPrescaler::DIV1, |
| 115 | ahb5_pre: AHB5Prescaler::DIV1, | ||
| 47 | apb1_pre: APBPrescaler::DIV1, | 116 | apb1_pre: APBPrescaler::DIV1, |
| 48 | apb2_pre: APBPrescaler::DIV1, | 117 | apb2_pre: APBPrescaler::DIV1, |
| 49 | apb7_pre: APBPrescaler::DIV1, | 118 | apb7_pre: APBPrescaler::DIV1, |
| 50 | ls: crate::rcc::LsConfig::new(), | 119 | lsi: crate::rcc::LsConfig::new(), |
| 120 | // lsi2: crate::rcc::LsConfig::new(), | ||
| 51 | voltage_scale: VoltageScale::RANGE2, | 121 | voltage_scale: VoltageScale::RANGE2, |
| 52 | mux: super::mux::ClockMux::default(), | 122 | mux: super::mux::ClockMux::default(), |
| 53 | } | 123 | } |
| @@ -81,7 +151,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 81 | crate::pac::PWR.vosr().write(|w| w.set_vos(config.voltage_scale)); | 151 | crate::pac::PWR.vosr().write(|w| w.set_vos(config.voltage_scale)); |
| 82 | while !crate::pac::PWR.vosr().read().vosrdy() {} | 152 | while !crate::pac::PWR.vosr().read().vosrdy() {} |
| 83 | 153 | ||
| 84 | let rtc = config.ls.init(); | 154 | let rtc = config.lsi.init(); |
| 85 | 155 | ||
| 86 | let hsi = config.hsi.then(|| { | 156 | let hsi = config.hsi.then(|| { |
| 87 | hsi_enable(); | 157 | hsi_enable(); |
| @@ -99,11 +169,15 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 99 | HSE_FREQ | 169 | HSE_FREQ |
| 100 | }); | 170 | }); |
| 101 | 171 | ||
| 172 | let pll_input = PllInput {hse, hsi }; | ||
| 173 | |||
| 174 | let pll1 = init_pll(config.pll1, &pll_input, config.voltage_scale); | ||
| 175 | |||
| 102 | let sys_clk = match config.sys { | 176 | let sys_clk = match config.sys { |
| 103 | Sysclk::HSE => hse.unwrap(), | 177 | Sysclk::HSE => hse.unwrap(), |
| 104 | Sysclk::HSI => hsi.unwrap(), | 178 | Sysclk::HSI => hsi.unwrap(), |
| 105 | Sysclk::_RESERVED_1 => unreachable!(), | 179 | Sysclk::_RESERVED_1 => unreachable!(), |
| 106 | Sysclk::PLL1_R => todo!(), | 180 | Sysclk::PLL1_R => pll1.r.unwrap(), |
| 107 | }; | 181 | }; |
| 108 | 182 | ||
| 109 | assert!(sys_clk.0 <= 100_000_000); | 183 | assert!(sys_clk.0 <= 100_000_000); |
| @@ -111,7 +185,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 111 | let hclk1 = sys_clk / config.ahb_pre; | 185 | let hclk1 = sys_clk / config.ahb_pre; |
| 112 | let hclk2 = hclk1; | 186 | let hclk2 = hclk1; |
| 113 | let hclk4 = hclk1; | 187 | let hclk4 = hclk1; |
| 114 | // TODO: hclk5 | ||
| 115 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre); | 188 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre); |
| 116 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre); | 189 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre); |
| 117 | let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre); | 190 | let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre); |
| @@ -157,6 +230,54 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 157 | w.set_ppre2(config.apb2_pre); | 230 | w.set_ppre2(config.apb2_pre); |
| 158 | }); | 231 | }); |
| 159 | 232 | ||
| 233 | // Set AHB5 prescaler depending on sysclk source | ||
| 234 | RCC.cfgr4().modify(|w| match config.sys { | ||
| 235 | // When using HSI or HSE, use HDIV5 bit (0 = div1, 1 = div2) | ||
| 236 | Sysclk::HSI | Sysclk::HSE => { | ||
| 237 | // Only Div1 and Div2 are valid for HDIV5, enforce this | ||
| 238 | match config.ahb5_pre { | ||
| 239 | AHB5Prescaler::DIV1 => w.set_hdiv5(Hdiv5::DIV1), | ||
| 240 | AHB5Prescaler::DIV2 => w.set_hdiv5(Hdiv5::DIV2), | ||
| 241 | _ => panic!("Invalid ahb5_pre for HSI/HSE sysclk: only DIV1 and DIV2 are allowed"), | ||
| 242 | }; | ||
| 243 | } | ||
| 244 | // When using PLL1, use HPRE5 bits [2:0] | ||
| 245 | Sysclk::PLL1_R => { | ||
| 246 | w.set_hpre5(config.ahb5_pre); | ||
| 247 | } | ||
| 248 | _ => {} | ||
| 249 | }); | ||
| 250 | |||
| 251 | let hclk5 = sys_clk / config.ahb5_pre; | ||
| 252 | |||
| 253 | |||
| 254 | #[cfg(all(stm32wba, peri_usb_otg_hs))] | ||
| 255 | let usb_refck = match config.mux.otghssel { | ||
| 256 | Otghssel::HSE => hse, | ||
| 257 | Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), | ||
| 258 | Otghssel::PLL1_P => pll1.p, | ||
| 259 | Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8), | ||
| 260 | }; | ||
| 261 | #[cfg(all(stm32wba, peri_usb_otg_hs))] | ||
| 262 | let usb_refck_sel = match usb_refck { | ||
| 263 | Some(clk_val) => match clk_val { | ||
| 264 | Hertz(16_000_000) => Usbrefcksel::MHZ16, | ||
| 265 | Hertz(19_200_000) => Usbrefcksel::MHZ19_2, | ||
| 266 | Hertz(20_000_000) => Usbrefcksel::MHZ20, | ||
| 267 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | ||
| 268 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | ||
| 269 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | ||
| 270 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | ||
| 271 | }, | ||
| 272 | None => Usbrefcksel::MHZ24, | ||
| 273 | }; | ||
| 274 | #[cfg(all(stm32wba, peri_usb_otg_hs))] | ||
| 275 | SYSCFG.otghsphycr().modify(|w| { | ||
| 276 | w.set_clksel(usb_refck_sel); | ||
| 277 | }); | ||
| 278 | |||
| 279 | let lsi = config.lsi.lsi.then_some(LSI_FREQ); | ||
| 280 | |||
| 160 | config.mux.init(); | 281 | config.mux.init(); |
| 161 | 282 | ||
| 162 | set_clocks!( | 283 | set_clocks!( |
| @@ -164,6 +285,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 164 | hclk1: Some(hclk1), | 285 | hclk1: Some(hclk1), |
| 165 | hclk2: Some(hclk2), | 286 | hclk2: Some(hclk2), |
| 166 | hclk4: Some(hclk4), | 287 | hclk4: Some(hclk4), |
| 288 | hclk5: Some(hclk5), | ||
| 167 | pclk1: Some(pclk1), | 289 | pclk1: Some(pclk1), |
| 168 | pclk2: Some(pclk2), | 290 | pclk2: Some(pclk2), |
| 169 | pclk7: Some(pclk7), | 291 | pclk7: Some(pclk7), |
| @@ -171,12 +293,117 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 171 | pclk2_tim: Some(pclk2_tim), | 293 | pclk2_tim: Some(pclk2_tim), |
| 172 | rtc: rtc, | 294 | rtc: rtc, |
| 173 | hse: hse, | 295 | hse: hse, |
| 296 | lsi: lsi, | ||
| 174 | hsi: hsi, | 297 | hsi: hsi, |
| 298 | pll1_p: pll1.p, | ||
| 299 | pll1_q: pll1.q, | ||
| 300 | pll1_r: pll1.r, | ||
| 175 | 301 | ||
| 176 | // TODO | 302 | // TODO |
| 177 | lse: None, | 303 | lse: None, |
| 178 | lsi: None, | ||
| 179 | pll1_p: None, | ||
| 180 | pll1_q: None, | ||
| 181 | ); | 304 | ); |
| 182 | } | 305 | } |
| 306 | |||
| 307 | pub(super) struct PllInput { | ||
| 308 | pub hsi: Option<Hertz>, | ||
| 309 | pub hse: Option<Hertz>, | ||
| 310 | } | ||
| 311 | |||
| 312 | #[allow(unused)] | ||
| 313 | #[derive(Default)] | ||
| 314 | pub(super) struct PllOutput { | ||
| 315 | pub p: Option<Hertz>, | ||
| 316 | pub q: Option<Hertz>, | ||
| 317 | pub r: Option<Hertz>, | ||
| 318 | } | ||
| 319 | |||
| 320 | fn pll_enable(enabled: bool) { | ||
| 321 | RCC.cr().modify(|w| w.set_pllon(enabled)); | ||
| 322 | while RCC.cr().read().pllrdy() != enabled {} | ||
| 323 | } | ||
| 324 | |||
| 325 | fn init_pll(config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput { | ||
| 326 | // Disable PLL | ||
| 327 | pll_enable(false); | ||
| 328 | |||
| 329 | let Some(pll) = config else { return PllOutput::default() }; | ||
| 330 | |||
| 331 | let pre_src_freq = match pll.source { | ||
| 332 | PllSource::DISABLE => panic!("must not select PLL source as DISABLE"), | ||
| 333 | PllSource::HSE => unwrap!(input.hse), | ||
| 334 | PllSource::HSI => unwrap!(input.hsi), | ||
| 335 | PllSource::_RESERVED_1 => panic!("must not select RESERVED_1 source as DISABLE"), | ||
| 336 | }; | ||
| 337 | |||
| 338 | // Only divide by the HSE prescaler when the PLL source is HSE | ||
| 339 | let src_freq = match pll.source { | ||
| 340 | PllSource::HSE => { | ||
| 341 | // read the prescaler bits and divide | ||
| 342 | let hsepre = RCC.cr().read().hsepre(); | ||
| 343 | pre_src_freq / hsepre | ||
| 344 | } | ||
| 345 | _ => pre_src_freq, | ||
| 346 | }; | ||
| 347 | |||
| 348 | // Calculate the reference clock, which is the source divided by m | ||
| 349 | let ref_freq = src_freq / pll.prediv; | ||
| 350 | // Check limits per RM0515 § 12.4.3 | ||
| 351 | assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16)); | ||
| 352 | |||
| 353 | // Check PLL clocks per RM0515 § 12.4.5 | ||
| 354 | let (vco_min, vco_max, out_max) = match voltage_range { | ||
| 355 | VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(100)), | ||
| 356 | VoltageScale::RANGE2 => panic!("PLL is unavailable in voltage range 2"), | ||
| 357 | }; | ||
| 358 | |||
| 359 | // Calculate the PLL VCO clock | ||
| 360 | // let vco_freq = ref_freq * pll.mul; | ||
| 361 | // Calculate VCO frequency including fractional part: FVCO = Fref_ck × (N + FRAC/2^13) | ||
| 362 | let numerator = (ref_freq.0 as u64) * (((pll.mul as u64) + 1 << 13) + pll.frac.unwrap_or(0) as u64); | ||
| 363 | let vco_hz = (numerator >> 13) as u32; | ||
| 364 | let vco_freq = Hertz(vco_hz); | ||
| 365 | assert!(vco_freq >= vco_min && vco_freq <= vco_max); | ||
| 366 | |||
| 367 | // Calculate output clocks. | ||
| 368 | let p = pll.divp.map(|div| vco_freq / div); | ||
| 369 | let q = pll.divq.map(|div| vco_freq / div); | ||
| 370 | let r = pll.divr.map(|div| vco_freq / div); | ||
| 371 | for freq in [p, q, r] { | ||
| 372 | if let Some(freq) = freq { | ||
| 373 | assert!(freq <= out_max); | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | let divr = RCC.pll1divr(); | ||
| 378 | divr.write(|w| { | ||
| 379 | w.set_plln(pll.mul); | ||
| 380 | w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1)); | ||
| 381 | w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1)); | ||
| 382 | w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1)); | ||
| 383 | }); | ||
| 384 | RCC.pll1fracr().write(|w| {w.set_pllfracn(pll.frac.unwrap_or(0));}); | ||
| 385 | |||
| 386 | let input_range = match ref_freq.0 { | ||
| 387 | ..=8_000_000 => Pllrge::FREQ_4TO8MHZ, | ||
| 388 | _ => Pllrge::FREQ_8TO16MHZ, | ||
| 389 | }; | ||
| 390 | |||
| 391 | macro_rules! write_fields { | ||
| 392 | ($w:ident) => { | ||
| 393 | $w.set_pllpen(pll.divp.is_some()); | ||
| 394 | $w.set_pllqen(pll.divq.is_some()); | ||
| 395 | $w.set_pllren(pll.divr.is_some()); | ||
| 396 | $w.set_pllfracen(pll.frac.is_some()); | ||
| 397 | $w.set_pllm(pll.prediv); | ||
| 398 | $w.set_pllsrc(pll.source); | ||
| 399 | $w.set_pllrge(input_range); | ||
| 400 | }; | ||
| 401 | } | ||
| 402 | |||
| 403 | RCC.pll1cfgr().write(|w| {write_fields!(w);}); | ||
| 404 | |||
| 405 | // Enable PLL | ||
| 406 | pll_enable(true); | ||
| 407 | |||
| 408 | PllOutput{ p, q, r } | ||
| 409 | } \ No newline at end of file | ||
