diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 1e5733d31..020f4e200 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,9 +1,8 @@ use crate::pac::rcc::regs::Cfgr; pub use crate::pac::rcc::vals::{ Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, - Pllr as PllRDiv, Ppre as APBPrescaler, + Pllr as PllRDiv, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc, }; -use crate::pac::rcc::vals::{Msirange, Pllsrc, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -11,42 +10,47 @@ use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); -/// System clock mux source #[derive(Clone, Copy)] -pub enum ClockSrc { - MSI(MSIRange), - PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option), - HSE(Hertz), - HSI16, -} +pub struct Pll { + /// PLL pre-divider (DIVM). + pub prediv: PllPreDiv, -/// PLL clock input source -#[derive(Clone, Copy)] -pub enum PLLSource { - HSI16, - HSE(Hertz), - MSI(MSIRange), -} + /// PLL multiplication factor. + pub mul: PllMul, -impl From for Pllsrc { - fn from(val: PLLSource) -> Pllsrc { - match val { - PLLSource::HSI16 => Pllsrc::HSI16, - PLLSource::HSE(_) => Pllsrc::HSE, - PLLSource::MSI(_) => Pllsrc::MSI, - } - } + /// PLL P division factor. If None, PLL P output is disabled. + pub divp: Option, + /// PLL Q division factor. If None, PLL Q output is disabled. + pub divq: Option, + /// PLL R division factor. If None, PLL R output is disabled. + pub divr: Option, } /// Clocks configutation pub struct Config { + // base clock sources + pub msi: Option, + pub hsi16: bool, + pub hse: Option, + #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + pub hsi48: bool, + + // pll + pub pll_src: PLLSource, + pub pll: Option, + pub pllsai1: Option, + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + pub pllsai2: Option, + + // sysclk, buses. pub mux: ClockSrc, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, - pub pllsai1: Option<(PllMul, PllPreDiv, Option, Option, Option)>, - #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] - pub hsi48: bool, + + // low speed LSI/LSE/RTC pub ls: super::LsConfig, } @@ -54,11 +58,20 @@ impl Default for Config { #[inline] fn default() -> Config { Config { - mux: ClockSrc::MSI(MSIRange::RANGE4M), + hse: None, + hsi16: false, + msi: Some(MSIRange::RANGE4M), + mux: ClockSrc::MSI, ahb_pre: AHBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1, + pll_src: PLLSource::NONE, + pll: None, pllsai1: None, + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + pllsai2: None, #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] hsi48: false, ls: Default::default(), @@ -80,154 +93,204 @@ pub(crate) unsafe fn init(config: Config) { // Wait until MSI is running while !RCC.cr().read().msirdy() {} } - if RCC.cfgr().read().sws() != Sw::MSI { + if RCC.cfgr().read().sws() != ClockSrc::MSI { // Set MSI as a clock source, reset prescalers. RCC.cfgr().write_value(Cfgr::default()); // Wait for clock switch status bits to change. - while RCC.cfgr().read().sws() != Sw::MSI {} + while RCC.cfgr().read().sws() != ClockSrc::MSI {} } let rtc = config.ls.init(); - let (sys_clk, sw) = match config.mux { - ClockSrc::MSI(range) => { - // Enable MSI - RCC.cr().write(|w| { - w.set_msirange(range); - w.set_msirgsel(true); - w.set_msion(true); + let msi = config.msi.map(|range| { + // Enable MSI + RCC.cr().write(|w| { + w.set_msirange(range); + w.set_msirgsel(true); + w.set_msion(true); - // If LSE is enabled, enable calibration of MSI - w.set_msipllen(config.ls.lse.is_some()); - }); - while !RCC.cr().read().msirdy() {} + // If LSE is enabled, enable calibration of MSI + w.set_msipllen(config.ls.lse.is_some()); + }); + while !RCC.cr().read().msirdy() {} - // Enable as clock source for USB, RNG if running at 48 MHz - if range == MSIRange::RANGE48M { - RCC.ccipr().modify(|w| { - w.set_clk48sel(0b11); - }); - } - (msirange_to_hertz(range), Sw::MSI) + // Enable as clock source for USB, RNG if running at 48 MHz + if range == MSIRange::RANGE48M { + RCC.ccipr().modify(|w| w.set_clk48sel(0b11)); } - ClockSrc::HSI16 => { - // Enable HSI16 - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - (HSI_FREQ, Sw::HSI16) - } - ClockSrc::HSE(freq) => { - // Enable HSE - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} + msirange_to_hertz(range) + }); - (freq, Sw::HSE) - } - ClockSrc::PLL(src, divr, prediv, mul, divq) => { - let src_freq = match src { - PLLSource::HSE(freq) => { - // Enable HSE - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - freq - } - PLLSource::HSI16 => { - // Enable HSI - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - HSI_FREQ - } - PLLSource::MSI(range) => { - // Enable MSI - RCC.cr().write(|w| { - w.set_msirange(range); - w.set_msipllen(false); // should be turned on if LSE is started - w.set_msirgsel(true); - w.set_msion(true); - }); - while !RCC.cr().read().msirdy() {} + let hsi16 = config.hsi16.then(|| { + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} - msirange_to_hertz(range) - } - }; + HSI_FREQ + }); - // Disable PLL - RCC.cr().modify(|w| w.set_pllon(false)); - while RCC.cr().read().pllrdy() {} + let hse = config.hse.map(|freq| { + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} - let freq = src_freq / prediv * mul / divr; - - #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))] - assert!(freq.0 <= 120_000_000); - #[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))] - assert!(freq.0 <= 80_000_000); - - RCC.pllcfgr().write(move |w| { - w.set_plln(mul); - w.set_pllm(prediv); - w.set_pllr(divr); - if let Some(divq) = divq { - w.set_pllq(divq); - w.set_pllqen(true); - } - w.set_pllsrc(src.into()); - }); - - // Enable as clock source for USB, RNG if PLL48 divisor is provided - if let Some(divq) = divq { - let freq = src_freq / prediv * mul / divq; - assert!(freq.0 == 48_000_000); - RCC.ccipr().modify(|w| { - w.set_clk48sel(0b10); - }); - } - - if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 { - RCC.pllsai1cfgr().write(move |w| { - w.set_plln(mul); - w.set_pllm(prediv); - if let Some(r_div) = r_div { - w.set_pllr(r_div); - w.set_pllren(true); - } - if let Some(q_div) = q_div { - w.set_pllq(q_div); - w.set_pllqen(true); - let freq = src_freq / prediv * mul / q_div; - if freq.0 == 48_000_000 { - RCC.ccipr().modify(|w| { - w.set_clk48sel(0b1); - }); - } - } - if let Some(p_div) = p_div { - w.set_pllp(p_div); - w.set_pllpen(true); - } - }); - - RCC.cr().modify(|w| w.set_pllsai1on(true)); - } - - // Enable PLL - RCC.cr().modify(|w| w.set_pllon(true)); - while !RCC.cr().read().pllrdy() {} - RCC.pllcfgr().modify(|w| w.set_pllren(true)); - - (freq, Sw::PLL) - } - }; + freq + }); #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] - if config.hsi48 { + let _hsi48 = config.hsi48.then(|| { RCC.crrcr().modify(|w| w.set_hsi48on(true)); while !RCC.crrcr().read().hsi48rdy() {} // Enable as clock source for USB, RNG and SDMMC RCC.ccipr().modify(|w| w.set_clk48sel(0)); + + Hertz(48_000_000) + }); + + let pll_src = match config.pll_src { + PLLSource::NONE => None, + PLLSource::HSE => hse, + PLLSource::HSI16 => hsi16, + PLLSource::MSI => msi, + }; + + let mut _pllp = None; + let mut _pllq = None; + let mut _pllr = None; + if let Some(pll) = config.pll { + let pll_src = pll_src.unwrap(); + + // Disable PLL + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} + + let vco_freq = pll_src / pll.prediv * pll.mul; + + _pllp = pll.divp.map(|div| vco_freq / div); + _pllq = pll.divq.map(|div| vco_freq / div); + _pllr = pll.divr.map(|div| vco_freq / div); + + RCC.pllcfgr().write(move |w| { + w.set_plln(pll.mul); + w.set_pllm(pll.prediv); + w.set_pllsrc(config.pll_src); + if let Some(divp) = pll.divp { + w.set_pllp(divp); + w.set_pllpen(true); + } + if let Some(divq) = pll.divq { + w.set_pllq(divq); + w.set_pllqen(true); + } + if let Some(divr) = pll.divr { + w.set_pllr(divr); + w.set_pllren(true); + } + }); + + if _pllq == Some(Hertz(48_000_000)) { + RCC.ccipr().modify(|w| w.set_clk48sel(0b10)); + } + + // Enable PLL + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + } else { + // even if we're not using the main pll, set the source for pllsai + RCC.pllcfgr().write(move |w| { + w.set_pllsrc(config.pll_src); + }); } + if let Some(pll) = config.pllsai1 { + let pll_src = pll_src.unwrap(); + + // Disable PLL + RCC.cr().modify(|w| w.set_pllsai1on(false)); + while RCC.cr().read().pllsai1rdy() {} + + let vco_freq = pll_src / pll.prediv * pll.mul; + + let _pllp = pll.divp.map(|div| vco_freq / div); + let _pllq = pll.divq.map(|div| vco_freq / div); + let _pllr = pll.divr.map(|div| vco_freq / div); + + RCC.pllsai1cfgr().write(move |w| { + w.set_plln(pll.mul); + w.set_pllm(pll.prediv); + if let Some(divp) = pll.divp { + w.set_pllp(divp); + w.set_pllpen(true); + } + if let Some(divq) = pll.divq { + w.set_pllq(divq); + w.set_pllqen(true); + } + if let Some(divr) = pll.divr { + w.set_pllr(divr); + w.set_pllren(true); + } + }); + + if _pllq == Some(Hertz(48_000_000)) { + RCC.ccipr().modify(|w| w.set_clk48sel(0b01)); + } + + // Enable PLL + RCC.cr().modify(|w| w.set_pllsai1on(true)); + while !RCC.cr().read().pllsai1rdy() {} + } + + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + if let Some(pll) = config.pllsai2 { + let pll_src = pll_src.unwrap(); + + // Disable PLL + RCC.cr().modify(|w| w.set_pllsai2on(false)); + while RCC.cr().read().pllsai2rdy() {} + + let vco_freq = pll_src / pll.prediv * pll.mul; + + let _pllp = pll.divp.map(|div| vco_freq / div); + let _pllq = pll.divq.map(|div| vco_freq / div); + let _pllr = pll.divr.map(|div| vco_freq / div); + + RCC.pllsai2cfgr().write(move |w| { + w.set_plln(pll.mul); + w.set_pllm(pll.prediv); + if let Some(divp) = pll.divp { + w.set_pllp(divp); + w.set_pllpen(true); + } + if let Some(divq) = pll.divq { + w.set_pllq(divq); + w.set_pllqen(true); + } + if let Some(divr) = pll.divr { + w.set_pllr(divr); + w.set_pllren(true); + } + }); + + // Enable PLL + RCC.cr().modify(|w| w.set_pllsai2on(true)); + while !RCC.cr().read().pllsai2rdy() {} + } + + let sys_clk = match config.mux { + ClockSrc::HSE => hse.unwrap(), + ClockSrc::HSI16 => hsi16.unwrap(), + ClockSrc::MSI => msi.unwrap(), + ClockSrc::PLL => _pllr.unwrap(), + }; + + #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))] + assert!(sys_clk.0 <= 120_000_000); + #[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))] + assert!(sys_clk.0 <= 80_000_000); + // Set flash wait states FLASH.acr().modify(|w| { w.set_latency(match sys_clk.0 { @@ -240,7 +303,7 @@ pub(crate) unsafe fn init(config: Config) { }); RCC.cfgr().modify(|w| { - w.set_sw(sw); + w.set_sw(config.mux); w.set_hpre(config.ahb_pre); w.set_ppre1(config.apb1_pre); w.set_ppre2(config.apb2_pre); @@ -277,7 +340,7 @@ pub(crate) unsafe fn init(config: Config) { }); } -fn msirange_to_hertz(range: Msirange) -> Hertz { +fn msirange_to_hertz(range: MSIRange) -> Hertz { match range { MSIRange::RANGE100K => Hertz(100_000), MSIRange::RANGE200K => Hertz(200_000), diff --git a/examples/stm32l4/src/bin/rng.rs b/examples/stm32l4/src/bin/rng.rs index d0208d8a3..94251c12c 100644 --- a/examples/stm32l4/src/bin/rng.rs +++ b/examples/stm32l4/src/bin/rng.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ClockSrc, PLLSource, PllMul, PllPreDiv, PllQDiv, PllRDiv}; +use embassy_stm32::rcc::{ClockSrc, PLLSource, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv}; use embassy_stm32::rng::Rng; use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; @@ -16,14 +16,16 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - // 72Mhz clock (16 / 1 * 18 / 4) - config.rcc.mux = ClockSrc::PLL( - PLLSource::HSI16, - PllRDiv::DIV4, - PllPreDiv::DIV1, - PllMul::MUL18, - Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6) - ); + config.rcc.mux = ClockSrc::PLL; + config.rcc.hsi16 = true; + config.rcc.pll_src = PLLSource::HSI16; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL18, + divp: None, + divq: Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6) + divr: Some(PllRDiv::DIV4), // sysclk 72Mhz clock (16 / 1 * 18 / 4) + }); let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index f5d46e95f..cd9f72ff3 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -5,7 +5,7 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ClockSrc, LsConfig, PLLSource, PllMul, PllPreDiv, PllRDiv}; +use embassy_stm32::rcc::{ClockSrc, LsConfig, PLLSource, Pll, PllMul, PllPreDiv, PllRDiv}; use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_stm32::time::Hertz; use embassy_stm32::Config; @@ -14,18 +14,20 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = { - let mut config = Config::default(); - config.rcc.mux = ClockSrc::PLL( - PLLSource::HSE(Hertz::mhz(8)), - PllRDiv::DIV2, - PllPreDiv::DIV1, - PllMul::MUL20, - None, - ); - config.rcc.ls = LsConfig::default_lse(); - embassy_stm32::init(config) - }; + let mut config = Config::default(); + config.rcc.mux = ClockSrc::PLL; + config.rcc.hse = Some(Hertz::mhz(8)); + config.rcc.pll_src = PLLSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL20, + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2) + }); + config.rcc.ls = LsConfig::default_lse(); + let p = embassy_stm32::init(config); + info!("Hello World!"); let now = NaiveDate::from_ymd_opt(2020, 5, 15) diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index e2ac22d09..c1a27cf83 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -48,7 +48,7 @@ use embassy_net_adin1110::{self, Device, Runner, ADIN1110}; use embedded_hal_bus::spi::ExclusiveDevice; use hal::gpio::Pull; use hal::i2c::Config as I2C_Config; -use hal::rcc::{ClockSrc, PLLSource, PllMul, PllPreDiv, PllRDiv}; +use hal::rcc::{ClockSrc, PLLSource, Pll, PllMul, PllPreDiv, PllRDiv}; use hal::spi::{Config as SPI_Config, Spi}; use hal::time::Hertz; @@ -77,13 +77,16 @@ async fn main(spawner: Spawner) { // 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2) // 80MHz highest frequency for flash 0 wait. - config.rcc.mux = ClockSrc::PLL( - PLLSource::HSE(Hertz(8_000_000)), - PllRDiv::DIV2, - PllPreDiv::DIV1, - PllMul::MUL20, - None, - ); + config.rcc.mux = ClockSrc::PLL; + config.rcc.hse = Some(Hertz::mhz(8)); + config.rcc.pll_src = PLLSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL20, + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2) + }); config.rcc.hsi48 = true; // needed for rng let dp = embassy_stm32::init(config); diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index dc0d98ad4..8f6eeef32 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -23,8 +23,17 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut config = Config::default(); - config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PllRDiv::DIV2, PllPreDiv::DIV1, PllMul::MUL10, None); config.rcc.hsi48 = true; + config.rcc.mux = ClockSrc::PLL; + config.rcc.hsi16 = true; + config.rcc.pll_src = PLLSource::HSI16; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL10, + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // sysclk 80Mhz (16 / 1 * 10 / 2) + }); let p = embassy_stm32::init(config); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 2bf500798..e1d7855fc 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -284,17 +284,19 @@ pub fn config() -> Config { config.rcc.adc_clock_source = AdcClockSource::PLL2_P; } - #[cfg(any(feature = "stm32l4a6zg", feature = "stm32l4r5zi"))] + #[cfg(any(feature = "stm32l496zg", feature = "stm32l4a6zg", feature = "stm32l4r5zi"))] { use embassy_stm32::rcc::*; - config.rcc.mux = ClockSrc::PLL( - // 72Mhz clock (16 / 1 * 18 / 4) - PLLSource::HSI16, - PllRDiv::DIV4, - PllPreDiv::DIV1, - PllMul::MUL18, - Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6) - ); + config.rcc.mux = ClockSrc::PLL; + config.rcc.hsi16 = true; + config.rcc.pll_src = PLLSource::HSI16; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL18, + divp: None, + divq: Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6) + divr: Some(PllRDiv::DIV4), // sysclk 72Mhz clock (16 / 1 * 18 / 4) + }); } #[cfg(any(feature = "stm32l552ze"))]