diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 290bcf6a..50ccd793 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-01a757e40df688efcda23607185640e1c2396ba9" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73e3f8a965a01fd5a168c3543b93ce49d475e130" } 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-01a757e40df688efcda23607185640e1c2396ba9", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73e3f8a965a01fd5a168c3543b93ce49d475e130", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 43c29281..aceafc49 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,7 +1,8 @@ use crate::pac::rcc::regs::Cfgr; +use crate::pac::rcc::vals::Msirgsel; pub use crate::pac::rcc::vals::{ - Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, - Pllr as PllRDiv, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc, + Clk48sel as Clk48Src, Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, + Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc, }; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -12,6 +13,9 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); #[derive(Clone, Copy)] pub struct Pll { + /// PLL source + pub source: PLLSource, + /// PLL pre-divider (DIVM). pub prediv: PllPreDiv, @@ -32,11 +36,10 @@ pub struct Config { pub msi: Option, pub hsi16: bool, pub hse: Option, - #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + #[cfg(not(any(stm32l47x, stm32l48x)))] pub hsi48: bool, // pll - pub pll_src: PLLSource, pub pll: Option, pub pllsai1: Option, #[cfg(any( @@ -50,6 +53,9 @@ pub struct Config { pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, + // muxes + pub clk48_src: Clk48Src, + // low speed LSI/LSE/RTC pub ls: super::LsConfig, } @@ -65,7 +71,6 @@ impl Default for Config { ahb_pre: AHBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1, - pll_src: PLLSource::NONE, pll: None, pllsai1: None, #[cfg(any( @@ -73,7 +78,8 @@ impl Default for Config { ))] pllsai2: None, #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] - hsi48: false, + hsi48: true, + clk48_src: Clk48Src::HSI48, ls: Default::default(), } } @@ -84,7 +90,7 @@ pub(crate) unsafe fn init(config: Config) { if !RCC.cr().read().msion() { // Turn on MSI and configure it to 4MHz. RCC.cr().modify(|w| { - w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0]. + w.set_msirgsel(Msirgsel::CR); w.set_msirange(MSIRange::RANGE4M); w.set_msipllen(false); w.set_msion(true) @@ -106,7 +112,7 @@ pub(crate) unsafe fn init(config: Config) { // Enable MSI RCC.cr().write(|w| { w.set_msirange(range); - w.set_msirgsel(true); + w.set_msirgsel(Msirgsel::CR); w.set_msion(true); // If LSE is enabled, enable calibration of MSI @@ -115,9 +121,7 @@ pub(crate) unsafe fn init(config: Config) { while !RCC.cr().read().msirdy() {} // Enable as clock source for USB, RNG if running at 48 MHz - if range == MSIRange::RANGE48M { - RCC.ccipr().modify(|w| w.set_clk48sel(0b11)); - } + if range == MSIRange::RANGE48M {} msirange_to_hertz(range) }); @@ -136,154 +140,66 @@ pub(crate) unsafe fn init(config: Config) { freq }); - #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] - let _hsi48 = config.hsi48.then(|| { + #[cfg(not(any(stm32l47x, stm32l48x)))] + let hsi48 = config.hsi48.then(|| { RCC.crrcr().modify(|w| w.set_hsi48on(true)); while !RCC.crrcr().read().hsi48rdy() {} - // Enable as clock source for USB, RNG and SDMMC - RCC.ccipr().modify(|w| w.set_clk48sel(0)); - Hertz(48_000_000) }); + #[cfg(any(stm32l47x, stm32l48x))] + let hsi48 = None; - let pll_src = match config.pll_src { - PLLSource::NONE => None, - PLLSource::HSE => hse, - PLLSource::HSI16 => hsi16, - PLLSource::MSI => msi, + let _plls = [ + &config.pll, + &config.pllsai1, + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + &config.pllsai2, + ]; + + // L4 has shared PLLSRC, PLLM, check it's equal in all PLLs. + #[cfg(all(stm32l4, not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))))] + match get_equal(_plls.into_iter().flatten().map(|p| (p.source, p.prediv))) { + Err(()) => panic!("Source must be equal across all enabled PLLs."), + Ok(None) => {} + Ok(Some((source, prediv))) => RCC.pllcfgr().write(|w| { + w.set_pllm(prediv); + w.set_pllsrc(source); + }), }; - let mut _pllp = None; - let mut _pllq = None; - let mut _pllr = None; - if let Some(pll) = config.pll { - let pll_src = pll_src.unwrap(); - - // Disable PLL - RCC.cr().modify(|w| w.set_pllon(false)); - while RCC.cr().read().pllrdy() {} - - let vco_freq = pll_src / pll.prediv * pll.mul; - - _pllp = pll.divp.map(|div| vco_freq / div); - _pllq = pll.divq.map(|div| vco_freq / div); - _pllr = pll.divr.map(|div| vco_freq / div); - - RCC.pllcfgr().write(move |w| { - w.set_plln(pll.mul); - w.set_pllm(pll.prediv); - w.set_pllsrc(config.pll_src); - if let Some(divp) = pll.divp { - w.set_pllp(divp); - w.set_pllpen(true); - } - if let Some(divq) = pll.divq { - w.set_pllq(divq); - w.set_pllqen(true); - } - if let Some(divr) = pll.divr { - w.set_pllr(divr); - w.set_pllren(true); - } - }); - - if _pllq == Some(Hertz(48_000_000)) { - RCC.ccipr().modify(|w| w.set_clk48sel(0b10)); - } - - // Enable PLL - RCC.cr().modify(|w| w.set_pllon(true)); - while !RCC.cr().read().pllrdy() {} - } else { - // even if we're not using the main pll, set the source for pllsai - RCC.pllcfgr().write(move |w| { - w.set_pllsrc(config.pll_src); - }); - } - - if let Some(pll) = config.pllsai1 { - let pll_src = pll_src.unwrap(); - - // Disable PLL - RCC.cr().modify(|w| w.set_pllsai1on(false)); - while RCC.cr().read().pllsai1rdy() {} - - let vco_freq = pll_src / pll.prediv * pll.mul; - - let _pllp = pll.divp.map(|div| vco_freq / div); - let _pllq = pll.divq.map(|div| vco_freq / div); - let _pllr = pll.divr.map(|div| vco_freq / div); - - RCC.pllsai1cfgr().write(move |w| { - w.set_plln(pll.mul); - w.set_pllm(pll.prediv); - if let Some(divp) = pll.divp { - w.set_pllp(divp); - w.set_pllpen(true); - } - if let Some(divq) = pll.divq { - w.set_pllq(divq); - w.set_pllqen(true); - } - if let Some(divr) = pll.divr { - w.set_pllr(divr); - w.set_pllren(true); - } - }); - - if _pllq == Some(Hertz(48_000_000)) { - RCC.ccipr().modify(|w| w.set_clk48sel(0b01)); - } - - // Enable PLL - RCC.cr().modify(|w| w.set_pllsai1on(true)); - while !RCC.cr().read().pllsai1rdy() {} - } + // L4+ has shared PLLSRC, check it's equal in all PLLs. + #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))] + match get_equal(_plls.into_iter().flatten().map(|p| p.source)) { + Err(()) => panic!("Source must be equal across all enabled PLLs."), + Ok(None) => {} + Ok(Some(source)) => RCC.pllcfgr().write(|w| { + w.set_pllsrc(source); + }), + }; + let pll_input = PllInput { hse, hsi16, msi }; + let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); + let pllsai1 = init_pll(PllInstance::Pllsai1, config.pllsai1, &pll_input); #[cfg(any( stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx ))] - if let Some(pll) = config.pllsai2 { - let pll_src = pll_src.unwrap(); - - // Disable PLL - RCC.cr().modify(|w| w.set_pllsai2on(false)); - while RCC.cr().read().pllsai2rdy() {} - - let vco_freq = pll_src / pll.prediv * pll.mul; - - let _pllp = pll.divp.map(|div| vco_freq / div); - let _pllq = pll.divq.map(|div| vco_freq / div); - let _pllr = pll.divr.map(|div| vco_freq / div); - - RCC.pllsai2cfgr().write(move |w| { - w.set_plln(pll.mul); - w.set_pllm(pll.prediv); - if let Some(divp) = pll.divp { - w.set_pllp(divp); - w.set_pllpen(true); - } - if let Some(divq) = pll.divq { - w.set_pllq(divq); - w.set_pllqen(true); - } - if let Some(divr) = pll.divr { - w.set_pllr(divr); - w.set_pllren(true); - } - }); - - // Enable PLL - RCC.cr().modify(|w| w.set_pllsai2on(true)); - while !RCC.cr().read().pllsai2rdy() {} - } + let _pllsai2 = init_pll(PllInstance::Pllsai2, config.pllsai2, &pll_input); let sys_clk = match config.mux { ClockSrc::HSE => hse.unwrap(), ClockSrc::HSI16 => hsi16.unwrap(), ClockSrc::MSI => msi.unwrap(), - ClockSrc::PLL => _pllr.unwrap(), + ClockSrc::PLL => pll._r.unwrap(), + }; + + let _clk48 = match config.clk48_src { + Clk48Src::HSI48 => hsi48, + Clk48Src::MSI => msi, + Clk48Src::PLLSAI1_Q => pllsai1._q, + Clk48Src::PLL_Q => pll._q, }; #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))] @@ -357,3 +273,136 @@ fn msirange_to_hertz(range: MSIRange) -> Hertz { _ => unreachable!(), } } + +fn get_equal(mut iter: impl Iterator) -> Result, ()> { + let Some(x) = iter.next() else { return Ok(None) }; + if !iter.all(|y| y == x) { + return Err(()); + } + return Ok(Some(x)); +} + +struct PllInput { + hsi16: Option, + hse: Option, + msi: Option, +} + +#[derive(Default)] +struct PllOutput { + _p: Option, + _q: Option, + _r: Option, +} + +#[derive(PartialEq, Eq, Clone, Copy)] +enum PllInstance { + Pll, + Pllsai1, + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + Pllsai2, +} + +fn init_pll(instance: PllInstance, config: Option, input: &PllInput) -> PllOutput { + // Disable PLL + match instance { + PllInstance::Pll => { + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} + } + PllInstance::Pllsai1 => { + RCC.cr().modify(|w| w.set_pllsai1on(false)); + while RCC.cr().read().pllsai1rdy() {} + } + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + PllInstance::Pllsai2 => { + RCC.cr().modify(|w| w.set_pllsai2on(false)); + while RCC.cr().read().pllsai2rdy() {} + } + } + + let Some(pll) = config else { return PllOutput::default() }; + + let pll_src = match pll.source { + PLLSource::NONE => panic!("must not select PLL source as NONE"), + PLLSource::HSE => input.hse, + PLLSource::HSI16 => input.hsi16, + PLLSource::MSI => input.msi, + }; + + let pll_src = pll_src.unwrap(); + + let vco_freq = pll_src / pll.prediv * pll.mul; + + 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); + $w.set_pllpen(true); + } + if let Some(divq) = pll.divq { + $w.set_pllq(divq); + $w.set_pllqen(true); + } + if let Some(divr) = pll.divr { + $w.set_pllr(divr); + $w.set_pllren(true); + } + }; + } + + match instance { + PllInstance::Pll => RCC.pllcfgr().write(|w| { + w.set_pllm(pll.prediv); + w.set_pllsrc(pll.source); + write_fields!(w); + }), + PllInstance::Pllsai1 => RCC.pllsai1cfgr().write(|w| { + #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx, stm32l5))] + w.set_pllm(pll.prediv); + #[cfg(stm32l5)] + w.set_pllsrc(pll.source); + write_fields!(w); + }), + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + PllInstance::Pllsai2 => RCC.pllsai2cfgr().write(|w| { + #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx, stm32l5))] + w.set_pllm(pll.prediv); + #[cfg(stm32l5)] + w.set_pllsrc(pll.source); + write_fields!(w); + }), + } + + // Enable PLL + match instance { + PllInstance::Pll => { + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + } + PllInstance::Pllsai1 => { + RCC.cr().modify(|w| w.set_pllsai1on(true)); + while !RCC.cr().read().pllsai1rdy() {} + } + #[cfg(any( + stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx + ))] + PllInstance::Pllsai2 => { + RCC.cr().modify(|w| w.set_pllsai2on(true)); + while !RCC.cr().read().pllsai2rdy() {} + } + } + + PllOutput { _p: p, _q: q, _r: r } +} diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs index 289217b1..7e095a6b 100644 --- a/embassy-stm32/src/rcc/l5.rs +++ b/embassy-stm32/src/rcc/l5.rs @@ -104,7 +104,7 @@ pub(crate) unsafe fn init(config: Config) { // Enable as clock source for USB, RNG if running at 48 MHz if range == MSIRange::RANGE48M { RCC.ccipr1().modify(|w| { - w.set_clk48msel(0b11); + w.set_clk48sel(0b11); }); } (msirange_to_hertz(range), Sw::MSI) @@ -173,7 +173,7 @@ pub(crate) unsafe fn init(config: Config) { let freq = src_freq / prediv * mul / divq; assert!(freq.0 == 48_000_000); RCC.ccipr1().modify(|w| { - w.set_clk48msel(0b10); + w.set_clk48sel(0b10); }); } @@ -191,7 +191,7 @@ pub(crate) unsafe fn init(config: Config) { let freq = src_freq / prediv * mul / q_div; if freq.0 == 48_000_000 { RCC.ccipr1().modify(|w| { - w.set_clk48msel(0b1); + w.set_clk48sel(0b1); }); } } @@ -218,7 +218,7 @@ pub(crate) unsafe fn init(config: Config) { while !RCC.crrcr().read().hsi48rdy() {} // Enable as clock source for USB, RNG and SDMMC - RCC.ccipr1().modify(|w| w.set_clk48msel(0)); + RCC.ccipr1().modify(|w| w.set_clk48sel(0)); } // Set flash wait states diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 9df40baa..76c9f34b 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -20,7 +20,7 @@ pub use mco::*; #[cfg_attr(rcc_g4, path = "g4.rs")] #[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab), path = "h.rs")] #[cfg_attr(any(rcc_l0, rcc_l0_v2, rcc_l1), path = "l0l1.rs")] -#[cfg_attr(rcc_l4, path = "l4.rs")] +#[cfg_attr(any(rcc_l4, rcc_l4plus), path = "l4.rs")] #[cfg_attr(rcc_l5, path = "l5.rs")] #[cfg_attr(rcc_u5, path = "u5.rs")] #[cfg_attr(rcc_wb, path = "wb.rs")] @@ -65,6 +65,7 @@ pub struct Clocks { pub hclk1: Hertz, #[cfg(any( rcc_l4, + rcc_l4plus, rcc_l5, rcc_f2, rcc_f4, @@ -85,6 +86,7 @@ pub struct Clocks { pub hclk2: Hertz, #[cfg(any( rcc_l4, + rcc_l4plus, rcc_l5, rcc_f2, rcc_f4, diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 1771e520..3d0c623f 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -13,7 +13,7 @@ fn main() -> ! { info!("Hello World!"); pac::RCC.ccipr().modify(|w| { - w.set_adcsel(0b11); + w.set_adcsel(pac::rcc::vals::Adcsel::SYSCLK); }); pac::RCC.ahb2enr().modify(|w| w.set_adcen(true)); diff --git a/examples/stm32l4/src/bin/rng.rs b/examples/stm32l4/src/bin/rng.rs index 94251c12..d184bcf7 100644 --- a/examples/stm32l4/src/bin/rng.rs +++ b/examples/stm32l4/src/bin/rng.rs @@ -18,8 +18,8 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.rcc.mux = ClockSrc::PLL; config.rcc.hsi16 = true; - config.rcc.pll_src = PLLSource::HSI16; config.rcc.pll = Some(Pll { + source: PLLSource::HSI16, prediv: PllPreDiv::DIV1, mul: PllMul::MUL18, divp: None, diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index cd9f72ff..a1b41f84 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -17,8 +17,8 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.rcc.mux = ClockSrc::PLL; config.rcc.hse = Some(Hertz::mhz(8)); - config.rcc.pll_src = PLLSource::HSE; config.rcc.pll = Some(Pll { + source: PLLSource::HSE, prediv: PllPreDiv::DIV1, mul: PllMul::MUL20, divp: None, diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index c1a27cf8..278d6543 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -79,8 +79,8 @@ async fn main(spawner: Spawner) { // 80MHz highest frequency for flash 0 wait. config.rcc.mux = ClockSrc::PLL; config.rcc.hse = Some(Hertz::mhz(8)); - config.rcc.pll_src = PLLSource::HSE; config.rcc.pll = Some(Pll { + source: PLLSource::HSE, prediv: PllPreDiv::DIV1, mul: PllMul::MUL20, divp: None, diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 8f6eeef3..3785c689 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -26,8 +26,8 @@ async fn main(_spawner: Spawner) { config.rcc.hsi48 = true; config.rcc.mux = ClockSrc::PLL; config.rcc.hsi16 = true; - config.rcc.pll_src = PLLSource::HSI16; config.rcc.pll = Some(Pll { + source: PLLSource::HSI16, prediv: PllPreDiv::DIV1, mul: PllMul::MUL10, divp: None, diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index e1d7855f..c5a24044 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -289,8 +289,8 @@ pub fn config() -> Config { use embassy_stm32::rcc::*; config.rcc.mux = ClockSrc::PLL; config.rcc.hsi16 = true; - config.rcc.pll_src = PLLSource::HSI16; config.rcc.pll = Some(Pll { + source: PLLSource::HSI16, prediv: PllPreDiv::DIV1, mul: PllMul::MUL18, divp: None,