From 7ce3b1938972eaaea0304a4b312706d84b72ac25 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Oct 2023 03:15:26 +0200 Subject: [PATCH 1/4] stm32/rcc: remove unused enum. --- embassy-stm32/src/rcc/h.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 86136d43..5dbcfea9 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -58,15 +58,6 @@ pub struct Hse { pub mode: HseMode, } -#[cfg(stm32h7)] -#[derive(Clone, Copy, Eq, PartialEq)] -pub enum Lse { - /// 32.768 kHz crystal/ceramic oscillator (LSEBYP=0) - Oscillator, - /// external clock input up to 1MHz (LSEBYP=1) - Bypass(Hertz), -} - #[derive(Clone, Copy, Eq, PartialEq)] pub enum Hsi { /// 64Mhz From 361fde35cf37e5c8171ab470449e85ad44da4e52 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Oct 2023 03:16:15 +0200 Subject: [PATCH 2/4] stm32/rcc: wait for mux switch. --- embassy-stm32/src/rcc/l4l5.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rcc/l4l5.rs b/embassy-stm32/src/rcc/l4l5.rs index a10169d6..683b47c0 100644 --- a/embassy-stm32/src/rcc/l4l5.rs +++ b/embassy-stm32/src/rcc/l4l5.rs @@ -241,6 +241,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_ppre1(config.apb1_pre); w.set_ppre2(config.apb2_pre); }); + while RCC.cfgr().read().sws() != config.mux {} let ahb_freq = sys_clk / config.ahb_pre; From 67010d123c874383f48ccd5c1b2287907677e460 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Oct 2023 03:16:36 +0200 Subject: [PATCH 3/4] stm32/rcc: refactor f7. --- embassy-stm32/src/rcc/f7.rs | 553 +++++++++++++------------ examples/stm32f7/src/bin/eth.rs | 22 +- examples/stm32f7/src/bin/hello.rs | 4 +- examples/stm32f7/src/bin/sdmmc.rs | 23 +- examples/stm32f7/src/bin/usb_serial.rs | 25 +- tests/stm32/src/common.rs | 18 +- 6 files changed, 358 insertions(+), 287 deletions(-) diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs index 7c6c150d..a984e4f4 100644 --- a/embassy-stm32/src/rcc/f7.rs +++ b/embassy-stm32/src/rcc/f7.rs @@ -1,5 +1,7 @@ -use crate::pac::pwr::vals::Vos; -use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllp, Pllq, Pllsrc, Ppre, Sw}; +pub use crate::pac::rcc::vals::{ + Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource, + Ppre as APBPrescaler, Sw as Sysclk, +}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -7,299 +9,304 @@ use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); -/// Clocks configuration -#[non_exhaustive] -#[derive(Default)] -pub struct Config { - pub hse: Option, - pub bypass_hse: bool, - pub hclk: Option, - pub sys_ck: Option, - pub pclk1: Option, - pub pclk2: Option, +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1) + Bypass, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +#[derive(Clone, Copy)] +pub struct Pll { + /// PLL pre-divider (DIVM). + pub prediv: PllPreDiv, + + /// PLL multiplication factor. + pub mul: PllMul, + + /// 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, +} + +/// Configuration of the core clocks +#[non_exhaustive] +pub struct Config { + pub hsi: bool, + pub hse: Option, + pub sys: Sysclk, + + pub pll_src: PllSource, + + pub pll: Option, + pub plli2s: Option, + pub pllsai: Option, + + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, - pub pll48: bool, pub ls: super::LsConfig, } -fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bool) -> PllResults { - let sysclk = pllsysclk.unwrap_or(pllsrcclk); - if pllsysclk.is_none() && !pll48clk { - RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8))); +impl Default for Config { + fn default() -> Self { + Self { + hsi: true, + hse: None, + sys: Sysclk::HSI, + pll_src: PllSource::HSI, + pll: None, + plli2s: None, + pllsai: None, - return PllResults { - use_pll: false, - pllsysclk: None, - pll48clk: None, - }; - } - // Input divisor from PLL source clock, must result to frequency in - // the range from 1 to 2 MHz - let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; - let pllm_max = pllsrcclk / 1_000_000; + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, - // Sysclk output divisor must be one of 2, 4, 6 or 8 - let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); - - let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div }; - - // Find the lowest pllm value that minimize the difference between - // target frequency and the real vco_out frequency. - let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { - let vco_in = pllsrcclk / pllm; - let plln = target_freq / vco_in; - target_freq - vco_in * plln - })); - - let vco_in = pllsrcclk / pllm; - assert!((1_000_000..=2_000_000).contains(&vco_in)); - - // Main scaler, must result in >= 100MHz (>= 192MHz for F401) - // and <= 432MHz, min 50, max 432 - let plln = if pll48clk { - // try the different valid pllq according to the valid - // main scaller values, and take the best - let pllq = unwrap!((4..=9).min_by_key(|pllq| { - let plln = 48_000_000 * pllq / vco_in; - let pll48_diff = 48_000_000 - vco_in * plln / pllq; - let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); - (pll48_diff, sysclk_diff) - })); - 48_000_000 * pllq / vco_in - } else { - sysclk * sysclk_div / vco_in - }; - - let pllp = (sysclk_div / 2) - 1; - - let pllq = (vco_in * plln + 47_999_999) / 48_000_000; - let real_pll48clk = vco_in * plln / pllq; - - RCC.pllcfgr().modify(|w| { - w.set_pllm(Pllm::from_bits(pllm as u8)); - w.set_plln(Plln::from_bits(plln as u16)); - w.set_pllp(Pllp::from_bits(pllp as u8)); - w.set_pllq(Pllq::from_bits(pllq as u8)); - w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); - }); - - let real_pllsysclk = vco_in * plln / sysclk_div; - - PllResults { - use_pll: true, - pllsysclk: Some(real_pllsysclk), - pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, + ls: Default::default(), + } } } -fn flash_setup(sysclk: u32) { +pub(crate) unsafe fn init(config: Config) { + // always enable overdrive for now. Make it configurable in the future. + PWR.cr1().modify(|w| w.set_oden(true)); + while !PWR.csr1().read().odrdy() {} + + PWR.cr1().modify(|w| w.set_odswen(true)); + while !PWR.csr1().read().odswrdy() {} + + // 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() {} + Some(HSI_FREQ) + } + }; + + // 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) + } + }; + + // Configure PLLs. + let pll_input = PllInput { + hse, + hsi, + source: config.pll_src, + }; + let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); + let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input); + let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input); + + // Configure sysclk + let sys = match config.sys { + Sysclk::HSI => unwrap!(hsi), + Sysclk::HSE => unwrap!(hse), + Sysclk::PLL1_P => unwrap!(pll.p), + _ => unreachable!(), + }; + + let hclk = sys / config.ahb_pre; + let (pclk1, pclk1_tim) = calc_pclk(hclk, config.apb1_pre); + let (pclk2, pclk2_tim) = calc_pclk(hclk, config.apb2_pre); + + assert!(max::SYSCLK.contains(&sys)); + assert!(max::HCLK.contains(&hclk)); + assert!(max::PCLK1.contains(&pclk1)); + assert!(max::PCLK2.contains(&pclk2)); + + let rtc = config.ls.init(); + + flash_setup(hclk); + + RCC.cfgr().modify(|w| { + w.set_sw(config.sys); + w.set_hpre(config.ahb_pre); + w.set_ppre1(config.apb1_pre); + w.set_ppre2(config.apb2_pre); + }); + while RCC.cfgr().read().sws() != config.sys {} + + set_freqs(Clocks { + sys, + hclk1: hclk, + hclk2: hclk, + hclk3: hclk, + pclk1, + pclk2, + pclk1_tim, + pclk2_tim, + rtc, + pll1_q: pll.q, + }); +} + +struct PllInput { + source: PllSource, + hsi: Option, + hse: Option, +} + +#[derive(Default)] +#[allow(unused)] +struct PllOutput { + p: Option, + q: Option, + r: Option, +} + +#[derive(PartialEq, Eq, Clone, Copy)] +enum PllInstance { + Pll, + Plli2s, + Pllsai, +} + +fn pll_enable(instance: PllInstance, enabled: bool) { + match instance { + PllInstance::Pll => { + RCC.cr().modify(|w| w.set_pllon(enabled)); + while RCC.cr().read().pllrdy() != enabled {} + } + PllInstance::Plli2s => { + RCC.cr().modify(|w| w.set_plli2son(enabled)); + while RCC.cr().read().plli2srdy() != enabled {} + } + PllInstance::Pllsai => { + RCC.cr().modify(|w| w.set_pllsaion(enabled)); + while RCC.cr().read().pllsairdy() != enabled {} + } + } +} + +fn init_pll(instance: PllInstance, config: Option, input: &PllInput) -> PllOutput { + // Disable PLL + pll_enable(instance, false); + + let Some(pll) = config else { return PllOutput::default() }; + + let pll_src = match input.source { + PllSource::HSE => input.hse, + PllSource::HSI => input.hsi, + }; + + let pll_src = pll_src.unwrap(); + + let in_freq = pll_src / pll.prediv; + assert!(max::PLL_IN.contains(&in_freq)); + let vco_freq = in_freq * pll.mul; + assert!(max::PLL_VCO.contains(&vco_freq)); + + 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); + + macro_rules! write_fields { + ($w:ident) => { + $w.set_plln(pll.mul); + if let Some(divp) = pll.divp { + $w.set_pllp(divp); + } + if let Some(divq) = pll.divq { + $w.set_pllq(divq); + } + if let Some(divr) = pll.divr { + $w.set_pllr(divr); + } + }; + } + + match instance { + PllInstance::Pll => RCC.pllcfgr().write(|w| { + w.set_pllm(pll.prediv); + w.set_pllsrc(input.source); + write_fields!(w); + }), + PllInstance::Plli2s => RCC.plli2scfgr().write(|w| { + write_fields!(w); + }), + PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| { + write_fields!(w); + }), + } + + // Enable PLL + pll_enable(instance, true); + + PllOutput { p, q, r } +} + +fn flash_setup(clk: Hertz) { use crate::pac::flash::vals::Latency; // Be conservative with voltage ranges const FLASH_LATENCY_STEP: u32 = 30_000_000; - critical_section::with(|_| { - FLASH - .acr() - .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); + let latency = (clk.0 - 1) / FLASH_LATENCY_STEP; + debug!("flash: latency={}", latency); + + let latency = Latency::from_bits(latency as u8); + FLASH.acr().write(|w| { + w.set_latency(latency); }); + while FLASH.acr().read().latency() != latency {} } -pub(crate) unsafe fn init(config: Config) { - if let Some(hse) = config.hse { - if config.bypass_hse { - assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0)); - } else { - assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0)); - } - } - - let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0); - let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); - let sysclk_on_pll = sysclk != pllsrcclk; - - assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk)); - - let plls = setup_pll( - pllsrcclk, - config.hse.is_some(), - if sysclk_on_pll { Some(sysclk) } else { None }, - config.pll48, - ); - - if config.pll48 { - let freq = unwrap!(plls.pll48clk); - - assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32); - } - - let sysclk = if sysclk_on_pll { unwrap!(plls.pllsysclk) } else { sysclk }; - - // AHB prescaler - let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk); - let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { - 0 => unreachable!(), - 1 => (Hpre::DIV1, 1), - 2 => (Hpre::DIV2, 2), - 3..=5 => (Hpre::DIV4, 4), - 6..=11 => (Hpre::DIV8, 8), - 12..=39 => (Hpre::DIV16, 16), - 40..=95 => (Hpre::DIV64, 64), - 96..=191 => (Hpre::DIV128, 128), - 192..=383 => (Hpre::DIV256, 256), - _ => (Hpre::DIV512, 512), - }; - - // Calculate real AHB clock - let hclk = sysclk / hpre_div; - - assert!(hclk <= max::HCLK_MAX); - - let pclk1 = config - .pclk1 - .map(|p| p.0) - .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); - - let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { - 0 => unreachable!(), - 1 => (0b000, 1), - 2 => (0b100, 2), - 3..=5 => (0b101, 4), - 6..=11 => (0b110, 8), - _ => (0b111, 16), - }; - let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; - - // Calculate real APB1 clock - let pclk1 = hclk / ppre1; - assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1)); - - let pclk2 = config - .pclk2 - .map(|p| p.0) - .unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk)); - let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { - 0 => unreachable!(), - 1 => (0b000, 1), - 2 => (0b100, 2), - 3..=5 => (0b101, 4), - 6..=11 => (0b110, 8), - _ => (0b111, 16), - }; - let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; - - // Calculate real APB2 clock - let pclk2 = hclk / ppre2; - assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2)); - - flash_setup(sysclk); - - if config.hse.is_some() { - RCC.cr().modify(|w| { - w.set_hsebyp(config.bypass_hse); - w.set_hseon(true); - }); - while !RCC.cr().read().hserdy() {} - } - - if plls.use_pll { - RCC.cr().modify(|w| w.set_pllon(false)); - - // setup VOSScale - let vos_scale = if sysclk <= 144_000_000 { - 3 - } else if sysclk <= 168_000_000 { - 2 - } else { - 1 - }; - PWR.cr1().modify(|w| { - w.set_vos(match vos_scale { - 3 => Vos::SCALE3, - 2 => Vos::SCALE2, - 1 => Vos::SCALE1, - _ => panic!("Invalid VOS Scale."), - }) - }); - - RCC.cr().modify(|w| w.set_pllon(true)); - - if hclk > max::HCLK_OVERDRIVE_FREQUENCY { - PWR.cr1().modify(|w| w.set_oden(true)); - while !PWR.csr1().read().odrdy() {} - - PWR.cr1().modify(|w| w.set_odswen(true)); - while !PWR.csr1().read().odswrdy() {} - } - - while !RCC.cr().read().pllrdy() {} - } - - RCC.cfgr().modify(|w| { - w.set_ppre2(Ppre::from_bits(ppre2_bits)); - w.set_ppre1(Ppre::from_bits(ppre1_bits)); - w.set_hpre(hpre_bits); - }); - - // Wait for the new prescalers to kick in - // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" - cortex_m::asm::delay(16); - - RCC.cfgr().modify(|w| { - w.set_sw(if sysclk_on_pll { - Sw::PLL1_P - } else if config.hse.is_some() { - Sw::HSE - } else { - Sw::HSI - }) - }); - - let rtc = config.ls.init(); - - set_freqs(Clocks { - sys: Hertz(sysclk), - pclk1: Hertz(pclk1), - pclk2: Hertz(pclk2), - - pclk1_tim: Hertz(pclk1 * timer_mul1), - pclk2_tim: Hertz(pclk2 * timer_mul2), - - hclk1: Hertz(hclk), - hclk2: Hertz(hclk), - hclk3: Hertz(hclk), - - pll1_q: plls.pll48clk.map(Hertz), - - rtc, - }); -} - -struct PllResults { - use_pll: bool, - pllsysclk: Option, - pll48clk: Option, +fn calc_pclk(hclk: Hertz, ppre: D) -> (Hertz, Hertz) +where + Hertz: core::ops::Div, +{ + let pclk = hclk / ppre; + let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 }; + (pclk, pclk_tim) } mod max { - pub(crate) const HSE_OSC_MIN: u32 = 4_000_000; - pub(crate) const HSE_OSC_MAX: u32 = 26_000_000; - pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000; - pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000; + use core::ops::RangeInclusive; - pub(crate) const HCLK_MAX: u32 = 216_000_000; - pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000; + use crate::time::Hertz; - pub(crate) const SYSCLK_MIN: u32 = 12_500_000; - pub(crate) const SYSCLK_MAX: u32 = 216_000_000; + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(26_000_000); + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(50_000_000); - pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN; - pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4; + pub(crate) const SYSCLK: RangeInclusive = Hertz(12_500_000)..=Hertz(216_000_000); + pub(crate) const HCLK: RangeInclusive = Hertz(12_500_000)..=Hertz(216_000_000); + pub(crate) const PCLK1: RangeInclusive = Hertz(12_500_000)..=Hertz(216_000_000 / 4); + pub(crate) const PCLK2: RangeInclusive = Hertz(12_500_000)..=Hertz(216_000_000 / 2); - pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN; - pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; - - // USB specification allows +-0.25% - pub(crate) const PLL_48_CLK: u32 = 48_000_000; - pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(2_100_000); + pub(crate) const PLL_VCO: RangeInclusive = Hertz(100_000_000)..=Hertz(432_000_000); } diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index d50473b9..7c6c419a 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::time::mhz; +use embassy_stm32::time::Hertz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::Timer; use embedded_io_async::Write; @@ -33,7 +33,25 @@ async fn net_task(stack: &'static Stack) -> ! { #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(200)); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL216, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/hello.rs b/examples/stm32f7/src/bin/hello.rs index 27ee83aa..a2a28711 100644 --- a/examples/stm32f7/src/bin/hello.rs +++ b/examples/stm32f7/src/bin/hello.rs @@ -4,15 +4,13 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::time::Hertz; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { - let mut config = Config::default(); - config.rcc.sys_ck = Some(Hertz(84_000_000)); + let config = Config::default(); let _p = embassy_stm32::init(config); loop { diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 9d43892a..430aa781 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; -use embassy_stm32::time::mhz; +use embassy_stm32::time::{mhz, Hertz}; use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; use {defmt_rtt as _, panic_probe as _}; @@ -16,8 +16,25 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(200)); - config.rcc.pll48 = true; + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL216, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz + divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index a2c76178..2f832c23 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -4,7 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::time::mhz; +use embassy_stm32::time::Hertz; 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,10 +22,25 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut config = Config::default(); - config.rcc.hse = Some(mhz(8)); - config.rcc.pll48 = true; - config.rcc.sys_ck = Some(mhz(200)); - + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL216, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz + divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } let p = embassy_stm32::init(config); // Create the driver, from the HAL. diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 9f1307ce..7bc74141 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -233,7 +233,23 @@ pub fn config() -> Config { #[cfg(feature = "stm32f767zi")] { - config.rcc.sys_ck = Some(Hertz(200_000_000)); + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL216, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz. + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; } #[cfg(feature = "stm32h563zi")] From f20f170b1fa97a86e9d9258ac5cea248203580fb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Oct 2023 04:31:53 +0200 Subject: [PATCH 4/4] stm32/rcc: refactor and unify f4 into f7. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rcc/f4.rs | 400 ----------------------- embassy-stm32/src/rcc/{f7.rs => f4f7.rs} | 67 ++++ embassy-stm32/src/rcc/mod.rs | 3 +- examples/stm32f4/src/bin/eth.rs | 22 +- examples/stm32f4/src/bin/hello.rs | 4 +- examples/stm32f4/src/bin/sdmmc.rs | 23 +- examples/stm32f4/src/bin/usb_ethernet.rs | 24 +- examples/stm32f4/src/bin/usb_serial.rs | 24 +- tests/stm32/src/common.rs | 22 +- 10 files changed, 168 insertions(+), 425 deletions(-) delete mode 100644 embassy-stm32/src/rcc/f4.rs rename embassy-stm32/src/rcc/{f7.rs => f4f7.rs} (71%) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ab7b9221..3b9220bc 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6f7449303bf8af60a63704d35df9af46006c6148" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6f7449303bf8af60a63704d35df9af46006c6148", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5b04234fbe61ea875f1a904cd5f68795daaeb526", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs deleted file mode 100644 index eb51dc89..00000000 --- a/embassy-stm32/src/rcc/f4.rs +++ /dev/null @@ -1,400 +0,0 @@ -use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllq, Pllr, Ppre, Sw}; -use crate::pac::{FLASH, PWR, RCC}; -use crate::rcc::{set_freqs, Clocks}; -use crate::time::Hertz; - -/// HSI speed -pub const HSI_FREQ: Hertz = Hertz(16_000_000); - -/// Clocks configuration -#[non_exhaustive] -#[derive(Default)] -pub struct Config { - pub hse: Option, - pub bypass_hse: bool, - pub hclk: Option, - pub sys_ck: Option, - pub pclk1: Option, - pub pclk2: Option, - - #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] - pub plli2s: Option, - - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] - pub pllsai: Option, - - pub pll48: bool, - pub ls: super::LsConfig, -} - -#[cfg(stm32f410)] -fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { - None -} - -// Not currently implemented, but will be in the future -#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] -fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { - None -} - -#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423)))] -fn calculate_sai_i2s_pll_values(vco_in: u32, max_div: u32, target: Option) -> Option<(u32, u32, u32)> { - let min_div = 2; - let target = match target { - Some(target) => target, - None => return None, - }; - - // We loop through the possible divider values to find the best configuration. Looping - // through all possible "N" values would result in more iterations. - let (n, outdiv, output, _error) = (min_div..=max_div) - .filter_map(|outdiv| { - let target_vco_out = match target.checked_mul(outdiv) { - Some(x) => x, - None => return None, - }; - let n = (target_vco_out + (vco_in >> 1)) / vco_in; - let vco_out = vco_in * n; - if !(100_000_000..=432_000_000).contains(&vco_out) { - return None; - } - let output = vco_out / outdiv; - let error = (output as i32 - target as i32).unsigned_abs(); - Some((n, outdiv, output, error)) - }) - .min_by_key(|(_, _, _, error)| *error)?; - - Some((n, outdiv, output)) -} - -#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] -fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { - let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 7, plli2s)?; - - RCC.plli2scfgr().modify(|w| { - w.set_plli2sn(n as u16); - w.set_plli2sr(outdiv as u8); - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] - w.set_plli2sq(outdiv as u8); //set sai divider same as i2s - }); - - Some(output) -} - -#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))] -fn setup_sai_pll(_vco_in: u32, _pllsai: Option) -> Option { - None -} - -#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] -fn setup_sai_pll(vco_in: u32, pllsai: Option) -> Option { - let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 15, pllsai)?; - - RCC.pllsaicfgr().modify(|w| { - w.set_pllsain(n as u16); - w.set_pllsaiq(outdiv as u8); - }); - - Some(output) -} - -fn setup_pll( - pllsrcclk: u32, - use_hse: bool, - pllsysclk: Option, - plli2s: Option, - pllsai: Option, - pll48clk: bool, -) -> PllResults { - use crate::pac::rcc::vals::{Pllp, Pllsrc}; - - let sysclk = pllsysclk.unwrap_or(pllsrcclk); - if pllsysclk.is_none() && !pll48clk { - RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8))); - - return PllResults { - use_pll: false, - pllsysclk: None, - pll48clk: None, - plli2sclk: None, - pllsaiclk: None, - }; - } - // Input divisor from PLL source clock, must result to frequency in - // the range from 1 to 2 MHz - let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000; - let pllm_max = pllsrcclk / 1_000_000; - - // Sysclk output divisor must be one of 2, 4, 6 or 8 - let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1); - - let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div }; - - // Find the lowest pllm value that minimize the difference between - // target frequency and the real vco_out frequency. - let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| { - let vco_in = pllsrcclk / pllm; - let plln = target_freq / vco_in; - target_freq - vco_in * plln - })); - - let vco_in = pllsrcclk / pllm; - assert!((1_000_000..=2_000_000).contains(&vco_in)); - - // Main scaler, must result in >= 100MHz (>= 192MHz for F401) - // and <= 432MHz, min 50, max 432 - let plln = if pll48clk { - // try the different valid pllq according to the valid - // main scaller values, and take the best - let pllq = unwrap!((4..=9).min_by_key(|pllq| { - let plln = 48_000_000 * pllq / vco_in; - let pll48_diff = 48_000_000 - vco_in * plln / pllq; - let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs(); - (pll48_diff, sysclk_diff) - })); - 48_000_000 * pllq / vco_in - } else { - sysclk * sysclk_div / vco_in - }; - - let pllp = (sysclk_div / 2) - 1; - - let pllq = (vco_in * plln + 47_999_999) / 48_000_000; - let real_pll48clk = vco_in * plln / pllq; - - RCC.pllcfgr().modify(|w| { - w.set_pllm(Pllm::from_bits(pllm as u8)); - w.set_plln(Plln::from_bits(plln as u16)); - w.set_pllp(Pllp::from_bits(pllp as u8)); - w.set_pllq(Pllq::from_bits(pllq as u8)); - w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); - w.set_pllr(Pllr::from_bits(0)); - }); - - let real_pllsysclk = vco_in * plln / sysclk_div; - - PllResults { - use_pll: true, - pllsysclk: Some(real_pllsysclk), - pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, - plli2sclk: setup_i2s_pll(vco_in, plli2s), - pllsaiclk: setup_sai_pll(vco_in, pllsai), - } -} - -fn flash_setup(sysclk: u32) { - use crate::pac::flash::vals::Latency; - - // Be conservative with voltage ranges - const FLASH_LATENCY_STEP: u32 = 30_000_000; - - critical_section::with(|_| { - FLASH - .acr() - .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); - }); -} - -pub(crate) unsafe fn init(config: Config) { - let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0); - let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); - let sysclk_on_pll = sysclk != pllsrcclk; - - let plls = setup_pll( - pllsrcclk, - config.hse.is_some(), - if sysclk_on_pll { Some(sysclk) } else { None }, - #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] - config.plli2s.map(|i2s| i2s.0), - #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] - None, - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] - config.pllsai.map(|sai| sai.0), - #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))] - None, - config.pll48, - ); - - if config.pll48 { - let freq = unwrap!(plls.pll48clk); - - assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32); - } - - let sysclk = if sysclk_on_pll { unwrap!(plls.pllsysclk) } else { sysclk }; - - // AHB prescaler - let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk); - let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk { - 0 => unreachable!(), - 1 => (Hpre::DIV1, 1), - 2 => (Hpre::DIV2, 2), - 3..=5 => (Hpre::DIV4, 4), - 6..=11 => (Hpre::DIV8, 8), - 12..=39 => (Hpre::DIV16, 16), - 40..=95 => (Hpre::DIV64, 64), - 96..=191 => (Hpre::DIV128, 128), - 192..=383 => (Hpre::DIV256, 256), - _ => (Hpre::DIV512, 512), - }; - - // Calculate real AHB clock - let hclk = sysclk / hpre_div; - - let pclk1 = config - .pclk1 - .map(|p| p.0) - .unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk)); - - let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 { - 0 => unreachable!(), - 1 => (0b000, 1), - 2 => (0b100, 2), - 3..=5 => (0b101, 4), - 6..=11 => (0b110, 8), - _ => (0b111, 16), - }; - let timer_mul1 = if ppre1 == 1 { 1 } else { 2 }; - - // Calculate real APB1 clock - let pclk1 = hclk / ppre1; - assert!(pclk1 <= max::PCLK1_MAX); - - let pclk2 = config - .pclk2 - .map(|p| p.0) - .unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk)); - let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 { - 0 => unreachable!(), - 1 => (0b000, 1), - 2 => (0b100, 2), - 3..=5 => (0b101, 4), - 6..=11 => (0b110, 8), - _ => (0b111, 16), - }; - let timer_mul2 = if ppre2 == 1 { 1 } else { 2 }; - - // Calculate real APB2 clock - let pclk2 = hclk / ppre2; - assert!(pclk2 <= max::PCLK2_MAX); - - flash_setup(sysclk); - - if config.hse.is_some() { - RCC.cr().modify(|w| { - w.set_hsebyp(config.bypass_hse); - w.set_hseon(true); - }); - while !RCC.cr().read().hserdy() {} - } - - if plls.use_pll { - RCC.cr().modify(|w| w.set_pllon(true)); - - if hclk > max::HCLK_OVERDRIVE_FREQUENCY { - PWR.cr1().modify(|w| w.set_oden(true)); - while !PWR.csr1().read().odrdy() {} - - PWR.cr1().modify(|w| w.set_odswen(true)); - while !PWR.csr1().read().odswrdy() {} - } - - while !RCC.cr().read().pllrdy() {} - } - - #[cfg(not(stm32f410))] - if plls.plli2sclk.is_some() { - RCC.cr().modify(|w| w.set_plli2son(true)); - - while !RCC.cr().read().plli2srdy() {} - } - - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] - if plls.pllsaiclk.is_some() { - RCC.cr().modify(|w| w.set_pllsaion(true)); - while !RCC.cr().read().pllsairdy() {} - } - - RCC.cfgr().modify(|w| { - w.set_ppre2(Ppre::from_bits(ppre2_bits)); - w.set_ppre1(Ppre::from_bits(ppre1_bits)); - w.set_hpre(hpre_bits); - }); - - // Wait for the new prescalers to kick in - // "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write" - cortex_m::asm::delay(16); - - RCC.cfgr().modify(|w| { - w.set_sw(if sysclk_on_pll { - Sw::PLL1_P - } else if config.hse.is_some() { - Sw::HSE - } else { - Sw::HSI - }) - }); - - let rtc = config.ls.init(); - - set_freqs(Clocks { - sys: Hertz(sysclk), - pclk1: Hertz(pclk1), - pclk2: Hertz(pclk2), - - pclk1_tim: Hertz(pclk1 * timer_mul1), - pclk2_tim: Hertz(pclk2 * timer_mul2), - - hclk1: Hertz(hclk), - hclk2: Hertz(hclk), - hclk3: Hertz(hclk), - - pll1_q: plls.pll48clk.map(Hertz), - - #[cfg(not(stm32f410))] - plli2s1_q: plls.plli2sclk.map(Hertz), - #[cfg(not(stm32f410))] - plli2s1_r: None, - - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] - pllsai1_q: plls.pllsaiclk.map(Hertz), - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] - pllsai1_r: None, - - rtc, - }); -} - -struct PllResults { - use_pll: bool, - pllsysclk: Option, - pll48clk: Option, - #[allow(dead_code)] - plli2sclk: Option, - #[allow(dead_code)] - pllsaiclk: Option, -} - -mod max { - #[cfg(stm32f401)] - pub(crate) const SYSCLK_MAX: u32 = 84_000_000; - #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))] - pub(crate) const SYSCLK_MAX: u32 = 168_000_000; - #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] - pub(crate) const SYSCLK_MAX: u32 = 100_000_000; - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))] - pub(crate) const SYSCLK_MAX: u32 = 180_000_000; - - pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 168_000_000; - - pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2; - - #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] - pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX; - #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))] - pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2; - - pub(crate) const PLL_48_CLK: u32 = 48_000_000; - pub(crate) const PLL_48_TOLERANCE: u32 = 120_000; -} diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f4f7.rs similarity index 71% rename from embassy-stm32/src/rcc/f7.rs rename to embassy-stm32/src/rcc/f4f7.rs index a984e4f4..de37eab7 100644 --- a/embassy-stm32/src/rcc/f7.rs +++ b/embassy-stm32/src/rcc/f4f7.rs @@ -6,6 +6,20 @@ use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; +// TODO: on some F4s, PLLM is shared between all PLLs. Enforce that. +// TODO: on some F4s, add support for plli2s_src +// +// plli2s plli2s_m plli2s_src pllsai pllsai_m +// f401 y shared +// f410 +// f411 y individual +// f412 y individual y +// f4[12]3 y individual y +// f446 y individual y individual +// f4[67]9 y shared y shared +// f4[23][79] y shared y shared +// f4[01][57] y shared + /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); @@ -51,7 +65,9 @@ pub struct Config { pub pll_src: PllSource, pub pll: Option, + #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] pub plli2s: Option, + #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] pub pllsai: Option, pub ahb_pre: AHBPrescaler, @@ -69,7 +85,9 @@ impl Default for Config { sys: Sysclk::HSI, pll_src: PllSource::HSI, pll: None, + #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] plli2s: None, + #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] pllsai: None, ahb_pre: AHBPrescaler::DIV1, @@ -128,7 +146,9 @@ pub(crate) unsafe fn init(config: Config) { source: config.pll_src, }; let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); + #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input); + #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input); // Configure sysclk @@ -171,6 +191,15 @@ pub(crate) unsafe fn init(config: Config) { pclk2_tim, rtc, pll1_q: pll.q, + #[cfg(all(rcc_f4, not(stm32f410)))] + plli2s1_q: _plli2s.q, + #[cfg(all(rcc_f4, not(stm32f410)))] + plli2s1_r: _plli2s.r, + + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] + pllsai1_q: _pllsai.q, + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] + pllsai1_r: _pllsai.r, }); } @@ -191,7 +220,9 @@ struct PllOutput { #[derive(PartialEq, Eq, Clone, Copy)] enum PllInstance { Pll, + #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] Plli2s, + #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] Pllsai, } @@ -201,10 +232,12 @@ fn pll_enable(instance: PllInstance, enabled: bool) { RCC.cr().modify(|w| w.set_pllon(enabled)); while RCC.cr().read().pllrdy() != enabled {} } + #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] PllInstance::Plli2s => { RCC.cr().modify(|w| w.set_plli2son(enabled)); while RCC.cr().read().plli2srdy() != enabled {} } + #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] PllInstance::Pllsai => { RCC.cr().modify(|w| w.set_pllsaion(enabled)); while RCC.cr().read().pllsairdy() != enabled {} @@ -255,9 +288,11 @@ fn init_pll(instance: PllInstance, config: Option, input: &PllInput) -> Pll w.set_pllsrc(input.source); write_fields!(w); }), + #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] PllInstance::Plli2s => RCC.plli2scfgr().write(|w| { write_fields!(w); }), + #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| { write_fields!(w); }), @@ -294,6 +329,7 @@ where (pclk, pclk_tim) } +#[cfg(stm32f7)] mod max { use core::ops::RangeInclusive; @@ -310,3 +346,34 @@ mod max { pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(2_100_000); pub(crate) const PLL_VCO: RangeInclusive = Hertz(100_000_000)..=Hertz(432_000_000); } + +#[cfg(stm32f4)] +mod max { + use core::ops::RangeInclusive; + + use crate::time::Hertz; + + pub(crate) const HSE_OSC: RangeInclusive = Hertz(4_000_000)..=Hertz(26_000_000); + pub(crate) const HSE_BYP: RangeInclusive = Hertz(1_000_000)..=Hertz(50_000_000); + + #[cfg(stm32f401)] + pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(84_000_000); + #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))] + pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(168_000_000); + #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] + pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(100_000_000); + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))] + pub(crate) const SYSCLK: RangeInclusive = Hertz(0)..=Hertz(180_000_000); + + pub(crate) const HCLK: RangeInclusive = Hertz(0)..=Hertz(SYSCLK.end().0); + + pub(crate) const PCLK1: RangeInclusive = Hertz(0)..=Hertz(PCLK2.end().0 / 2); + + #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))] + pub(crate) const PCLK2: RangeInclusive = Hertz(0)..=Hertz(HCLK.end().0); + #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))] + pub(crate) const PCLK2: RangeInclusive = Hertz(0)..=Hertz(HCLK.end().0 / 2); + + pub(crate) const PLL_IN: RangeInclusive = Hertz(1_000_000)..=Hertz(2_100_000); + pub(crate) const PLL_VCO: RangeInclusive = Hertz(100_000_000)..=Hertz(432_000_000); +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d587a198..49174b27 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -13,8 +13,7 @@ pub use mco::*; #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")] #[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")] -#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] -#[cfg_attr(rcc_f7, path = "f7.rs")] +#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")] #[cfg_attr(rcc_c0, path = "c0.rs")] #[cfg_attr(rcc_g0, path = "g0.rs")] #[cfg_attr(rcc_g4, path = "g4.rs")] diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index ddf8596a..1747bbf4 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -10,7 +10,7 @@ use embassy_stm32::eth::generic_smi::GenericSMI; use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::time::mhz; +use embassy_stm32::time::Hertz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::Timer; use embedded_io_async::Write; @@ -32,7 +32,25 @@ async fn net_task(stack: &'static Stack) -> ! { #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(200)); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL180, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/hello.rs b/examples/stm32f4/src/bin/hello.rs index 27ee83aa..a2a28711 100644 --- a/examples/stm32f4/src/bin/hello.rs +++ b/examples/stm32f4/src/bin/hello.rs @@ -4,15 +4,13 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::time::Hertz; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { - let mut config = Config::default(); - config.rcc.sys_ck = Some(Hertz(84_000_000)); + let config = Config::default(); let _p = embassy_stm32::init(config); loop { diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 6ec7d0fe..37e42384 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; -use embassy_stm32::time::mhz; +use embassy_stm32::time::{mhz, Hertz}; use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; use {defmt_rtt as _, panic_probe as _}; @@ -20,8 +20,25 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(48)); - config.rcc.pll48 = true; + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL168, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. + divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 763e3a9e..7c0644ae 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_stm32::rng::{self, Rng}; -use embassy_stm32::time::mhz; +use embassy_stm32::time::Hertz; use embassy_stm32::usb_otg::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; @@ -46,9 +46,25 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let mut config = Config::default(); - config.rcc.pll48 = true; - config.rcc.sys_ck = Some(mhz(48)); - + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL168, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. + divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } let p = embassy_stm32::init(config); // Create the driver, from the HAL. diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 4ff6452e..004ff038 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -4,7 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::time::mhz; +use embassy_stm32::time::Hertz; 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,9 +22,25 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut config = Config::default(); - config.rcc.pll48 = true; - config.rcc.sys_ck = Some(mhz(48)); - + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL168, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. + divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } let p = embassy_stm32::init(config); // Create the driver, from the HAL. diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 7bc74141..8dde71fb 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -224,11 +224,23 @@ pub fn config() -> Config { #[cfg(feature = "stm32f429zi")] { - // TODO: stm32f429zi can do up to 180mhz, but that makes tests fail. - // perhaps we have some bug w.r.t overdrive. - config.rcc.sys_ck = Some(Hertz(168_000_000)); - config.rcc.pclk1 = Some(Hertz(42_000_000)); - config.rcc.pclk2 = Some(Hertz(84_000_000)); + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL180, + divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; } #[cfg(feature = "stm32f767zi")]