diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1a024e9a9..67f3f526b 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -59,7 +59,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2dba1f1ddee697e616aff2a4db57a6ffaf1b29b7" } +stm32-metapac = { git = "https://ci.embassy.dev/jobs/7d2e0e63527f/artifacts/generated.git" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -78,7 +78,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-2dba1f1ddee697e616aff2a4db57a6ffaf1b29b7", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://ci.embassy.dev/jobs/7d2e0e63527f/artifacts/generated.git", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs new file mode 100644 index 000000000..a4730ed49 --- /dev/null +++ b/embassy-stm32/src/rcc/h.rs @@ -0,0 +1,777 @@ +use core::ops::RangeInclusive; + +use crate::pac; +use crate::pac::pwr::vals::Vos; +#[cfg(stm32h5)] +pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; +#[cfg(stm32h7)] +pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; +pub use crate::pac::rcc::vals::Ckpersel as PerClockSource; +use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre}; +use crate::pac::{FLASH, PWR, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; + +/// HSI speed +pub const HSI_FREQ: Hertz = Hertz(64_000_000); + +/// CSI speed +pub const CSI_FREQ: Hertz = Hertz(4_000_000); + +/// HSI48 speed +pub const HSI48_FREQ: Hertz = Hertz(48_000_000); + +/// LSI speed +pub const LSI_FREQ: Hertz = Hertz(32_000); + +const VCO_RANGE: RangeInclusive = 150_000_000..=420_000_000; +#[cfg(any(stm32h5, pwr_h7rm0455))] +const VCO_WIDE_RANGE: RangeInclusive = 128_000_000..=560_000_000; +#[cfg(pwr_h7rm0468)] +const VCO_WIDE_RANGE: RangeInclusive = 192_000_000..=836_000_000; +#[cfg(any(pwr_h7rm0399, pwr_h7rm0433))] +const VCO_WIDE_RANGE: RangeInclusive = 192_000_000..=960_000_000; + +pub use super::bus::{AHBPrescaler, APBPrescaler}; + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum VoltageScale { + Scale0, + Scale1, + Scale2, + Scale3, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) + Bypass, + /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) + #[cfg(any(rcc_h5, rcc_h50))] + BypassDigital, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Hsi { + /// 64Mhz + Mhz64, + /// 32Mhz (divided by 2) + Mhz32, + /// 16Mhz (divided by 4) + Mhz16, + /// 8Mhz (divided by 8) + Mhz8, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Sysclk { + /// HSI selected as sysclk + HSI, + /// HSE selected as sysclk + HSE, + /// CSI selected as sysclk + CSI, + /// PLL1_P selected as sysclk + Pll1P, +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum PllSource { + Hsi, + Csi, + Hse, +} + +#[derive(Clone, Copy)] +pub struct Pll { + /// Source clock selection. + #[cfg(stm32h5)] + pub source: PllSource, + + /// PLL pre-divider (DIVM). Must be between 1 and 63. + pub prediv: u8, + + /// PLL multiplication factor. Must be between 4 and 512. + pub mul: u16, + + /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. + /// On PLL1, it must be even (in particular, it cannot be 1.) + pub divp: Option, + /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. + pub divq: Option, + /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. + pub divr: Option, +} + +fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz { + match (tim, apb) { + (TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk, + (TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk, + (TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32, + (TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32, + (TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32, + + (TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk, + (TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk, + (TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk, + (TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32, + (TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32, + + _ => unreachable!(), + } +} + +/// Timer prescaler +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum TimerPrescaler { + /// The timers kernel clock is equal to hclk if PPREx corresponds to a + /// division by 1 or 2, else it is equal to 2*pclk + DefaultX2, + + /// The timers kernel clock is equal to hclk if PPREx corresponds to a + /// division by 1, 2 or 4, else it is equal to 4*pclk + DefaultX4, +} + +impl From for Timpre { + fn from(value: TimerPrescaler) -> Self { + match value { + TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, + TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, + } + } +} + +/// Configuration of the core clocks +#[non_exhaustive] +pub struct Config { + pub hsi: Option, + pub hse: Option, + pub csi: bool, + pub hsi48: bool, + pub sys: Sysclk, + + #[cfg(stm32h7)] + pub pll_src: PllSource, + + pub pll1: Option, + pub pll2: Option, + #[cfg(any(rcc_h5, stm32h7))] + pub pll3: Option, + + pub d1c_pre: AHBPrescaler, + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, + pub apb3_pre: APBPrescaler, + #[cfg(stm32h7)] + pub apb4_pre: APBPrescaler, + + pub per_clock_source: PerClockSource, + pub adc_clock_source: AdcClockSource, + pub timer_prescaler: TimerPrescaler, + pub voltage_scale: VoltageScale, +} + +impl Default for Config { + fn default() -> Self { + Self { + hsi: Some(Hsi::Mhz64), + hse: None, + csi: false, + hsi48: false, + sys: Sysclk::HSI, + #[cfg(stm32h7)] + pll_src: PllSource::Hsi, + pll1: None, + pll2: None, + #[cfg(any(rcc_h5, stm32h7))] + pll3: None, + + d1c_pre: AHBPrescaler::DIV1, + ahb_pre: AHBPrescaler::DIV1, + apb1_pre: APBPrescaler::DIV1, + apb2_pre: APBPrescaler::DIV1, + apb3_pre: APBPrescaler::DIV1, + #[cfg(stm32h7)] + apb4_pre: APBPrescaler::DIV1, + + per_clock_source: PerClockSource::HSI, + adc_clock_source: AdcClockSource::from_bits(0), // PLL2_P on H7, HCLK on H5 + timer_prescaler: TimerPrescaler::DefaultX2, + voltage_scale: VoltageScale::Scale0, + } + } +} + +pub(crate) unsafe fn init(config: Config) { + #[cfg(stm32h7)] + RCC.apb4enr().modify(|w| w.set_syscfgen(true)); + #[cfg(stm32h5)] + RCC.apb3enr().modify(|w| w.set_sbsen(true)); + + // NB. The lower bytes of CR3 can only be written once after + // POR, and must be written with a valid combination. Refer to + // RM0433 Rev 7 6.8.4. This is partially enforced by dropping + // `self` at the end of this method, but of course we cannot + // know what happened between the previous POR and here. + #[cfg(pwr_h7rm0433)] + PWR.cr3().modify(|w| { + w.set_scuen(true); + w.set_ldoen(true); + w.set_bypass(false); + }); + + #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] + PWR.cr3().modify(|w| { + // hardcode "Direct SPMS" for now, this is what works on nucleos with the + // default solderbridge configuration. + w.set_sden(true); + w.set_ldoen(false); + }); + + // Validate the supply configuration. If you are stuck here, it is + // because the voltages on your board do not match those specified + // in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset + // VOS = Scale 3, so check that the voltage on the VCAP pins = + // 1.0V. + #[cfg(any(pwr_h7rm0433, pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] + while !PWR.csr1().read().actvosrdy() {} + + // Configure voltage scale. + #[cfg(any(pwr_h5, pwr_h50))] + { + PWR.voscr().modify(|w| { + w.set_vos(match config.voltage_scale { + VoltageScale::Scale0 => Vos::SCALE0, + VoltageScale::Scale1 => Vos::SCALE1, + VoltageScale::Scale2 => Vos::SCALE2, + VoltageScale::Scale3 => Vos::SCALE3, + }) + }); + while !PWR.vossr().read().vosrdy() {} + } + + #[cfg(syscfg_h7)] + { + // in chips without the overdrive bit, we can go from any scale to any scale directly. + PWR.d3cr().modify(|w| { + w.set_vos(match config.voltage_scale { + VoltageScale::Scale0 => Vos::SCALE0, + VoltageScale::Scale1 => Vos::SCALE1, + VoltageScale::Scale2 => Vos::SCALE2, + VoltageScale::Scale3 => Vos::SCALE3, + }) + }); + while !PWR.d3cr().read().vosrdy() {} + } + + #[cfg(syscfg_h7od)] + { + match config.voltage_scale { + VoltageScale::Scale0 => { + // to go to scale0, we must go to Scale1 first... + PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1)); + while !PWR.d3cr().read().vosrdy() {} + + // Then enable overdrive. + critical_section::with(|_| pac::SYSCFG.pwrcr().modify(|w| w.set_oden(1))); + while !PWR.d3cr().read().vosrdy() {} + } + _ => { + // for all other scales, we can go directly. + PWR.d3cr().modify(|w| { + w.set_vos(match config.voltage_scale { + VoltageScale::Scale0 => unreachable!(), + VoltageScale::Scale1 => Vos::SCALE1, + VoltageScale::Scale2 => Vos::SCALE2, + VoltageScale::Scale3 => Vos::SCALE3, + }) + }); + while !PWR.d3cr().read().vosrdy() {} + } + } + } + + // Configure HSI + let hsi = match config.hsi { + None => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + Some(hsi) => { + let (freq, hsidiv) = match hsi { + Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1), + Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2), + Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4), + Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8), + }; + RCC.cr().modify(|w| { + w.set_hsidiv(hsidiv); + w.set_hsion(true); + }); + while !RCC.cr().read().hsirdy() {} + Some(freq) + } + }; + + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + RCC.cr().modify(|w| { + w.set_hsebyp(hse.mode != HseMode::Oscillator); + #[cfg(any(rcc_h5, rcc_h50))] + w.set_hseext(match hse.mode { + HseMode::Oscillator | HseMode::Bypass => pac::rcc::vals::Hseext::ANALOG, + HseMode::BypassDigital => pac::rcc::vals::Hseext::DIGITAL, + }); + }); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } + }; + + // Configure HSI48. + RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); + let _hsi48 = match config.hsi48 { + false => None, + true => { + while !RCC.cr().read().hsi48rdy() {} + Some(CSI_FREQ) + } + }; + + // Configure CSI. + RCC.cr().modify(|w| w.set_csion(config.csi)); + let csi = match config.csi { + false => None, + true => { + while !RCC.cr().read().csirdy() {} + Some(CSI_FREQ) + } + }; + + // Configure PLLs. + let pll_input = PllInput { + csi, + hse, + hsi, + #[cfg(stm32h7)] + source: config.pll_src, + }; + let pll1 = init_pll(0, config.pll1, &pll_input); + let pll2 = init_pll(1, config.pll2, &pll_input); + #[cfg(any(rcc_h5, stm32h7))] + let _pll3 = init_pll(2, config.pll3, &pll_input); + + // Configure sysclk + let (sys, sw) = match config.sys { + Sysclk::HSI => (unwrap!(hsi), Sw::HSI), + Sysclk::HSE => (unwrap!(hse), Sw::HSE), + Sysclk::CSI => (unwrap!(csi), Sw::CSI), + Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1), + }; + + // Check limits. + #[cfg(stm32h5)] + let (hclk_max, pclk_max) = match config.voltage_scale { + VoltageScale::Scale0 => (Hertz(250_000_000), Hertz(250_000_000)), + VoltageScale::Scale1 => (Hertz(200_000_000), Hertz(200_000_000)), + VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)), + VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)), + }; + #[cfg(stm32h7)] + let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale { + VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)), + VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)), + VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)), + VoltageScale::Scale3 => (Hertz(200_000_000), Hertz(100_000_000), Hertz(50_000_000)), + }; + + #[cfg(stm32h7)] + let hclk = { + let d1cpre_clk = sys / config.d1c_pre; + assert!(d1cpre_clk <= d1cpre_clk_max); + sys / config.ahb_pre + }; + #[cfg(stm32h5)] + let hclk = sys / config.ahb_pre; + assert!(hclk <= hclk_max); + + let apb1 = hclk / config.apb1_pre; + let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler); + assert!(apb1 <= pclk_max); + let apb2 = hclk / config.apb2_pre; + let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler); + assert!(apb2 <= pclk_max); + let apb3 = hclk / config.apb3_pre; + assert!(apb3 <= pclk_max); + #[cfg(stm32h7)] + let apb4 = hclk / config.apb4_pre; + #[cfg(stm32h7)] + assert!(apb4 <= pclk_max); + + let _per_ck = match config.per_clock_source { + Ckpersel::HSI => hsi, + Ckpersel::CSI => csi, + Ckpersel::HSE => hse, + _ => unreachable!(), + }; + + #[cfg(stm32h7)] + let adc = match config.adc_clock_source { + AdcClockSource::PLL2_P => pll2.p, + AdcClockSource::PLL3_R => _pll3.r, + AdcClockSource::PER => _per_ck, + _ => unreachable!(), + }; + #[cfg(stm32h5)] + let adc = match config.adc_clock_source { + AdcClockSource::HCLK => Some(hclk), + AdcClockSource::SYSCLK => Some(sys), + AdcClockSource::PLL2_R => pll2.r, + AdcClockSource::HSE => hse, + AdcClockSource::HSI_KER => hsi, + AdcClockSource::CSI_KER => csi, + _ => unreachable!(), + }; + + flash_setup(hclk, config.voltage_scale); + + #[cfg(stm32h7)] + { + RCC.d1cfgr().modify(|w| { + w.set_d1cpre(config.d1c_pre); + w.set_d1ppre(config.apb3_pre); + w.set_hpre(config.ahb_pre); + }); + // Ensure core prescaler value is valid before future lower core voltage + while RCC.d1cfgr().read().d1cpre() != config.d1c_pre {} + + RCC.d2cfgr().modify(|w| { + w.set_d2ppre1(config.apb1_pre); + w.set_d2ppre2(config.apb2_pre); + }); + RCC.d3cfgr().modify(|w| { + w.set_d3ppre(config.apb4_pre); + }); + + RCC.d1ccipr().modify(|w| { + w.set_ckpersel(config.per_clock_source); + }); + RCC.d3ccipr().modify(|w| { + w.set_adcsel(config.adc_clock_source); + }); + } + #[cfg(stm32h5)] + { + // Set hpre + RCC.cfgr2().modify(|w| w.set_hpre(config.ahb_pre)); + while RCC.cfgr2().read().hpre() != config.ahb_pre {} + + // set ppre + RCC.cfgr2().modify(|w| { + w.set_ppre1(config.apb1_pre); + w.set_ppre2(config.apb2_pre); + w.set_ppre3(config.apb3_pre); + }); + + RCC.ccipr5().modify(|w| { + w.set_ckpersel(config.per_clock_source); + w.set_adcdacsel(config.adc_clock_source) + }); + } + + RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into())); + + RCC.cfgr().modify(|w| w.set_sw(sw)); + while RCC.cfgr().read().sws() != sw {} + + // IO compensation cell - Requires CSI clock and SYSCFG + #[cfg(stm32h7)] // TODO h5 + if csi.is_some() { + // Enable the compensation cell, using back-bias voltage code + // provide by the cell. + critical_section::with(|_| { + pac::SYSCFG.cccsr().modify(|w| { + w.set_en(true); + w.set_cs(false); + w.set_hslv(false); + }) + }); + while !pac::SYSCFG.cccsr().read().ready() {} + } + + set_freqs(Clocks { + sys, + ahb1: hclk, + ahb2: hclk, + ahb3: hclk, + ahb4: hclk, + apb1, + apb2, + apb3, + #[cfg(stm32h7)] + apb4, + apb1_tim, + apb2_tim, + adc: adc, + }); +} + +struct PllInput { + hsi: Option, + hse: Option, + csi: Option, + #[cfg(stm32h7)] + source: PllSource, +} + +struct PllOutput { + p: Option, + #[allow(dead_code)] + q: Option, + #[allow(dead_code)] + r: Option, +} + +fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { + let Some(config) = config else { + // Stop PLL + RCC.cr().modify(|w| w.set_pllon(num, false)); + while RCC.cr().read().pllrdy(num) {} + + // "To save power when PLL1 is not used, the value of PLL1M must be set to 0."" + #[cfg(stm32h7)] + RCC.pllckselr().write(|w| w.set_divm(num, 0)); + #[cfg(stm32h5)] + RCC.pllcfgr(num).write(|w| w.set_divm(0)); + + return PllOutput { + p: None, + q: None, + r: None, + }; + }; + + assert!(1 <= config.prediv && config.prediv <= 63); + assert!(4 <= config.mul && config.mul <= 512); + + #[cfg(stm32h5)] + let source = config.source; + #[cfg(stm32h7)] + let source = input.source; + + let (in_clk, src) = match source { + PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI), + PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE), + PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI), + }; + + let ref_clk = in_clk / config.prediv as u32; + + let ref_range = match ref_clk.0 { + ..=1_999_999 => Pllrge::RANGE1, + ..=3_999_999 => Pllrge::RANGE2, + ..=7_999_999 => Pllrge::RANGE4, + ..=16_000_000 => Pllrge::RANGE8, + x => panic!("pll ref_clk out of range: {} mhz", x), + }; + + // The smaller range (150 to 420 MHz) must + // be chosen when the reference clock frequency is lower than 2 MHz. + let wide_allowed = ref_range != Pllrge::RANGE1; + + let vco_clk = ref_clk * config.mul; + let vco_range = if VCO_RANGE.contains(&vco_clk.0) { + Pllvcosel::MEDIUMVCO + } else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk.0) { + Pllvcosel::WIDEVCO + } else { + panic!("pll vco_clk out of range: {} mhz", vco_clk.0) + }; + + let p = config.divp.map(|div| { + assert!(1 <= div && div <= 128); + if num == 0 { + // on PLL1, DIVP must be even. + assert!(div % 2 == 0); + } + + vco_clk / div + }); + let q = config.divq.map(|div| { + assert!(1 <= div && div <= 128); + vco_clk / div + }); + let r = config.divr.map(|div| { + assert!(1 <= div && div <= 128); + vco_clk / div + }); + + #[cfg(stm32h5)] + RCC.pllcfgr(num).write(|w| { + w.set_pllsrc(src); + w.set_divm(config.prediv); + w.set_pllvcosel(vco_range); + w.set_pllrge(ref_range); + w.set_pllfracen(false); + w.set_pllpen(p.is_some()); + w.set_pllqen(q.is_some()); + w.set_pllren(r.is_some()); + }); + + #[cfg(stm32h7)] + { + RCC.pllckselr().modify(|w| { + w.set_divm(num, config.prediv); + w.set_pllsrc(src); + }); + RCC.pllcfgr().modify(|w| { + w.set_pllvcosel(num, vco_range); + w.set_pllrge(num, ref_range); + w.set_pllfracen(num, false); + w.set_divpen(num, p.is_some()); + w.set_divqen(num, q.is_some()); + w.set_divren(num, r.is_some()); + }); + } + + RCC.plldivr(num).write(|w| { + w.set_plln(config.mul - 1); + w.set_pllp((config.divp.unwrap_or(1) - 1) as u8); + w.set_pllq((config.divq.unwrap_or(1) - 1) as u8); + w.set_pllr((config.divr.unwrap_or(1) - 1) as u8); + }); + + RCC.cr().modify(|w| w.set_pllon(num, true)); + while !RCC.cr().read().pllrdy(num) {} + + PllOutput { p, q, r } +} + +fn flash_setup(clk: Hertz, vos: VoltageScale) { + // RM0481 Rev 1, table 37 + // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0 + // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz + // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz + // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz + // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz + // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz + // 5 2 170 to 200 MHz 210 to 250 MHz + #[cfg(stm32h5)] + let (latency, wrhighfreq) = match (vos, clk.0) { + (VoltageScale::Scale0, ..=42_000_000) => (0, 0), + (VoltageScale::Scale0, ..=84_000_000) => (1, 0), + (VoltageScale::Scale0, ..=126_000_000) => (2, 1), + (VoltageScale::Scale0, ..=168_000_000) => (3, 1), + (VoltageScale::Scale0, ..=210_000_000) => (4, 2), + (VoltageScale::Scale0, ..=250_000_000) => (5, 2), + + (VoltageScale::Scale1, ..=34_000_000) => (0, 0), + (VoltageScale::Scale1, ..=68_000_000) => (1, 0), + (VoltageScale::Scale1, ..=102_000_000) => (2, 1), + (VoltageScale::Scale1, ..=136_000_000) => (3, 1), + (VoltageScale::Scale1, ..=170_000_000) => (4, 2), + (VoltageScale::Scale1, ..=200_000_000) => (5, 2), + + (VoltageScale::Scale2, ..=30_000_000) => (0, 0), + (VoltageScale::Scale2, ..=60_000_000) => (1, 0), + (VoltageScale::Scale2, ..=90_000_000) => (2, 1), + (VoltageScale::Scale2, ..=120_000_000) => (3, 1), + (VoltageScale::Scale2, ..=150_000_000) => (4, 2), + + (VoltageScale::Scale3, ..=20_000_000) => (0, 0), + (VoltageScale::Scale3, ..=40_000_000) => (1, 0), + (VoltageScale::Scale3, ..=60_000_000) => (2, 1), + (VoltageScale::Scale3, ..=80_000_000) => (3, 1), + (VoltageScale::Scale3, ..=100_000_000) => (4, 2), + + _ => unreachable!(), + }; + + #[cfg(flash_h7)] + let (latency, wrhighfreq) = match (vos, clk.0) { + // VOS 0 range VCORE 1.26V - 1.40V + (VoltageScale::Scale0, ..=70_000_000) => (0, 0), + (VoltageScale::Scale0, ..=140_000_000) => (1, 1), + (VoltageScale::Scale0, ..=185_000_000) => (2, 1), + (VoltageScale::Scale0, ..=210_000_000) => (2, 2), + (VoltageScale::Scale0, ..=225_000_000) => (3, 2), + (VoltageScale::Scale0, ..=240_000_000) => (4, 2), + // VOS 1 range VCORE 1.15V - 1.26V + (VoltageScale::Scale1, ..=70_000_000) => (0, 0), + (VoltageScale::Scale1, ..=140_000_000) => (1, 1), + (VoltageScale::Scale1, ..=185_000_000) => (2, 1), + (VoltageScale::Scale1, ..=210_000_000) => (2, 2), + (VoltageScale::Scale1, ..=225_000_000) => (3, 2), + // VOS 2 range VCORE 1.05V - 1.15V + (VoltageScale::Scale2, ..=55_000_000) => (0, 0), + (VoltageScale::Scale2, ..=110_000_000) => (1, 1), + (VoltageScale::Scale2, ..=165_000_000) => (2, 1), + (VoltageScale::Scale2, ..=224_000_000) => (3, 2), + // VOS 3 range VCORE 0.95V - 1.05V + (VoltageScale::Scale3, ..=45_000_000) => (0, 0), + (VoltageScale::Scale3, ..=90_000_000) => (1, 1), + (VoltageScale::Scale3, ..=135_000_000) => (2, 1), + (VoltageScale::Scale3, ..=180_000_000) => (3, 2), + (VoltageScale::Scale3, ..=224_000_000) => (4, 2), + _ => unreachable!(), + }; + + // See RM0455 Rev 10 Table 16. FLASH recommended number of wait + // states and programming delay + #[cfg(flash_h7ab)] + let (latency, wrhighfreq) = match (vos, clk.0) { + // VOS 0 range VCORE 1.25V - 1.35V + (VoltageScale::Scale0, ..=42_000_000) => (0, 0), + (VoltageScale::Scale0, ..=84_000_000) => (1, 0), + (VoltageScale::Scale0, ..=126_000_000) => (2, 1), + (VoltageScale::Scale0, ..=168_000_000) => (3, 1), + (VoltageScale::Scale0, ..=210_000_000) => (4, 2), + (VoltageScale::Scale0, ..=252_000_000) => (5, 2), + (VoltageScale::Scale0, ..=280_000_000) => (6, 3), + // VOS 1 range VCORE 1.15V - 1.25V + (VoltageScale::Scale1, ..=38_000_000) => (0, 0), + (VoltageScale::Scale1, ..=76_000_000) => (1, 0), + (VoltageScale::Scale1, ..=114_000_000) => (2, 1), + (VoltageScale::Scale1, ..=152_000_000) => (3, 1), + (VoltageScale::Scale1, ..=190_000_000) => (4, 2), + (VoltageScale::Scale1, ..=225_000_000) => (5, 2), + // VOS 2 range VCORE 1.05V - 1.15V + (VoltageScale::Scale2, ..=34) => (0, 0), + (VoltageScale::Scale2, ..=68) => (1, 0), + (VoltageScale::Scale2, ..=102) => (2, 1), + (VoltageScale::Scale2, ..=136) => (3, 1), + (VoltageScale::Scale2, ..=160) => (4, 2), + // VOS 3 range VCORE 0.95V - 1.05V + (VoltageScale::Scale3, ..=22) => (0, 0), + (VoltageScale::Scale3, ..=44) => (1, 0), + (VoltageScale::Scale3, ..=66) => (2, 1), + (VoltageScale::Scale3, ..=88) => (3, 1), + _ => unreachable!(), + }; + + debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); + + FLASH.acr().write(|w| { + w.set_wrhighfreq(wrhighfreq); + w.set_latency(latency); + }); + while FLASH.acr().read().latency() != latency {} +} diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs deleted file mode 100644 index 15f28c5dc..000000000 --- a/embassy-stm32/src/rcc/h5.rs +++ /dev/null @@ -1,511 +0,0 @@ -use core::marker::PhantomData; - -use stm32_metapac::rcc::vals::Timpre; - -use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; -use crate::pac::{FLASH, PWR, RCC}; -use crate::rcc::{set_freqs, Clocks}; -use crate::time::Hertz; -use crate::{peripherals, Peripheral}; - -/// HSI speed -pub const HSI_FREQ: Hertz = Hertz(64_000_000); - -/// CSI speed -pub const CSI_FREQ: Hertz = Hertz(4_000_000); - -/// HSI48 speed -pub const HSI48_FREQ: Hertz = Hertz(48_000_000); - -/// LSI speed -pub const LSI_FREQ: Hertz = Hertz(32_000); - -const VCO_MIN: u32 = 150_000_000; -const VCO_MAX: u32 = 420_000_000; -const VCO_WIDE_MIN: u32 = 128_000_000; -const VCO_WIDE_MAX: u32 = 560_000_000; - -pub use super::bus::{AHBPrescaler, APBPrescaler}; -pub use crate::pac::pwr::vals::Vos as VoltageScale; - -pub enum HseMode { - /// crystal/ceramic oscillator (HSEBYP=0) - Oscillator, - /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) - BypassAnalog, - /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) - BypassDigital, -} - -pub struct Hse { - /// HSE frequency. - pub freq: Hertz, - /// HSE mode. - pub mode: HseMode, -} - -pub enum Hsi { - /// 64Mhz - Mhz64, - /// 32Mhz (divided by 2) - Mhz32, - /// 16Mhz (divided by 4) - Mhz16, - /// 8Mhz (divided by 8) - Mhz8, -} - -pub enum Sysclk { - /// HSI selected as sysclk - HSI, - /// HSE selected as sysclk - HSE, - /// CSI selected as sysclk - CSI, - /// PLL1_P selected as sysclk - Pll1P, -} - -pub enum PllSource { - Hsi, - Csi, - Hse, -} - -pub struct Pll { - /// Source clock selection. - pub source: PllSource, - - /// PLL pre-divider (DIVM). Must be between 1 and 63. - pub prediv: u8, - - /// PLL multiplication factor. Must be between 4 and 512. - pub mul: u16, - - /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. - /// On PLL1, it must be even (in particular, it cannot be 1.) - pub divp: Option, - /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. - pub divq: Option, - /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. - pub divr: Option, -} - -fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz { - match (tim, apb) { - // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a - // division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 - (TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk, - (TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk, - (TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32, - (TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32, - (TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32, - // The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2 - // corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2 - // this makes NO SENSE and is different than in the H7. Mistake in the RM?? - (TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk * 2u32, - (TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk, - (TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk / 2u32, - (TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32, - (TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32, - - _ => unreachable!(), - } -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum TimerPrescaler { - DefaultX2, - DefaultX4, -} - -impl From for Timpre { - fn from(value: TimerPrescaler) -> Self { - match value { - TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, - TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, - } - } -} - -/// Configuration of the core clocks -#[non_exhaustive] -pub struct Config { - pub hsi: Option, - pub hse: Option, - pub csi: bool, - pub hsi48: bool, - pub sys: Sysclk, - - pub pll1: Option, - pub pll2: Option, - #[cfg(rcc_h5)] - pub pll3: Option, - - pub ahb_pre: AHBPrescaler, - pub apb1_pre: APBPrescaler, - pub apb2_pre: APBPrescaler, - pub apb3_pre: APBPrescaler, - pub timer_prescaler: TimerPrescaler, - - pub voltage_scale: VoltageScale, -} - -impl Default for Config { - fn default() -> Self { - Self { - hsi: Some(Hsi::Mhz64), - hse: None, - csi: false, - hsi48: false, - sys: Sysclk::HSI, - pll1: None, - pll2: None, - #[cfg(rcc_h5)] - pll3: None, - - ahb_pre: AHBPrescaler::DIV1, - apb1_pre: APBPrescaler::DIV1, - apb2_pre: APBPrescaler::DIV1, - apb3_pre: APBPrescaler::DIV1, - timer_prescaler: TimerPrescaler::DefaultX2, - - voltage_scale: VoltageScale::SCALE3, - } - } -} - -pub(crate) mod sealed { - pub trait McoInstance { - type Source; - unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); - } -} - -pub trait McoInstance: sealed::McoInstance + 'static {} - -pin_trait!(McoPin, McoInstance); - -macro_rules! impl_peri { - ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { - impl sealed::McoInstance for peripherals::$peri { - type Source = $source; - - unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { - RCC.cfgr().modify(|w| { - w.$set_source(source); - w.$set_prescaler(prescaler); - }); - } - } - - impl McoInstance for peripherals::$peri {} - }; -} - -impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); -impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); - -pub struct Mco<'d, T: McoInstance> { - phantom: PhantomData<&'d mut T>, -} - -impl<'d, T: McoInstance> Mco<'d, T> { - pub fn new( - _peri: impl Peripheral

