diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4bbd43c47..b326c26fd 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e853cf944b150898312984d092d63926970c340d" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e853cf944b150898312984d092d63926970c340d", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 1946c5a15..7ca737bf0 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -1,25 +1,56 @@ use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::Sw; -pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Ppre as APBPrescaler}; +pub use crate::pac::rcc::vals::{ + Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Hsikerdiv as HsiKerDiv, Ppre as APBPrescaler, Sw as Sysclk, +}; use crate::pac::{FLASH, RCC}; use crate::time::Hertz; /// HSI speed -pub const HSI_FREQ: Hertz = Hertz(48_000_000); +pub const HSI_FREQ: Hertz = Hertz(16_000_000); -/// System clock mux source -#[derive(Clone, Copy)] -pub enum Sysclk { - HSE(Hertz), - HSI(HSIPrescaler), - LSI, +/// HSE Mode +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1) + Bypass, +} + +/// HSE Configuration +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +/// HSI Configuration +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hsi { + /// Division factor for HSISYS clock. Default is 4. + pub sys_div: HsiSysDiv, + /// Division factor for HSIKER clock. Default is 3. + pub ker_div: HsiKerDiv, } /// Clocks configutation +#[non_exhaustive] pub struct Config { + /// HSI Configuration + pub hsi: Option, + + /// HSE Configuration + pub hse: Option, + + /// System Clock Configuration pub sys: Sysclk, + pub ahb_pre: AHBPrescaler, - pub apb_pre: APBPrescaler, + pub apb1_pre: APBPrescaler, + + /// Low-Speed Clock Configuration pub ls: super::LsConfig, /// Per-peripheral kernel clock selection muxes @@ -30,9 +61,14 @@ impl Default for Config { #[inline] fn default() -> Config { Config { - sys: Sysclk::HSI(HSIPrescaler::DIV1), + hsi: Some(Hsi { + sys_div: HsiSysDiv::DIV4, + ker_div: HsiKerDiv::DIV3, + }), + hse: None, + sys: Sysclk::HSISYS, ahb_pre: AHBPrescaler::DIV1, - apb_pre: APBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, ls: Default::default(), mux: Default::default(), } @@ -40,111 +76,109 @@ impl Default for Config { } pub(crate) unsafe fn init(config: Config) { - let (sys_clk, sw) = match config.sys { - Sysclk::HSI(div) => { - // Enable HSI - RCC.cr().write(|w| { - w.set_hsidiv(div); - w.set_hsion(true) + // Configure HSI + let (hsi, hsisys, hsiker) = match config.hsi { + None => { + RCC.cr().modify(|w| w.set_hsion(false)); + (None, None, None) + } + Some(hsi) => { + RCC.cr().modify(|w| { + w.set_hsidiv(hsi.sys_div); + w.set_hsikerdiv(hsi.ker_div); + w.set_hsion(true); }); while !RCC.cr().read().hsirdy() {} - - (HSI_FREQ / div, Sw::HSI) - } - Sysclk::HSE(freq) => { - // Enable HSE - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - - (freq, Sw::HSE) - } - Sysclk::LSI => { - // Enable LSI - RCC.csr2().write(|w| w.set_lsion(true)); - while !RCC.csr2().read().lsirdy() {} - (super::LSI_FREQ, Sw::LSI) + ( + Some(HSI_FREQ), + Some(HSI_FREQ / hsi.sys_div), + Some(HSI_FREQ / hsi.ker_div), + ) } }; + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } + + RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } + }; + + let sys = match config.sys { + Sysclk::HSISYS => unwrap!(hsisys), + Sysclk::HSE => unwrap!(hse), + _ => unreachable!(), + }; + + assert!(max::SYSCLK.contains(&sys)); + + // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. + let hclk = sys / config.ahb_pre; + assert!(max::HCLK.contains(&hclk)); + + let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + assert!(max::PCLK.contains(&pclk1)); + + let latency = match hclk.0 { + ..=24_000_000 => Latency::WS0, + _ => Latency::WS1, + }; + + // Configure flash read access latency based on voltage scale and frequency + FLASH.acr().modify(|w| { + w.set_latency(latency); + }); + + // Spin until the effective flash latency is set. + while FLASH.acr().read().latency() != latency {} + + // Now that boost mode and flash read access latency are configured, set up SYSCLK + RCC.cfgr().modify(|w| { + w.set_sw(config.sys); + w.set_hpre(config.ahb_pre); + w.set_ppre(config.apb1_pre); + }); + let rtc = config.ls.init(); - // Determine the flash latency implied by the target clock speed - // RM0454 § 3.3.4: - let target_flash_latency = if sys_clk <= Hertz(24_000_000) { - Latency::WS0 - } else { - Latency::WS1 - }; - - // Increase the number of cycles we wait for flash if the new value is higher - // There's no harm in waiting a little too much before the clock change, but we'll - // crash immediately if we don't wait enough after the clock change - let mut set_flash_latency_after = false; - FLASH.acr().modify(|w| { - // Is the current flash latency less than what we need at the new SYSCLK? - if w.latency().to_bits() <= target_flash_latency.to_bits() { - // We must increase the number of wait states now - w.set_latency(target_flash_latency) - } else { - // We may decrease the number of wait states later - set_flash_latency_after = true; - } - - // RM0490 § 3.3.4: - // > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register - // > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the - // > Flash memory. - // - // Enable flash prefetching if we have at least one wait state, and disable it otherwise. - w.set_prften(target_flash_latency.to_bits() > 0); - }); - - if !set_flash_latency_after { - // Spin until the effective flash latency is compatible with the clock change - while FLASH.acr().read().latency() < target_flash_latency {} - } - - // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once - RCC.cfgr().modify(|w| { - w.set_sw(sw); - w.set_hpre(config.ahb_pre); - w.set_ppre(config.apb_pre); - }); - // Spin until the SYSCLK changes have taken effect - loop { - let cfgr = RCC.cfgr().read(); - if cfgr.sw() == sw && cfgr.hpre() == config.ahb_pre && cfgr.ppre() == config.apb_pre { - break; - } - } - - // Set the flash latency to require fewer wait states - if set_flash_latency_after { - FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); - } - - let ahb_freq = sys_clk / config.ahb_pre; - - let (apb_freq, apb_tim_freq) = match config.apb_pre { - APBPrescaler::DIV1 => (ahb_freq, ahb_freq), - pre => { - let freq = ahb_freq / pre; - (freq, freq * 2u32) - } - }; - config.mux.init(); - // without this, the ringbuffered uart test fails. - cortex_m::asm::dsb(); - set_clocks!( - hsi: None, - lse: None, - sys: Some(sys_clk), - hclk1: Some(ahb_freq), - pclk1: Some(apb_freq), - pclk1_tim: Some(apb_tim_freq), + sys: Some(sys), + hclk1: Some(hclk), + pclk1: Some(pclk1), + pclk1_tim: Some(pclk1_tim), + hsi: hsi, + hsiker: hsiker, + hse: hse, rtc: rtc, + + // TODO + lsi: None, + lse: None, ); } + +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(48_000_000); + pub(crate) const HSE_BYP: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + pub(crate) const PCLK: RangeInclusive = Hertz(8)..=Hertz(48_000_000); + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(48_000_000); +} diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 5cfe9953b..ea4422ccc 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -1,7 +1,8 @@ use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{self, Sw}; +pub use crate::pac::pwr::vals::Vos as VoltageRange; pub use crate::pac::rcc::vals::{ - Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler, + Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, + Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; use crate::pac::{FLASH, PWR, RCC}; use crate::time::Hertz; @@ -9,6 +10,7 @@ use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); +/// HSE Mode #[derive(Clone, Copy, Eq, PartialEq)] pub enum HseMode { /// crystal/ceramic oscillator (HSEBYP=0) @@ -17,69 +19,71 @@ pub enum HseMode { Bypass, } -/// System clock mux source -#[derive(Clone, Copy)] -pub enum Sysclk { - HSE(Hertz, HseMode), - HSI(HSIPrescaler), - PLL(PllConfig), - LSI, -} - -/// The PLL configuration. -/// -/// * `VCOCLK = source / m * n` -/// * `PLLRCLK = VCOCLK / r` -/// * `PLLQCLK = VCOCLK / q` -/// * `PLLPCLK = VCOCLK / p` -#[derive(Clone, Copy)] -pub struct PllConfig { - /// The source from which the PLL receives a clock signal - pub source: PllSource, - /// The initial divisor of that clock signal - pub m: Pllm, - /// The PLL VCO multiplier, which must be in the range `8..=86`. - pub n: Plln, - /// The final divisor for `PLLRCLK` output which drives the system clock - pub r: Pllr, - - /// The divisor for the `PLLQCLK` output, if desired - pub q: Option, - - /// The divisor for the `PLLPCLK` output, if desired - pub p: Option, -} - -impl Default for PllConfig { - #[inline] - fn default() -> PllConfig { - // HSI / 1 * 8 / 2 = 64 MHz - PllConfig { - source: PllSource::HSI, - m: Pllm::DIV1, - n: Plln::MUL8, - r: Pllr::DIV2, - q: None, - p: None, - } - } -} - +/// HSE Configuration #[derive(Clone, Copy, Eq, PartialEq)] -pub enum PllSource { - HSI, - HSE(Hertz, HseMode), +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +/// PLL Configuration +/// +/// Use this struct to configure the PLL source, input frequency, multiplication factor, and output +/// dividers. Be sure to keep check the datasheet for your specific part for the appropriate +/// frequency ranges for each of these settings. +pub struct Pll { + /// PLL Source clock selection. + pub source: PllSource, + + /// PLL pre-divider + pub prediv: PllPreDiv, + + /// PLL multiplication factor for VCO + pub mul: PllMul, + + /// PLL division factor for P clock (ADC Clock) + pub divp: Option, + + /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI) + pub divq: Option, + + /// PLL division factor for R clock (SYSCLK) + pub divr: Option, } /// Clocks configutation +#[non_exhaustive] pub struct Config { + /// HSI Enable + pub hsi: bool, + + /// HSE Configuration + pub hse: Option, + + /// System Clock Configuration pub sys: Sysclk, - pub ahb_pre: AHBPrescaler, - pub apb_pre: APBPrescaler, - pub low_power_run: bool, - pub ls: super::LsConfig, + + /// HSI48 Configuration #[cfg(crs)] pub hsi48: Option, + + /// PLL Configuration + pub pll: Option, + + /// If PLL is requested as the main clock source in the `sys` field then the PLL configuration + /// MUST turn on the PLLR output. + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + + /// Low-Speed Clock Configuration + pub ls: super::LsConfig, + + pub low_power_run: bool, + + pub voltage_range: VoltageRange, + /// Per-peripheral kernel clock selection muxes pub mux: super::mux::ClockMux, } @@ -88,248 +92,218 @@ impl Default for Config { #[inline] fn default() -> Config { Config { - sys: Sysclk::HSI(HSIPrescaler::DIV1), - ahb_pre: AHBPrescaler::DIV1, - apb_pre: APBPrescaler::DIV1, - low_power_run: false, - ls: Default::default(), + hsi: true, + hse: None, + sys: Sysclk::HSI, #[cfg(crs)] hsi48: Some(Default::default()), + pll: None, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + low_power_run: false, + ls: Default::default(), + voltage_range: VoltageRange::RANGE1, mux: Default::default(), } } } -impl PllConfig { - pub(crate) fn init(self) -> (Hertz, Option, Option) { - let (src, input_freq) = match self.source { - PllSource::HSI => (vals::Pllsrc::HSI, HSI_FREQ), - PllSource::HSE(freq, _) => (vals::Pllsrc::HSE, freq), - }; - - let m_freq = input_freq / self.m; - // RM0454 § 5.4.4: - // > Caution: The software must set these bits so that the PLL input frequency after the - // > /M divider is between 2.66 and 16 MHz. - debug_assert!(m_freq.0 >= 2_660_000 && m_freq.0 <= 16_000_000); - - let n_freq = m_freq * self.n as u32; - // RM0454 § 5.4.4: - // > Caution: The software must set these bits so that the VCO output frequency is between - // > 64 and 344 MHz. - debug_assert!(n_freq.0 >= 64_000_000 && n_freq.0 <= 344_000_000); - - let r_freq = n_freq / self.r; - // RM0454 § 5.4.4: - // > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock. - debug_assert!(r_freq.0 <= 64_000_000); - - let q_freq = self.q.map(|q| n_freq / q); - let p_freq = self.p.map(|p| n_freq / p); - - // RM0454 § 5.2.3: - // > To modify the PLL configuration, proceed as follows: - // > 1. Disable the PLL by setting PLLON to 0 in Clock control register (RCC_CR). - RCC.cr().modify(|w| w.set_pllon(false)); - - // > 2. Wait until PLLRDY is cleared. The PLL is now fully stopped. - while RCC.cr().read().pllrdy() {} - - // > 3. Change the desired parameter. - // Enable whichever clock source we're using, and wait for it to become ready - match self.source { - PllSource::HSI => { - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - } - PllSource::HSE(_, mode) => { - RCC.cr().write(|w| { - w.set_hsebyp(mode != HseMode::Oscillator); - w.set_hseon(true); - }); - while !RCC.cr().read().hserdy() {} - } - } - - // Configure PLLCFGR - RCC.pllcfgr().modify(|w| { - w.set_pllr(self.r); - w.set_pllren(false); - w.set_pllq(self.q.unwrap_or(Pllq::DIV2)); - w.set_pllqen(false); - w.set_pllp(self.p.unwrap_or(Pllp::DIV2)); - w.set_pllpen(false); - w.set_plln(self.n); - w.set_pllm(self.m); - w.set_pllsrc(src) - }); - - // > 4. Enable the PLL again by setting PLLON to 1. - RCC.cr().modify(|w| w.set_pllon(true)); - - // Wait for the PLL to become ready - while !RCC.cr().read().pllrdy() {} - - // > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL - // > configuration register (RCC_PLLCFGR). - RCC.pllcfgr().modify(|w| { - // We'll use R for system clock, so enable that unconditionally - w.set_pllren(true); - - // We may also use Q or P - w.set_pllqen(self.q.is_some()); - w.set_pllpen(self.p.is_some()); - }); - - (r_freq, q_freq, p_freq) - } +#[derive(Default)] +pub struct PllFreq { + pub pll_p: Option, + pub pll_q: Option, + pub pll_r: Option, } pub(crate) unsafe fn init(config: Config) { - let mut pll1_q_freq = None; - let mut pll1_p_freq = None; - - let (sys_clk, sw) = match config.sys { - Sysclk::HSI(div) => { - // Enable HSI - RCC.cr().write(|w| { - w.set_hsidiv(div); - w.set_hsion(true) - }); + // Configure HSI + let hsi = match config.hsi { + false => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + true => { + RCC.cr().modify(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {} - - (HSI_FREQ / div, Sw::HSI) - } - Sysclk::HSE(freq, mode) => { - // Enable HSE - RCC.cr().write(|w| { - w.set_hseon(true); - w.set_hsebyp(mode != HseMode::Oscillator); - }); - while !RCC.cr().read().hserdy() {} - - (freq, Sw::HSE) - } - Sysclk::PLL(pll) => { - let (r_freq, q_freq, p_freq) = pll.init(); - - pll1_q_freq = q_freq; - pll1_p_freq = p_freq; - - (r_freq, Sw::PLL1_R) - } - Sysclk::LSI => { - // Enable LSI - RCC.csr().write(|w| w.set_lsion(true)); - while !RCC.csr().read().lsirdy() {} - (super::LSI_FREQ, Sw::LSI) + Some(HSI_FREQ) } }; - // Determine the flash latency implied by the target clock speed - // RM0454 § 3.3.4: - let target_flash_latency = if sys_clk.0 <= 24_000_000 { - Latency::WS0 - } else if sys_clk.0 <= 48_000_000 { - Latency::WS1 - } else { - Latency::WS2 - }; - - // Increase the number of cycles we wait for flash if the new value is higher - // There's no harm in waiting a little too much before the clock change, but we'll - // crash immediately if we don't wait enough after the clock change - let mut set_flash_latency_after = false; - FLASH.acr().modify(|w| { - // Is the current flash latency less than what we need at the new SYSCLK? - if w.latency().to_bits() <= target_flash_latency.to_bits() { - // We must increase the number of wait states now - w.set_latency(target_flash_latency) - } else { - // We may decrease the number of wait states later - set_flash_latency_after = true; + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None } - - // RM0454 § 3.3.5: - // > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register - // > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the - // > Flash memory. - // - // Enable flash prefetching if we have at least one wait state, and disable it otherwise. - w.set_prften(target_flash_latency.to_bits() > 0); - }); - - if !set_flash_latency_after { - // Spin until the effective flash latency is compatible with the clock change - while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {} - } - - // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once - let (sw, hpre, ppre) = (sw.into(), config.ahb_pre, config.apb_pre); - RCC.cfgr().modify(|w| { - w.set_sw(sw); - w.set_hpre(hpre); - w.set_ppre(ppre); - }); - - if set_flash_latency_after { - // We can make the flash require fewer wait states - // Spin until the SYSCLK changes have taken effect - loop { - let cfgr = RCC.cfgr().read(); - if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre { - break; + Some(hse) => { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), } - } - // Set the flash latency to require fewer wait states - FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); - } - - let ahb_freq = sys_clk / config.ahb_pre; - - let (apb_freq, apb_tim_freq) = match config.apb_pre { - APBPrescaler::DIV1 => (ahb_freq, ahb_freq), - pre => { - let freq = ahb_freq / pre; - (freq, freq * 2u32) + RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) } }; + // Configure HSI48 if required + #[cfg(crs)] + let hsi48 = config.hsi48.map(super::init_hsi48); + + let pll = config + .pll + .map(|pll_config| { + let src_freq = match pll_config.source { + PllSource::HSI => unwrap!(hsi), + PllSource::HSE => unwrap!(hse), + _ => unreachable!(), + }; + + // Disable PLL before configuration + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} + + let in_freq = src_freq / pll_config.prediv; + assert!(max::PLL_IN.contains(&in_freq)); + let internal_freq = in_freq * pll_config.mul; + + assert!(max::PLL_VCO.contains(&internal_freq)); + + RCC.pllcfgr().write(|w| { + w.set_plln(pll_config.mul); + w.set_pllm(pll_config.prediv); + w.set_pllsrc(pll_config.source.into()); + }); + + let pll_p_freq = pll_config.divp.map(|div_p| { + RCC.pllcfgr().modify(|w| { + w.set_pllp(div_p); + w.set_pllpen(true); + }); + let freq = internal_freq / div_p; + assert!(max::PLL_P.contains(&freq)); + freq + }); + + let pll_q_freq = pll_config.divq.map(|div_q| { + RCC.pllcfgr().modify(|w| { + w.set_pllq(div_q); + w.set_pllqen(true); + }); + let freq = internal_freq / div_q; + assert!(max::PLL_Q.contains(&freq)); + freq + }); + + let pll_r_freq = pll_config.divr.map(|div_r| { + RCC.pllcfgr().modify(|w| { + w.set_pllr(div_r); + w.set_pllren(true); + }); + let freq = internal_freq / div_r; + assert!(max::PLL_R.contains(&freq)); + freq + }); + + // Enable the PLL + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + + PllFreq { + pll_p: pll_p_freq, + pll_q: pll_q_freq, + pll_r: pll_r_freq, + } + }) + .unwrap_or_default(); + + let sys = match config.sys { + Sysclk::HSI => unwrap!(hsi), + Sysclk::HSE => unwrap!(hse), + Sysclk::PLL1_R => unwrap!(pll.pll_r), + _ => unreachable!(), + }; + + assert!(max::SYSCLK.contains(&sys)); + + // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. + let hclk = sys / config.ahb_pre; + assert!(max::HCLK.contains(&hclk)); + + let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + assert!(max::PCLK.contains(&pclk1)); + + let latency = match (config.voltage_range, hclk.0) { + (VoltageRange::RANGE1, ..=24_000_000) => Latency::WS0, + (VoltageRange::RANGE1, ..=48_000_000) => Latency::WS1, + (VoltageRange::RANGE1, _) => Latency::WS2, + (VoltageRange::RANGE2, ..=8_000_000) => Latency::WS0, + (VoltageRange::RANGE2, ..=16_000_000) => Latency::WS1, + (VoltageRange::RANGE2, _) => Latency::WS2, + _ => unreachable!(), + }; + + // Configure flash read access latency based on voltage scale and frequency (RM0444 3.3.4) + FLASH.acr().modify(|w| { + w.set_latency(latency); + }); + + // Spin until the effective flash latency is set. + while FLASH.acr().read().latency() != latency {} + + // Now that boost mode and flash read access latency are configured, set up SYSCLK + RCC.cfgr().modify(|w| { + w.set_sw(config.sys); + w.set_hpre(config.ahb_pre); + w.set_ppre(config.apb1_pre); + }); + if config.low_power_run { - assert!(sys_clk.0 <= 2_000_000); + assert!(sys <= Hertz(2_000_000)); PWR.cr1().modify(|w| w.set_lpr(true)); } let rtc = config.ls.init(); - let lse_freq = config.ls.lse.map(|lse| lse.frequency); - - let hsi_freq = (sw == Sw::HSI).then_some(HSI_FREQ); - let hsi_div_8_freq = hsi_freq.map(|f| f / 8u32); - let lsi_freq = (sw == Sw::LSI).then_some(super::LSI_FREQ); - let hse_freq = (sw == Sw::HSE).then_some(sys_clk); - - #[cfg(crs)] - let hsi48 = config.hsi48.map(super::init_hsi48); - #[cfg(not(crs))] - let hsi48: Option = None; config.mux.init(); set_clocks!( - sys: Some(sys_clk), - hclk1: Some(ahb_freq), - pclk1: Some(apb_freq), - pclk1_tim: Some(apb_tim_freq), - hsi: hsi_freq, + sys: Some(sys), + hclk1: Some(hclk), + pclk1: Some(pclk1), + pclk1_tim: Some(pclk1_tim), + pll1_p: pll.pll_p, + pll1_q: pll.pll_q, + pll1_r: pll.pll_r, + hsi: hsi, + hse: hse, + #[cfg(crs)] hsi48: hsi48, - hsi_div_8: hsi_div_8_freq, - hse: hse_freq, - lse: lse_freq, - lsi: lsi_freq, - pll1_q: pll1_q_freq, - pll1_p: pll1_p_freq, rtc: rtc, - hsi_div_488: None, + hsi_div_8: hsi.map(|h| h / 8u32), + hsi_div_488: hsi.map(|h| h / 488u32), + + // TODO + lsi: None, + lse: None, ); } + +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(48_000_000); + pub(crate) const HSE_BYP: RangeInclusive = Hertz(0)..=Hertz(48_000_000); + pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(64_000_000); + pub(crate) const PCLK: RangeInclusive = Hertz(8)..=Hertz(64_000_000); + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(64_000_000); + pub(crate) const PLL_IN: RangeInclusive = Hertz(2_660_000)..=Hertz(16_000_000); + pub(crate) const PLL_VCO: RangeInclusive = Hertz(96_000_000)..=Hertz(344_000_000); + pub(crate) const PLL_P: RangeInclusive = Hertz(3_090_000)..=Hertz(122_000_000); + pub(crate) const PLL_Q: RangeInclusive = Hertz(12_000_000)..=Hertz(128_000_000); + pub(crate) const PLL_R: RangeInclusive = Hertz(12_000_000)..=Hertz(64_000_000); +} diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 79bdbeb77..cd2d2a8a2 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,12 +1,9 @@ -use stm32_metapac::flash::vals::Latency; -use stm32_metapac::rcc::vals::Sw; -use stm32_metapac::FLASH; - +use crate::pac::flash::vals::Latency; pub use crate::pac::rcc::vals::{ - Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, - Ppre as APBPrescaler, Sw as Sysclk, + Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, + Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; -use crate::pac::{PWR, RCC}; +use crate::pac::{FLASH, PWR, RCC}; use crate::time::Hertz; /// HSI speed @@ -37,7 +34,7 @@ pub struct Hse { /// frequency ranges for each of these settings. pub struct Pll { /// PLL Source clock selection. - pub source: Pllsrc, + pub source: PllSource, /// PLL pre-divider pub prediv: PllPreDiv, @@ -73,7 +70,7 @@ pub struct Config { /// PLL Configuration pub pll: Option, - /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration + /// If PLL is requested as the main clock source in the `sys` field then the PLL configuration /// MUST turn on the PLLR output. pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, @@ -112,6 +109,7 @@ impl Default for Config { } } +#[derive(Default)] pub struct PllFreq { pub pll_p: Option, pub pll_q: Option, @@ -154,91 +152,91 @@ pub(crate) unsafe fn init(config: Config) { // Configure HSI48 if required let hsi48 = config.hsi48.map(super::init_hsi48); - let pll_freq = config.pll.map(|pll_config| { - let src_freq = match pll_config.source { - Pllsrc::HSI => unwrap!(hsi), - Pllsrc::HSE => unwrap!(hse), - _ => unreachable!(), - }; + let pll = config + .pll + .map(|pll_config| { + let src_freq = match pll_config.source { + PllSource::HSI => unwrap!(hsi), + PllSource::HSE => unwrap!(hse), + _ => unreachable!(), + }; - // Disable PLL before configuration - RCC.cr().modify(|w| w.set_pllon(false)); - while RCC.cr().read().pllrdy() {} + // Disable PLL before configuration + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} - let in_freq = src_freq / pll_config.prediv; - assert!(max::PLL_IN.contains(&in_freq)); - let internal_freq = in_freq * pll_config.mul; + let in_freq = src_freq / pll_config.prediv; + assert!(max::PLL_IN.contains(&in_freq)); + let internal_freq = in_freq * pll_config.mul; - assert!(max::PLL_VCO.contains(&internal_freq)); + assert!(max::PLL_VCO.contains(&internal_freq)); - RCC.pllcfgr().write(|w| { - w.set_plln(pll_config.mul); - w.set_pllm(pll_config.prediv); - w.set_pllsrc(pll_config.source.into()); - }); - - let pll_p_freq = pll_config.divp.map(|div_p| { - RCC.pllcfgr().modify(|w| { - w.set_pllp(div_p); - w.set_pllpen(true); + RCC.pllcfgr().write(|w| { + w.set_plln(pll_config.mul); + w.set_pllm(pll_config.prediv); + w.set_pllsrc(pll_config.source.into()); }); - let freq = internal_freq / div_p; - assert!(max::PCLK.contains(&freq)); - freq - }); - let pll_q_freq = pll_config.divq.map(|div_q| { - RCC.pllcfgr().modify(|w| { - w.set_pllq(div_q); - w.set_pllqen(true); + let pll_p_freq = pll_config.divp.map(|div_p| { + RCC.pllcfgr().modify(|w| { + w.set_pllp(div_p); + w.set_pllpen(true); + }); + let freq = internal_freq / div_p; + assert!(max::PLL_P.contains(&freq)); + freq }); - let freq = internal_freq / div_q; - assert!(max::PCLK.contains(&freq)); - freq - }); - let pll_r_freq = pll_config.divr.map(|div_r| { - RCC.pllcfgr().modify(|w| { - w.set_pllr(div_r); - w.set_pllren(true); + let pll_q_freq = pll_config.divq.map(|div_q| { + RCC.pllcfgr().modify(|w| { + w.set_pllq(div_q); + w.set_pllqen(true); + }); + let freq = internal_freq / div_q; + assert!(max::PLL_Q.contains(&freq)); + freq }); - let freq = internal_freq / div_r; - assert!(max::PCLK.contains(&freq)); - freq - }); - // Enable the PLL - RCC.cr().modify(|w| w.set_pllon(true)); - while !RCC.cr().read().pllrdy() {} + let pll_r_freq = pll_config.divr.map(|div_r| { + RCC.pllcfgr().modify(|w| { + w.set_pllr(div_r); + w.set_pllren(true); + }); + let freq = internal_freq / div_r; + assert!(max::PLL_R.contains(&freq)); + freq + }); - PllFreq { - pll_p: pll_p_freq, - pll_q: pll_q_freq, - pll_r: pll_r_freq, - } - }); + // Enable the PLL + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} - let (sys_clk, sw) = match config.sys { - Sysclk::HSI => (HSI_FREQ, Sw::HSI), - Sysclk::HSE => (unwrap!(hse), Sw::HSE), - Sysclk::PLL1_R => { - assert!(pll_freq.is_some()); - assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); + PllFreq { + pll_p: pll_p_freq, + pll_q: pll_q_freq, + pll_r: pll_r_freq, + } + }) + .unwrap_or_default(); - let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0; - - assert!(max::SYSCLK.contains(&Hertz(freq))); - - (Hertz(freq), Sw::PLL1_R) - } + let sys = match config.sys { + Sysclk::HSI => unwrap!(hsi), + Sysclk::HSE => unwrap!(hse), + Sysclk::PLL1_R => unwrap!(pll.pll_r), _ => unreachable!(), }; - // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. - let hclk = sys_clk / config.ahb_pre; + assert!(max::SYSCLK.contains(&sys)); + // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. + let hclk = sys / config.ahb_pre; assert!(max::HCLK.contains(&hclk)); + let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); + assert!(max::PCLK.contains(&pclk2)); + assert!(max::PCLK.contains(&pclk2)); + // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) if config.boost { // RM0440 p235 @@ -253,23 +251,28 @@ pub(crate) unsafe fn init(config: Config) { // 4. Configure and switch to new frequency } + let latency = match (config.boost, hclk.0) { + (true, ..=34_000_000) => Latency::WS0, + (true, ..=68_000_000) => Latency::WS1, + (true, ..=102_000_000) => Latency::WS2, + (true, ..=136_000_000) => Latency::WS3, + (true, _) => Latency::WS4, + + (false, ..=36_000_000) => Latency::WS0, + (false, ..=60_000_000) => Latency::WS1, + (false, ..=90_000_000) => Latency::WS2, + (false, ..=120_000_000) => Latency::WS3, + (false, _) => Latency::WS4, + }; + // Configure flash read access latency based on boost mode and frequency (RM0440 p98) FLASH.acr().modify(|w| { - w.set_latency(match (config.boost, hclk.0) { - (true, ..=34_000_000) => Latency::WS0, - (true, ..=68_000_000) => Latency::WS1, - (true, ..=102_000_000) => Latency::WS2, - (true, ..=136_000_000) => Latency::WS3, - (true, _) => Latency::WS4, - - (false, ..=36_000_000) => Latency::WS0, - (false, ..=60_000_000) => Latency::WS1, - (false, ..=90_000_000) => Latency::WS2, - (false, ..=120_000_000) => Latency::WS3, - (false, _) => Latency::WS4, - }) + w.set_latency(latency); }); + // Spin until the effective flash latency is set. + while FLASH.acr().read().latency() != latency {} + if config.boost { // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency. cortex_m::asm::delay(16); @@ -277,17 +280,14 @@ pub(crate) unsafe fn init(config: Config) { // Now that boost mode and flash read access latency are configured, set up SYSCLK RCC.cfgr().modify(|w| { - w.set_sw(sw); + w.set_sw(config.sys); w.set_hpre(config.ahb_pre); w.set_ppre1(config.apb1_pre); w.set_ppre2(config.apb2_pre); }); - let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre); - let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre); - if config.low_power_run { - assert!(sys_clk <= Hertz(2_000_000)); + assert!(sys <= Hertz(2_000_000)); PWR.cr1().modify(|w| w.set_lpr(true)); } @@ -296,17 +296,18 @@ pub(crate) unsafe fn init(config: Config) { config.mux.init(); set_clocks!( - sys: Some(sys_clk), + sys: Some(sys), hclk1: Some(hclk), hclk2: Some(hclk), hclk3: Some(hclk), - pclk1: Some(apb1_freq), - pclk1_tim: Some(apb1_tim_freq), - pclk2: Some(apb2_freq), - pclk2_tim: Some(apb2_tim_freq), - pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p), - pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_q), - pll1_r: pll_freq.as_ref().and_then(|pll| pll.pll_r), + pclk1: Some(pclk1), + pclk1_tim: Some(pclk1_tim), + pclk2: Some(pclk2), + pclk2_tim: Some(pclk2_tim), + pll1_p: pll.pll_p, + pll1_q: pll.pll_q, + pll1_r: pll.pll_r, + hsi: hsi, hse: hse, hsi48: hsi48, rtc: rtc, @@ -342,4 +343,7 @@ mod max { /// PLL VCO (internal) Frequency Range (STM32G474 Datasheet p123, Table 46) pub(crate) const PLL_VCO: RangeInclusive = Hertz(96_000_000)..=Hertz(344_000_000); + pub(crate) const PLL_P: RangeInclusive = Hertz(2_064_500)..=Hertz(170_000_000); + pub(crate) const PLL_Q: RangeInclusive = Hertz(8_000_000)..=Hertz(170_000_000); + pub(crate) const PLL_R: RangeInclusive = Hertz(8_000_000)..=Hertz(170_000_000); } diff --git a/examples/stm32g0/src/bin/hf_timer.rs b/examples/stm32g0/src/bin/hf_timer.rs index 647ff0419..3ea06cdee 100644 --- a/examples/stm32g0/src/bin/hf_timer.rs +++ b/examples/stm32g0/src/bin/hf_timer.rs @@ -16,15 +16,16 @@ async fn main(_spawner: Spawner) { let mut config = PeripheralConfig::default(); { use embassy_stm32::rcc::*; - - config.rcc.sys = Sysclk::PLL(PllConfig { + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { source: PllSource::HSI, - m: Pllm::DIV1, - n: Plln::MUL16, - r: Pllr::DIV4, // CPU clock comes from PLLR (HSI (16MHz) / 1 * 16 / 4 = 64MHz) - q: Some(Pllq::DIV2), // TIM1 or TIM15 can be sourced from PLLQ (HSI (16MHz) / 1 * 16 / 2 = 128MHz) - p: None, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL16, + divp: None, + divq: Some(PllQDiv::DIV2), // 16 / 1 * 16 / 2 = 128 Mhz + divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz }); + config.rcc.sys = Sysclk::PLL1_R; // configure TIM1 mux to select PLLQ as clock source // https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index f81335f93..ae64bc8e4 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { { use embassy_stm32::rcc::*; config.rcc.pll = Some(Pll { - source: Pllsrc::HSI, + source: PllSource::HSI, prediv: PllPreDiv::DIV4, mul: PllMul::MUL85, divp: None, diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs index 93b206de8..4373a89a8 100644 --- a/examples/stm32g4/src/bin/can.rs +++ b/examples/stm32g4/src/bin/can.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { mode: HseMode::Oscillator, }); config.rcc.pll = Some(Pll { - source: Pllsrc::HSE, + source: PllSource::HSE, prediv: PllPreDiv::DIV6, mul: PllMul::MUL85, divp: None, diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 2609abfa2..08ed95b34 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk}; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -11,20 +10,20 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - - config.rcc.hsi = true; - config.rcc.pll = Some(Pll { - source: Pllsrc::HSI, - prediv: PllPreDiv::DIV4, - mul: PllMul::MUL85, - divp: None, - divq: None, - // Main system clock at 170 MHz - divr: Some(PllRDiv::DIV2), - }); - - config.rcc.sys = Sysclk::PLL1_R; - + { + use embassy_stm32::rcc::*; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL85, + divp: None, + divq: None, + // Main system clock at 170 MHz + divr: Some(PllRDiv::DIV2), + }); + config.rcc.sys = Sysclk::PLL1_R; + } let _p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 90caaae14..dc95aa6e5 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { mode: HseMode::Oscillator, }); config.rcc.pll = Some(Pll { - source: Pllsrc::HSE, + source: PllSource::HSE, prediv: PllPreDiv::DIV2, mul: PllMul::MUL72, divp: None, diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index cf3e04a4b..3297ea7e2 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -260,6 +260,30 @@ pub fn config() -> Config { #[allow(unused_mut)] let mut config = Config::default(); + #[cfg(feature = "stm32c031c6")] + { + config.rcc.hsi = Some(Hsi { + sys_div: HsiSysDiv::DIV1, // 48Mhz + ker_div: HsiKerDiv::DIV3, // 16Mhz + }); + config.rcc.sys = Sysclk::HSISYS; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + } + + #[cfg(feature = "stm32g071rb")] + { + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL16, + divp: None, + divq: None, + divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz + }); + config.rcc.sys = Sysclk::PLL1_R; + } #[cfg(feature = "stm32wb55rg")] { config.rcc = embassy_stm32::rcc::WPAN_DEFAULT; @@ -456,7 +480,7 @@ pub fn config() -> Config { mode: HseMode::Oscillator, }); config.rcc.pll = Some(Pll { - source: Pllsrc::HSE, + source: PllSource::HSE, prediv: PllPreDiv::DIV6, mul: PllMul::MUL85, divp: None,