+ 'd, - _pin: impl Peripheral

> + 'd, - _source: T::Source, - ) -> Self { - todo!(); - } -} - -pub(crate) unsafe fn init(config: Config) { - let max_clk = match config.voltage_scale { - VoltageScale::SCALE0 => Hertz(250_000_000), - VoltageScale::SCALE1 => Hertz(200_000_000), - VoltageScale::SCALE2 => Hertz(150_000_000), - VoltageScale::SCALE3 => Hertz(100_000_000), - }; - - // Configure voltage scale. - PWR.voscr().modify(|w| w.set_vos(config.voltage_scale)); - while !PWR.vossr().read().vosrdy() {} - - // Configure HSI - let hsi = match config.hsi { - None => { - RCC.cr().modify(|w| w.set_hsion(false)); - None - } - Some(hsi) => { - let (freq, hsidiv) = match hsi { - Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1), - Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2), - Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4), - Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8), - }; - RCC.cr().modify(|w| { - w.set_hsidiv(hsidiv); - w.set_hsion(true); - }); - while !RCC.cr().read().hsirdy() {} - Some(freq) - } - }; - - // Configure HSE - let hse = match config.hse { - None => { - RCC.cr().modify(|w| w.set_hseon(false)); - None - } - Some(hse) => { - let (byp, ext) = match hse.mode { - HseMode::Oscillator => (false, Hseext::ANALOG), - HseMode::BypassAnalog => (true, Hseext::ANALOG), - HseMode::BypassDigital => (true, Hseext::DIGITAL), - }; - - RCC.cr().modify(|w| { - w.set_hsebyp(byp); - w.set_hseext(ext); - }); - RCC.cr().modify(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - Some(hse.freq) - } - }; - - // Configure HSI48. - RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); - let _hsi48 = match config.hsi48 { - false => None, - true => { - while !RCC.cr().read().hsi48rdy() {} - Some(CSI_FREQ) - } - }; - - // Configure CSI. - RCC.cr().modify(|w| w.set_csion(config.csi)); - let csi = match config.csi { - false => None, - true => { - while !RCC.cr().read().csirdy() {} - Some(CSI_FREQ) - } - }; - - // Configure PLLs. - let pll_input = PllInput { csi, hse, hsi }; - let pll1 = init_pll(0, config.pll1, &pll_input); - let _pll2 = init_pll(1, config.pll2, &pll_input); - #[cfg(rcc_h5)] - let _pll3 = init_pll(2, config.pll3, &pll_input); - - // Configure sysclk - let (sys, sw) = match config.sys { - Sysclk::HSI => (unwrap!(hsi), Sw::HSI), - Sysclk::HSE => (unwrap!(hse), Sw::HSE), - Sysclk::CSI => (unwrap!(csi), Sw::CSI), - Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1), - }; - assert!(sys <= max_clk); - - let hclk = sys / config.ahb_pre; - - let apb1 = hclk / config.apb1_pre; - let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler); - let apb2 = hclk / config.apb2_pre; - let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler); - let apb3 = hclk / config.apb3_pre; - - flash_setup(hclk, config.voltage_scale); - - // Set hpre - let hpre = config.ahb_pre.into(); - RCC.cfgr2().modify(|w| w.set_hpre(hpre)); - while RCC.cfgr2().read().hpre() != hpre {} - - // set ppre - RCC.cfgr2().modify(|w| { - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); - w.set_ppre3(config.apb3_pre.into()); - }); - - RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into())); - - RCC.cfgr().modify(|w| w.set_sw(sw)); - while RCC.cfgr().read().sws() != sw {} - - set_freqs(Clocks { - sys, - ahb1: hclk, - ahb2: hclk, - ahb3: hclk, - ahb4: hclk, - apb1, - apb2, - apb3, - apb1_tim, - apb2_tim, - adc: None, - }); -} - -struct PllInput { - hsi: Option, - hse: Option, - csi: Option, -} - -struct PllOutput { - p: Option, - #[allow(dead_code)] - q: Option, - #[allow(dead_code)] - r: Option, -} - -fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { - let Some(config) = config else { - // Stop PLL - RCC.cr().modify(|w| w.set_pllon(num, false)); - while RCC.cr().read().pllrdy(num) {} - - // "To save power when PLL1 is not used, the value of PLL1M must be set to 0."" - RCC.pllcfgr(num).write(|w| { - w.set_divm(0); - }); - - return PllOutput { - p: None, - q: None, - r: None, - }; - }; - - assert!(1 <= config.prediv && config.prediv <= 63); - assert!(4 <= config.mul && config.mul <= 512); - - let (in_clk, src) = match config.source { - PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI), - PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE), - PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI), - }; - - let ref_clk = in_clk / config.prediv as u32; - - let ref_range = match ref_clk.0 { - ..=1_999_999 => Pllrge::RANGE1, - ..=3_999_999 => Pllrge::RANGE2, - ..=7_999_999 => Pllrge::RANGE4, - ..=16_000_000 => Pllrge::RANGE8, - x => panic!("pll ref_clk out of range: {} mhz", x), - }; - - // The smaller range (150 to 420 MHz) must - // be chosen when the reference clock frequency is lower than 2 MHz. - let wide_allowed = ref_range != Pllrge::RANGE1; - - let vco_clk = ref_clk * config.mul; - let vco_range = match vco_clk.0 { - VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO, - VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO, - x => panic!("pll vco_clk out of range: {} mhz", x), - }; - - let p = config.divp.map(|div| { - assert!(1 <= div && div <= 128); - if num == 0 { - // on PLL1, DIVP must be even. - assert!(div % 2 == 0); - } - - vco_clk / div - }); - let q = config.divq.map(|div| { - assert!(1 <= div && div <= 128); - vco_clk / div - }); - let r = config.divr.map(|div| { - assert!(1 <= div && div <= 128); - vco_clk / div - }); - - RCC.pllcfgr(num).write(|w| { - w.set_pllsrc(src); - w.set_divm(config.prediv); - w.set_pllvcosel(vco_range); - w.set_pllrge(ref_range); - w.set_pllfracen(false); - w.set_pllpen(p.is_some()); - w.set_pllqen(q.is_some()); - w.set_pllren(r.is_some()); - }); - RCC.plldivr(num).write(|w| { - w.set_plln(config.mul - 1); - w.set_pllp((config.divp.unwrap_or(1) - 1) as u8); - w.set_pllq((config.divq.unwrap_or(1) - 1) as u8); - w.set_pllr((config.divr.unwrap_or(1) - 1) as u8); - }); - - RCC.cr().modify(|w| w.set_pllon(num, true)); - while !RCC.cr().read().pllrdy(num) {} - - PllOutput { p, q, r } -} - -fn flash_setup(clk: Hertz, vos: VoltageScale) { - // RM0481 Rev 1, table 37 - // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0 - // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz - // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz - // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz - // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz - // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz - // 5 2 170 to 200 MHz 210 to 250 MHz - - // See RM0433 Rev 7 Table 17. FLASH recommended number of wait - // states and programming delay - let (latency, wrhighfreq) = match (vos, clk.0) { - (VoltageScale::SCALE0, ..=42_000_000) => (0, 0), - (VoltageScale::SCALE0, ..=84_000_000) => (1, 0), - (VoltageScale::SCALE0, ..=126_000_000) => (2, 1), - (VoltageScale::SCALE0, ..=168_000_000) => (3, 1), - (VoltageScale::SCALE0, ..=210_000_000) => (4, 2), - (VoltageScale::SCALE0, ..=250_000_000) => (5, 2), - - (VoltageScale::SCALE1, ..=34_000_000) => (0, 0), - (VoltageScale::SCALE1, ..=68_000_000) => (1, 0), - (VoltageScale::SCALE1, ..=102_000_000) => (2, 1), - (VoltageScale::SCALE1, ..=136_000_000) => (3, 1), - (VoltageScale::SCALE1, ..=170_000_000) => (4, 2), - (VoltageScale::SCALE1, ..=200_000_000) => (5, 2), - - (VoltageScale::SCALE2, ..=30_000_000) => (0, 0), - (VoltageScale::SCALE2, ..=60_000_000) => (1, 0), - (VoltageScale::SCALE2, ..=90_000_000) => (2, 1), - (VoltageScale::SCALE2, ..=120_000_000) => (3, 1), - (VoltageScale::SCALE2, ..=150_000_000) => (4, 2), - - (VoltageScale::SCALE3, ..=20_000_000) => (0, 0), - (VoltageScale::SCALE3, ..=40_000_000) => (1, 0), - (VoltageScale::SCALE3, ..=60_000_000) => (2, 1), - (VoltageScale::SCALE3, ..=80_000_000) => (3, 1), - (VoltageScale::SCALE3, ..=100_000_000) => (4, 2), - - _ => unreachable!(), - }; - - debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); - - FLASH.acr().write(|w| { - w.set_wrhighfreq(wrhighfreq); - w.set_latency(latency); - }); - while FLASH.acr().read().latency() != latency {} -} diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs deleted file mode 100644 index ea26c26c1..000000000 --- a/embassy-stm32/src/rcc/h7.rs +++ /dev/null @@ -1,934 +0,0 @@ -use core::marker::PhantomData; - -use embassy_hal_internal::into_ref; -use stm32_metapac::pwr::vals::Vos; -use stm32_metapac::rcc::vals::{Mco1, Mco2}; - -pub use self::pll::PllConfig; -use crate::gpio::sealed::AFType; -use crate::gpio::Speed; -use crate::pac::rcc::vals::{Adcsel, Ckpersel, Hpre, Hsidiv, Pllsrc, Ppre, Sw, Timpre}; -use crate::pac::{PWR, RCC, SYSCFG}; -use crate::rcc::{set_freqs, Clocks}; -use crate::time::Hertz; -use crate::{peripherals, Peripheral}; - -/// HSI speed -pub const HSI_FREQ: Hertz = Hertz(64_000_000); - -/// CSI speed -pub const CSI_FREQ: Hertz = Hertz(4_000_000); - -/// HSI48 speed -pub const HSI48_FREQ: Hertz = Hertz(48_000_000); - -/// LSI speed -pub const LSI_FREQ: Hertz = Hertz(32_000); - -#[derive(Clone, Copy)] -pub enum VoltageScale { - Scale0, - Scale1, - Scale2, - Scale3, -} - -#[derive(Clone, Copy)] -pub enum AdcClockSource { - Pll2PCk, - Pll3RCk, - PerCk, -} - -impl AdcClockSource { - pub fn adcsel(&self) -> Adcsel { - match self { - AdcClockSource::Pll2PCk => Adcsel::PLL2_P, - AdcClockSource::Pll3RCk => Adcsel::PLL3_R, - AdcClockSource::PerCk => Adcsel::PER, - } - } -} - -impl Default for AdcClockSource { - fn default() -> Self { - Self::Pll2PCk - } -} - -/// Core clock frequencies -#[derive(Clone, Copy)] -pub struct CoreClocks { - pub hclk: Hertz, - pub pclk1: Hertz, - pub pclk2: Hertz, - pub pclk3: Hertz, - pub pclk4: Hertz, - pub ppre1: u8, - pub ppre2: u8, - pub ppre3: u8, - pub ppre4: u8, - pub csi_ck: Option, - pub hsi_ck: Option, - pub hsi48_ck: Option, - pub lsi_ck: Option, - pub per_ck: Option, - pub hse_ck: Option, - pub pll1_p_ck: Option, - pub pll1_q_ck: Option, - pub pll1_r_ck: Option, - pub pll2_p_ck: Option, - pub pll2_q_ck: Option, - pub pll2_r_ck: Option, - pub pll3_p_ck: Option, - pub pll3_q_ck: Option, - pub pll3_r_ck: Option, - pub timx_ker_ck: Option, - pub timy_ker_ck: Option, - pub adc_ker_ck: Option, - pub sys_ck: Hertz, - pub c_ck: Hertz, -} - -/// Configuration of the core clocks -#[non_exhaustive] -pub struct Config { - pub hse: Option, - pub bypass_hse: bool, - pub sys_ck: Option, - pub per_ck: Option, - pub hclk: Option, - pub pclk1: Option, - pub pclk2: Option, - pub pclk3: Option, - pub pclk4: Option, - pub pll1: PllConfig, - pub pll2: PllConfig, - pub pll3: PllConfig, - pub adc_clock_source: AdcClockSource, - pub voltage_scale: VoltageScale, -} - -impl Default for Config { - fn default() -> Self { - Self { - hse: None, - bypass_hse: false, - sys_ck: None, - per_ck: None, - hclk: None, - pclk1: None, - pclk2: None, - pclk3: None, - pclk4: None, - pll1: Default::default(), - pll2: Default::default(), - pll3: Default::default(), - adc_clock_source: Default::default(), - voltage_scale: VoltageScale::Scale1, - } - } -} - -/// Setup traceclk -/// Returns a pll1_r_ck -fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) { - let pll1_r_ck = match (sys_use_pll1_p, config.pll1.r_ck) { - // pll1_p_ck selected as system clock but pll1_r_ck not - // set. The traceclk mux is synchronous with the system - // clock mux, but has pll1_r_ck as an input. In order to - // keep traceclk running, we force a pll1_r_ck. - (true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)), - - // Either pll1 not selected as system clock, free choice - // of pll1_r_ck. Or pll1 is selected, assume user has set - // a suitable pll1_r_ck frequency. - _ => config.pll1.r_ck, - }; - config.pll1.r_ck = pll1_r_ck; -} - -/// Divider calculator for pclk 1 - 4 -/// -/// Returns real pclk, bits, ppre and the timer kernel clock -fn ppre_calculate( - requested_pclk: u32, - hclk: u32, - max_pclk: u32, - tim_pre: Option, -) -> (u32, u8, u8, Option) { - let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk { - 0 => panic!(), - 1 => (0b000, 1), - 2 => (0b100, 2), - 3..=5 => (0b101, 4), - 6..=11 => (0b110, 8), - _ => (0b111, 16), - }; - let real_pclk = hclk / u32::from(ppre); - assert!(real_pclk <= max_pclk); - - let tim_ker_clk = if let Some(tim_pre) = tim_pre { - let clk = match (bits, tim_pre) { - (0b101, Timpre::DEFAULTX2) => hclk / 2, - (0b110, Timpre::DEFAULTX4) => hclk / 2, - (0b110, Timpre::DEFAULTX2) => hclk / 4, - (0b111, Timpre::DEFAULTX4) => hclk / 4, - (0b111, Timpre::DEFAULTX2) => hclk / 8, - _ => hclk, - }; - Some(clk) - } else { - None - }; - (real_pclk, bits, ppre, tim_ker_clk) -} - -/// Setup sys_ck -/// Returns sys_ck frequency, and a pll1_p_ck -fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) { - // Compare available with wanted clocks - let sys_ck = config.sys_ck.unwrap_or(srcclk); - - if sys_ck != srcclk { - // The requested system clock is not the immediately available - // HSE/HSI clock. Perhaps there are other ways of obtaining - // the requested system clock (such as `HSIDIV`) but we will - // ignore those for now. - // - // Therefore we must use pll1_p_ck - let pll1_p_ck = match config.pll1.p_ck { - Some(p_ck) => { - assert!( - p_ck == sys_ck, - "Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck" - ); - Some(p_ck) - } - None => Some(sys_ck), - }; - config.pll1.p_ck = pll1_p_ck; - - (sys_ck, true) - } else { - // sys_ck is derived directly from a source clock - // (HSE/HSI). pll1_p_ck can be as requested - (sys_ck, false) - } -} - -fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { - use crate::pac::FLASH; - - // ACLK in MHz, round down and subtract 1 from integers. eg. - // 61_999_999 -> 61MHz - // 62_000_000 -> 61MHz - // 62_000_001 -> 62MHz - let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000; - - // See RM0433 Rev 7 Table 17. FLASH recommended number of wait - // states and programming delay - #[cfg(flash_h7)] - let (wait_states, progr_delay) = match vos { - // VOS 0 range VCORE 1.26V - 1.40V - VoltageScale::Scale0 => match rcc_aclk_mhz { - 0..=69 => (0, 0), - 70..=139 => (1, 1), - 140..=184 => (2, 1), - 185..=209 => (2, 2), - 210..=224 => (3, 2), - 225..=239 => (4, 2), - _ => (7, 3), - }, - // VOS 1 range VCORE 1.15V - 1.26V - VoltageScale::Scale1 => match rcc_aclk_mhz { - 0..=69 => (0, 0), - 70..=139 => (1, 1), - 140..=184 => (2, 1), - 185..=209 => (2, 2), - 210..=224 => (3, 2), - _ => (7, 3), - }, - // VOS 2 range VCORE 1.05V - 1.15V - VoltageScale::Scale2 => match rcc_aclk_mhz { - 0..=54 => (0, 0), - 55..=109 => (1, 1), - 110..=164 => (2, 1), - 165..=224 => (3, 2), - _ => (7, 3), - }, - // VOS 3 range VCORE 0.95V - 1.05V - VoltageScale::Scale3 => match rcc_aclk_mhz { - 0..=44 => (0, 0), - 45..=89 => (1, 1), - 90..=134 => (2, 1), - 135..=179 => (3, 2), - 180..=224 => (4, 2), - _ => (7, 3), - }, - }; - - // See RM0455 Rev 10 Table 16. FLASH recommended number of wait - // states and programming delay - #[cfg(flash_h7ab)] - let (wait_states, progr_delay) = match vos { - // VOS 0 range VCORE 1.25V - 1.35V - VoltageScale::Scale0 => match rcc_aclk_mhz { - 0..=42 => (0, 0), - 43..=84 => (1, 0), - 85..=126 => (2, 1), - 127..=168 => (3, 1), - 169..=210 => (4, 2), - 211..=252 => (5, 2), - 253..=280 => (6, 3), - _ => (7, 3), - }, - // VOS 1 range VCORE 1.15V - 1.25V - VoltageScale::Scale1 => match rcc_aclk_mhz { - 0..=38 => (0, 0), - 39..=76 => (1, 0), - 77..=114 => (2, 1), - 115..=152 => (3, 1), - 153..=190 => (4, 2), - 191..=225 => (5, 2), - _ => (7, 3), - }, - // VOS 2 range VCORE 1.05V - 1.15V - VoltageScale::Scale2 => match rcc_aclk_mhz { - 0..=34 => (0, 0), - 35..=68 => (1, 0), - 69..=102 => (2, 1), - 103..=136 => (3, 1), - 137..=160 => (4, 2), - _ => (7, 3), - }, - // VOS 3 range VCORE 0.95V - 1.05V - VoltageScale::Scale3 => match rcc_aclk_mhz { - 0..=22 => (0, 0), - 23..=44 => (1, 0), - 45..=66 => (2, 1), - 67..=88 => (3, 1), - _ => (7, 3), - }, - }; - - FLASH.acr().write(|w| { - w.set_wrhighfreq(progr_delay); - w.set_latency(wait_states) - }); - while FLASH.acr().read().latency() != wait_states {} -} - -pub enum McoClock { - Disabled, - Bypassed, - Divided(u8), -} - -impl McoClock { - fn into_raw(&self) -> u8 { - match self { - McoClock::Disabled => 0, - McoClock::Bypassed => 1, - McoClock::Divided(divisor) => { - if *divisor > 15 { - panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.") - } - *divisor - } - } - } -} - -#[derive(Copy, Clone)] -pub enum Mco1Source { - Hsi, - Lse, - Hse, - Pll1Q, - Hsi48, -} - -impl Default for Mco1Source { - fn default() -> Self { - Self::Hsi - } -} - -pub trait McoSource { - type Raw; - - fn into_raw(&self) -> Self::Raw; -} - -impl McoSource for Mco1Source { - type Raw = Mco1; - fn into_raw(&self) -> Self::Raw { - match self { - Mco1Source::Hsi => Mco1::HSI, - Mco1Source::Lse => Mco1::LSE, - Mco1Source::Hse => Mco1::HSE, - Mco1Source::Pll1Q => Mco1::PLL1_Q, - Mco1Source::Hsi48 => Mco1::HSI48, - } - } -} - -#[derive(Copy, Clone)] -pub enum Mco2Source { - SysClk, - Pll2Q, - Hse, - Pll1Q, - Csi, - Lsi, -} - -impl Default for Mco2Source { - fn default() -> Self { - Self::SysClk - } -} - -impl McoSource for Mco2Source { - type Raw = Mco2; - fn into_raw(&self) -> Self::Raw { - match self { - Mco2Source::SysClk => Mco2::SYSCLK, - Mco2Source::Pll2Q => Mco2::PLL2_P, - Mco2Source::Hse => Mco2::HSE, - Mco2Source::Pll1Q => Mco2::PLL1_P, - Mco2Source::Csi => Mco2::CSI, - Mco2Source::Lsi => Mco2::LSI, - } - } -} - -pub(crate) mod sealed { - pub trait McoInstance { - type Source; - unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); - } -} - -pub trait McoInstance: sealed::McoInstance + 'static {} - -pin_trait!(McoPin, McoInstance); - -macro_rules! impl_peri { - ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { - impl sealed::McoInstance for peripherals::$peri { - type Source = $source; - - unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { - RCC.cfgr().modify(|w| { - w.$set_source(source); - w.$set_prescaler(prescaler); - }); - } - } - - impl McoInstance for peripherals::$peri {} - }; -} - -impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); -impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); - -pub struct Mco<'d, T: McoInstance> { - phantom: PhantomData<&'d mut T>, -} - -impl<'d, T: McoInstance> Mco<'d, T> { - pub fn new( - _peri: impl Peripheral

+ 'd, - pin: impl Peripheral

> + 'd, - source: impl McoSource, - prescaler: McoClock, - ) -> Self { - into_ref!(pin); - - critical_section::with(|_| unsafe { - T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); - pin.set_speed(Speed::VeryHigh); - }); - - Self { phantom: PhantomData } - } -} - -pub(crate) unsafe fn init(mut config: Config) { - // NB. The lower bytes of CR3 can only be written once after - // POR, and must be written with a valid combination. Refer to - // RM0433 Rev 7 6.8.4. This is partially enforced by dropping - // `self` at the end of this method, but of course we cannot - // know what happened between the previous POR and here. - #[cfg(pwr_h7rm0433)] - PWR.cr3().modify(|w| { - w.set_scuen(true); - w.set_ldoen(true); - w.set_bypass(false); - }); - - #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] - PWR.cr3().modify(|w| { - // hardcode "Direct SPMS" for now, this is what works on nucleos with the - // default solderbridge configuration. - w.set_sden(true); - w.set_ldoen(false); - }); - - // Validate the supply configuration. If you are stuck here, it is - // because the voltages on your board do not match those specified - // in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset - // VOS = Scale 3, so check that the voltage on the VCAP pins = - // 1.0V. - info!("a"); - while !PWR.csr1().read().actvosrdy() {} - info!("b"); - - #[cfg(syscfg_h7)] - { - // in chips without the overdrive bit, we can go from any scale to any scale directly. - PWR.d3cr().modify(|w| { - w.set_vos(match config.voltage_scale { - VoltageScale::Scale0 => Vos::SCALE0, - VoltageScale::Scale1 => Vos::SCALE1, - VoltageScale::Scale2 => Vos::SCALE2, - VoltageScale::Scale3 => Vos::SCALE3, - }) - }); - while !PWR.d3cr().read().vosrdy() {} - } - - #[cfg(syscfg_h7od)] - { - match config.voltage_scale { - VoltageScale::Scale0 => { - // to go to scale0, we must go to Scale1 first... - PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1)); - while !PWR.d3cr().read().vosrdy() {} - - // Then enable overdrive. - critical_section::with(|_| { - RCC.apb4enr().modify(|w| w.set_syscfgen(true)); - SYSCFG.pwrcr().modify(|w| w.set_oden(1)); - }); - while !PWR.d3cr().read().vosrdy() {} - } - _ => { - // for all other scales, we can go directly. - PWR.d3cr().modify(|w| { - w.set_vos(match config.voltage_scale { - VoltageScale::Scale0 => unreachable!(), - VoltageScale::Scale1 => Vos::SCALE1, - VoltageScale::Scale2 => Vos::SCALE2, - VoltageScale::Scale3 => Vos::SCALE3, - }) - }); - while !PWR.d3cr().read().vosrdy() {} - } - } - } - - // Freeze the core clocks, returning a Core Clocks Distribution - // and Reset (CCDR) structure. The actual frequency of the clocks - // configured is returned in the `clocks` member of the CCDR - // structure. - // - // Note that `freeze` will never result in a clock _faster_ than - // that specified. It may result in a clock that is a factor of [1, - // 2) slower. - // - // `syscfg` is required to enable the I/O compensation cell. - // - // # Panics - // - // If a clock specification cannot be achieved within the - // hardware specification then this function will panic. This - // function may also panic if a clock specification can be - // achieved, but the mechanism for doing so is not yet - // implemented here. - - let srcclk = config.hse.unwrap_or(HSI_FREQ); // Available clocks - let (sys_ck, sys_use_pll1_p) = sys_ck_setup(&mut config, srcclk); - - // Configure traceclk from PLL if needed - traceclk_setup(&mut config, sys_use_pll1_p); - - let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0); - let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1); - let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2); - - let sys_ck = if sys_use_pll1_p { - Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup - } else { - sys_ck - }; - - // This routine does not support HSIDIV != 1. To - // do so it would need to ensure all PLLxON bits are clear - // before changing the value of HSIDIV - let cr = RCC.cr().read(); - assert!(cr.hsion()); - assert!(cr.hsidiv() == Hsidiv::DIV1); - - RCC.csr().modify(|w| w.set_lsion(true)); - while !RCC.csr().read().lsirdy() {} - - // per_ck from HSI by default - let (per_ck, ckpersel) = match (config.per_ck == config.hse, config.per_ck) { - (true, Some(hse)) => (hse, Ckpersel::HSE), // HSE - (_, Some(CSI_FREQ)) => (CSI_FREQ, Ckpersel::CSI), // CSI - _ => (HSI_FREQ, Ckpersel::HSI), // HSI - }; - - // D1 Core Prescaler - // Set to 1 - let d1cpre_bits = 0; - let d1cpre_div = 1; - let sys_d1cpre_ck = sys_ck.0 / d1cpre_div; - - // Refer to part datasheet "General operating conditions" - // table for (rev V). We do not assert checks for earlier - // revisions which may have lower limits. - let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match config.voltage_scale { - VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000), - VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000), - VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000), - VoltageScale::Scale3 => (200_000_000, 100_000_000, 50_000_000), - }; - assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max); - - let rcc_hclk = config.hclk.map(|v| v.0).unwrap_or(sys_d1cpre_ck / 2); - assert!(rcc_hclk <= rcc_hclk_max); - - // Estimate divisor - let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk { - 0 => panic!(), - 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 AXI and AHB clock - let rcc_hclk = sys_d1cpre_ck / hpre_div; - assert!(rcc_hclk <= rcc_hclk_max); - let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7 - // Timer prescaler selection - let timpre = Timpre::DEFAULTX2; - - let requested_pclk1 = config.pclk1.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); - let (rcc_pclk1, ppre1_bits, ppre1, rcc_timerx_ker_ck) = - ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre)); - - let requested_pclk2 = config.pclk2.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); - let (rcc_pclk2, ppre2_bits, ppre2, rcc_timery_ker_ck) = - ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre)); - - let requested_pclk3 = config.pclk3.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); - let (rcc_pclk3, ppre3_bits, ppre3, _) = ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None); - - let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); - let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None); - - // Start switching clocks ------------------- - - // Ensure CSI is on and stable - RCC.cr().modify(|w| w.set_csion(true)); - while !RCC.cr().read().csirdy() {} - - // Ensure HSI48 is on and stable - RCC.cr().modify(|w| w.set_hsi48on(true)); - while !RCC.cr().read().hsi48on() {} - - // XXX: support MCO ? - - let hse_ck = match config.hse { - Some(hse) => { - // Ensure HSE is on and stable - RCC.cr().modify(|w| { - w.set_hseon(true); - w.set_hsebyp(config.bypass_hse); - }); - while !RCC.cr().read().hserdy() {} - Some(hse) - } - None => None, - }; - - let pllsrc = if config.hse.is_some() { Pllsrc::HSE } else { Pllsrc::HSI }; - RCC.pllckselr().modify(|w| w.set_pllsrc(pllsrc)); - - let enable_pll = |pll| { - RCC.cr().modify(|w| w.set_pllon(pll, true)); - while !RCC.cr().read().pllrdy(pll) {} - }; - - if pll1_p_ck.is_some() { - enable_pll(0); - } - - if pll2_p_ck.is_some() { - enable_pll(1); - } - - if pll3_p_ck.is_some() { - enable_pll(2); - } - - // Core Prescaler / AHB Prescaler / APB3 Prescaler - RCC.d1cfgr().modify(|w| { - w.set_d1cpre(Hpre::from_bits(d1cpre_bits)); - w.set_d1ppre(Ppre::from_bits(ppre3_bits)); - w.set_hpre(hpre_bits) - }); - // Ensure core prescaler value is valid before future lower - // core voltage - while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {} - - flash_setup(rcc_aclk, config.voltage_scale); - - // APB1 / APB2 Prescaler - RCC.d2cfgr().modify(|w| { - w.set_d2ppre1(Ppre::from_bits(ppre1_bits)); - w.set_d2ppre2(Ppre::from_bits(ppre2_bits)); - }); - - // APB4 Prescaler - RCC.d3cfgr().modify(|w| w.set_d3ppre(Ppre::from_bits(ppre4_bits))); - - // Peripheral Clock (per_ck) - RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel)); - - // ADC clock MUX - RCC.d3ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel())); - - let adc_ker_ck = match config.adc_clock_source { - AdcClockSource::Pll2PCk => pll2_p_ck.map(Hertz), - AdcClockSource::Pll3RCk => pll3_r_ck.map(Hertz), - AdcClockSource::PerCk => Some(per_ck), - }; - - // Set timer clocks prescaler setting - RCC.cfgr().modify(|w| w.set_timpre(timpre)); - - // Select system clock source - let sw = match (sys_use_pll1_p, config.hse.is_some()) { - (true, _) => Sw::PLL1, - (false, true) => Sw::HSE, - _ => Sw::HSI, - }; - RCC.cfgr().modify(|w| w.set_sw(sw)); - while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} - - // IO compensation cell - Requires CSI clock and SYSCFG - assert!(RCC.cr().read().csirdy()); - RCC.apb4enr().modify(|w| w.set_syscfgen(true)); - - // Enable the compensation cell, using back-bias voltage code - // provide by the cell. - critical_section::with(|_| { - SYSCFG.cccsr().modify(|w| { - w.set_en(true); - w.set_cs(false); - w.set_hslv(false); - }) - }); - while !SYSCFG.cccsr().read().ready() {} - - let core_clocks = CoreClocks { - hclk: Hertz(rcc_hclk), - pclk1: Hertz(rcc_pclk1), - pclk2: Hertz(rcc_pclk2), - pclk3: Hertz(rcc_pclk3), - pclk4: Hertz(rcc_pclk4), - ppre1, - ppre2, - ppre3, - ppre4, - csi_ck: Some(CSI_FREQ), - hsi_ck: Some(HSI_FREQ), - hsi48_ck: Some(HSI48_FREQ), - lsi_ck: Some(LSI_FREQ), - per_ck: Some(per_ck), - hse_ck, - pll1_p_ck: pll1_p_ck.map(Hertz), - pll1_q_ck: pll1_q_ck.map(Hertz), - pll1_r_ck: pll1_r_ck.map(Hertz), - pll2_p_ck: pll2_p_ck.map(Hertz), - pll2_q_ck: pll2_q_ck.map(Hertz), - pll2_r_ck: pll2_r_ck.map(Hertz), - pll3_p_ck: pll3_p_ck.map(Hertz), - pll3_q_ck: pll3_q_ck.map(Hertz), - pll3_r_ck: pll3_r_ck.map(Hertz), - timx_ker_ck: rcc_timerx_ker_ck.map(Hertz), - timy_ker_ck: rcc_timery_ker_ck.map(Hertz), - adc_ker_ck, - sys_ck, - c_ck: Hertz(sys_d1cpre_ck), - }; - - set_freqs(Clocks { - sys: core_clocks.c_ck, - ahb1: core_clocks.hclk, - ahb2: core_clocks.hclk, - ahb3: core_clocks.hclk, - ahb4: core_clocks.hclk, - apb1: core_clocks.pclk1, - apb2: core_clocks.pclk2, - apb4: core_clocks.pclk4, - apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1), - apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2), - adc: core_clocks.adc_ker_ck, - }); -} - -mod pll { - use super::{Hertz, RCC}; - - const VCO_MIN: u32 = 150_000_000; - const VCO_MAX: u32 = 420_000_000; - - #[derive(Default)] - pub struct PllConfig { - pub p_ck: Option, - pub q_ck: Option, - pub r_ck: Option, - } - - pub(super) struct PllConfigResults { - pub ref_x_ck: u32, - pub pll_x_m: u32, - pub pll_x_p: u32, - pub vco_ck_target: u32, - } - - fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) { - let pll_x_p = if plln == 0 { - if output > VCO_MAX / 2 { - 1 - } else { - ((VCO_MAX / output) | 1) - 1 // Must be even or unity - } - } else { - // Specific to PLL2/3, will subtract 1 later - if output > VCO_MAX / 2 { - 1 - } else { - VCO_MAX / output - } - }; - - let vco_ck = output * pll_x_p; - - assert!(pll_x_p < 128); - assert!(vco_ck >= VCO_MIN); - assert!(vco_ck <= VCO_MAX); - - (vco_ck, pll_x_p) - } - - /// # Safety - /// - /// Must have exclusive access to the RCC register block - fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults { - use crate::pac::rcc::vals::{Pllrge, Pllvcosel}; - - let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln); - - // Input divisor, resulting in a reference clock in the range - // 1 to 2 MHz. Choose the highest reference clock (lowest m) - let pll_x_m = (pll_src + 1_999_999) / 2_000_000; - assert!(pll_x_m < 64); - - // Calculate resulting reference clock - let ref_x_ck = pll_src / pll_x_m; - assert!((1_000_000..=2_000_000).contains(&ref_x_ck)); - - RCC.pllcfgr().modify(|w| { - w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO); - w.set_pllrge(plln, Pllrge::RANGE1); - }); - PllConfigResults { - ref_x_ck, - pll_x_m, - pll_x_p, - vco_ck_target, - } - } - - /// # Safety - /// - /// Must have exclusive access to the RCC register block - pub(super) fn pll_setup(pll_src: u32, config: &PllConfig, plln: usize) -> (Option, Option, Option) { - use crate::pac::rcc::vals::Divp; - - match config.p_ck { - Some(requested_output) => { - let config_results = vco_setup(pll_src, requested_output.0, plln); - let PllConfigResults { - ref_x_ck, - pll_x_m, - pll_x_p, - vco_ck_target, - } = config_results; - - RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8)); - - // Feedback divider. Integer only - let pll_x_n = vco_ck_target / ref_x_ck; - assert!(pll_x_n >= 4); - assert!(pll_x_n <= 512); - RCC.plldivr(plln).modify(|w| w.set_divn1((pll_x_n - 1) as u16)); - - // No FRACN - RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false)); - let vco_ck = ref_x_ck * pll_x_n; - - RCC.plldivr(plln) - .modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8))); - RCC.pllcfgr().modify(|w| w.set_divpen(plln, true)); - - // Calulate additional output dividers - let q_ck = match config.q_ck { - Some(Hertz(ck)) if ck > 0 => { - let div = (vco_ck + ck - 1) / ck; - RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8)); - RCC.pllcfgr().modify(|w| w.set_divqen(plln, true)); - Some(vco_ck / div) - } - _ => None, - }; - let r_ck = match config.r_ck { - Some(Hertz(ck)) if ck > 0 => { - let div = (vco_ck + ck - 1) / ck; - RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8)); - RCC.pllcfgr().modify(|w| w.set_divren(plln, true)); - Some(vco_ck / div) - } - _ => None, - }; - - (Some(vco_ck / pll_x_p), q_ck, r_ck) - } - None => { - assert!( - config.q_ck.is_none(), - "Must set PLL P clock for Q clock to take effect!" - ); - assert!( - config.r_ck.is_none(), - "Must set PLL P clock for R clock to take effect!" - ); - (None, None, None) - } - } - } -} diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs new file mode 100644 index 000000000..2453ed821 --- /dev/null +++ b/embassy-stm32/src/rcc/mco.rs @@ -0,0 +1,71 @@ +use core::marker::PhantomData; + +use embassy_hal_internal::into_ref; + +use crate::gpio::sealed::AFType; +use crate::gpio::Speed; +pub use crate::pac::rcc::vals::{Mco1 as Mco1Source, Mco2 as Mco2Source}; +use crate::pac::RCC; +use crate::{peripherals, Peripheral}; + +pub(crate) mod sealed { + pub trait McoInstance { + type Source; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); + } +} + +pub trait McoInstance: sealed::McoInstance + 'static {} + +pin_trait!(McoPin, McoInstance); + +macro_rules! impl_peri { + ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { + impl sealed::McoInstance for peripherals::$peri { + type Source = $source; + + unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { + RCC.cfgr().modify(|w| { + w.$set_source(source); + w.$set_prescaler(prescaler); + }); + } + } + + impl McoInstance for peripherals::$peri {} + }; +} + +impl_peri!(MCO1, Mco1Source, set_mco1, set_mco1pre); +impl_peri!(MCO2, Mco2Source, set_mco2, set_mco2pre); + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + /// Create a new MCO instance. + /// + /// `prescaler` must be between 1 and 15. + pub fn new( + _peri: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + source: T::Source, + prescaler: u8, + ) -> Self { + into_ref!(pin); + + assert!( + 1 <= prescaler && prescaler <= 15, + "Mco prescaler must be between 1 and 15. Refer to the reference manual for more information." + ); + + critical_section::with(|_| unsafe { + T::apply_clock_settings(source, prescaler); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_speed(Speed::VeryHigh); + }); + + Self { phantom: PhantomData } + } +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index ff9b9bac8..0d6b0e308 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -1,12 +1,17 @@ #![macro_use] -pub(crate) mod bd; -pub mod bus; use core::mem::MaybeUninit; pub use crate::rcc::bd::RtcClockSource; use crate::time::Hertz; +pub(crate) mod bd; +mod bus; +#[cfg(any(stm32h5, stm32h7))] +mod mco; +#[cfg(any(stm32h5, stm32h7))] +pub use mco::*; + #[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")] @@ -16,7 +21,7 @@ use crate::time::Hertz; #[cfg_attr(rcc_c0, path = "c0.rs")] #[cfg_attr(rcc_g0, path = "g0.rs")] #[cfg_attr(rcc_g4, path = "g4.rs")] -#[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")] +#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab), path = "h.rs")] #[cfg_attr(rcc_l0, path = "l0.rs")] #[cfg_attr(rcc_l1, path = "l1.rs")] #[cfg_attr(rcc_l4, path = "l4.rs")] @@ -25,7 +30,6 @@ use crate::time::Hertz; #[cfg_attr(rcc_wb, path = "wb.rs")] #[cfg_attr(rcc_wba, path = "wba.rs")] #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] -#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] mod _version; pub use _version::*; #[cfg(feature = "low-power")] @@ -53,7 +57,7 @@ pub struct Clocks { pub apb2: Hertz, #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2_tim: Hertz, - #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))] + #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5))] pub apb3: Hertz, #[cfg(any(rcc_h7, rcc_h7ab))] pub apb4: Hertz, diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 5f1e62d0a..16bf5d949 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -100,6 +100,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.connect(remote_endpoint).await; if let Err(e) = r { info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(1)).await; continue; } info!("connected!"); @@ -108,7 +109,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 01c38106e..93c97c8ee 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -101,6 +101,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.connect(remote_endpoint).await; if let Err(e) = r { info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(1)).await; continue; } info!("connected!"); @@ -109,7 +110,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 41ef2acaa..4e92d0647 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -53,7 +53,7 @@ async fn main(spawner: Spawner) -> ! { config.rcc.apb2_pre = APBPrescaler::DIV1; config.rcc.apb3_pre = APBPrescaler::DIV1; config.rcc.sys = Sysclk::Pll1P; - config.rcc.voltage_scale = VoltageScale::SCALE0; + config.rcc.voltage_scale = VoltageScale::Scale0; let p = embassy_stm32::init(config); info!("Hello World!"); @@ -128,7 +128,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 63c694aff..cbe540a06 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -40,7 +40,7 @@ async fn main(_spawner: Spawner) { config.rcc.apb2_pre = APBPrescaler::DIV2; config.rcc.apb3_pre = APBPrescaler::DIV4; config.rcc.sys = Sysclk::Pll1P; - config.rcc.voltage_scale = VoltageScale::SCALE0; + config.rcc.voltage_scale = VoltageScale::Scale0; let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index 0e1e28c72..77922d4bc 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -5,8 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; -use embassy_stm32::rcc::AdcClockSource; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,10 +12,34 @@ 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(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.per_ck = Some(mhz(64)); - config.rcc.adc_clock_source = AdcClockSource::PerCk; + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; + } let mut p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 6f75a0630..de8ddc292 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -6,8 +6,8 @@ use embassy_executor::Spawner; use embassy_stm32::dcmi::{self, *}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::i2c::I2c; -use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; -use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::rcc::{Mco, Mco1Source}; +use embassy_stm32::time::khz; use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; use embassy_time::{Duration, Timer}; use ov7725::*; @@ -26,17 +26,30 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(400)); - config.rcc.pll1.q_ck = Some(mhz(100)); - config.rcc.pclk1 = Some(mhz(100)); - config.rcc.pclk2 = Some(mhz(100)); - config.rcc.pclk3 = Some(mhz(100)); - config.rcc.pclk4 = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // 100mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); defmt::info!("Hello World!"); - let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); + let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 3); let mut led = Output::new(p.PE3, Level::High, Speed::Low); let cam_i2c = I2c::new( diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index ee078286b..93df7a319 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -6,7 +6,6 @@ use cortex_m_rt::entry; use defmt::*; use embassy_stm32::dac::{DacCh1, DacChannel, Value}; use embassy_stm32::dma::NoDma; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; @@ -15,9 +14,34 @@ fn main() -> ! { info!("Hello World, dude!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; + } let p = embassy_stm32::init(config); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index a9cb5d1ed..8c921abca 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -8,7 +8,7 @@ use embassy_stm32::dac::{DacChannel, ValueArray}; use embassy_stm32::pac::timer::vals::{Mms, Opm}; use embassy_stm32::peripherals::{TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; -use embassy_stm32::time::{mhz, Hertz}; +use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Basic16bitInstance; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; @@ -22,9 +22,34 @@ pub type Dac2Type = #[embassy_executor::main] async fn main(spawner: Spawner) { let mut config = embassy_stm32::Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(100)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; + } // Initialize the board and obtain a Peripherals instance let p: embassy_stm32::Peripherals = embassy_stm32::init(config); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index e691c6d06..1b5d71ed3 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -10,7 +10,6 @@ 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::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io_async::Write; @@ -33,9 +32,27 @@ 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(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for RNG + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); @@ -102,6 +119,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.connect(remote_endpoint).await; if let Err(e) = r { info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(1)).await; continue; } info!("connected!"); @@ -109,7 +127,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index ebef54c3c..3abd31c73 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -10,7 +10,6 @@ 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::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io_async::Write; @@ -34,9 +33,27 @@ 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(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for RNG + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); @@ -108,7 +125,7 @@ async fn main(spawner: Spawner) -> ! { let r = connection.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - continue; + break; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h7/src/bin/fmc.rs b/examples/stm32h7/src/bin/fmc.rs index 85c690fe6..de0b351df 100644 --- a/examples/stm32h7/src/bin/fmc.rs +++ b/examples/stm32h7/src/bin/fmc.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::fmc::Fmc; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -13,9 +12,26 @@ 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(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // 100mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 45b0872b5..a1e955c39 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::Speed; -use embassy_stm32::time::{khz, mhz, Hertz}; +use embassy_stm32::time::{khz, Hertz}; use embassy_stm32::timer::*; use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; @@ -15,13 +15,27 @@ 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(mhz(400)); - config.rcc.hclk = Some(mhz(400)); - config.rcc.pll1.q_ck = Some(mhz(100)); - config.rcc.pclk1 = Some(mhz(100)); - config.rcc.pclk2 = Some(mhz(100)); - config.rcc.pclk3 = Some(mhz(100)); - config.rcc.pclk4 = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for RNG + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // 100 Mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/mco.rs b/examples/stm32h7/src/bin/mco.rs index 036455d5e..9d6d805ae 100644 --- a/examples/stm32h7/src/bin/mco.rs +++ b/examples/stm32h7/src/bin/mco.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; +use embassy_stm32::rcc::{Mco, Mco1Source}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::High, Speed::Low); - let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(8)); + let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 8); loop { info!("high"); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index aa5ec1bcf..5c8e57aa2 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; -use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; use embassy_stm32::Config; @@ -15,13 +15,26 @@ 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(mhz(400)); - config.rcc.hclk = Some(mhz(400)); - config.rcc.pll1.q_ck = Some(mhz(100)); - config.rcc.pclk1 = Some(mhz(100)); - config.rcc.pclk2 = Some(mhz(100)); - config.rcc.pclk3 = Some(mhz(100)); - config.rcc.pclk4 = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index 7c8c50eca..af1d6ebb8 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, peripherals, rng}; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -14,7 +14,9 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(Default::default()); + let mut config = Config::default(); + config.rcc.hsi48 = true; // needed for RNG. + let p = embassy_stm32::init(config); info!("Hello World!"); let mut rng = Rng::new(p.RNG, Irqs); diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index ce91b6b1c..752aefdf7 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -16,7 +16,26 @@ bind_interrupts!(struct Irqs { #[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.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(4), // default clock chosen by SDMMCSEL. 200 Mhz + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); info!("Hello World!"); diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 28bba2b8d..9fe46f031 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -38,9 +38,26 @@ fn main() -> ! { info!("Hello World!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(4), // used by SPI3. 100Mhz. + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index f6e30cfa5..88d65d5be 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -34,9 +34,26 @@ fn main() -> ! { info!("Hello World!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(4), // used by SPI3. 100Mhz. + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } let p = embassy_stm32::init(config); let mut spi_config = spi::Config::default(); diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 97291f60c..14de43568 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -4,7 +4,6 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::time::mhz; 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 +21,27 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut config = Config::default(); - config.rcc.sys_ck = Some(mhz(400)); - config.rcc.hclk = Some(mhz(200)); - config.rcc.pll1.q_ck = Some(mhz(100)); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.hsi48 = true; // needed for USB + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + } 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 ca5cb43ac..3a1b5c3ec 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -31,9 +31,32 @@ pub fn config() -> Config { #[cfg(feature = "stm32h755zi")] { - config.rcc.sys_ck = Some(Hertz(400_000_000)); - config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); - config.rcc.adc_clock_source = embassy_stm32::rcc::AdcClockSource::PerCk; + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(Hsi::Mhz64); + config.rcc.csi = true; + config.rcc.pll_src = PllSource::Hsi; + config.rcc.pll1 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(2), + divq: Some(8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + prediv: 4, + mul: 50, + divp: Some(8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::Pll1P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.adc_clock_source = AdcClockSource::PLL2_P; } #[cfg(feature = "stm32u585ai")]