stm32/rcc: add better support for L4/L4+ differences.

This commit is contained in:
Dario Nieuwenhuis 2023-10-16 03:09:33 +02:00
parent f54753beaa
commit 5c5e681819
10 changed files with 206 additions and 155 deletions

View file

@ -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]

View file

@ -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<MSIRange>,
pub hsi16: bool,
pub hse: Option<Hertz>,
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
#[cfg(not(any(stm32l47x, stm32l48x)))]
pub hsi48: bool,
// pll
pub pll_src: PLLSource,
pub pll: Option<Pll>,
pub pllsai1: Option<Pll>,
#[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<T: Eq>(mut iter: impl Iterator<Item = T>) -> Result<Option<T>, ()> {
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<Hertz>,
hse: Option<Hertz>,
msi: Option<Hertz>,
}
#[derive(Default)]
struct PllOutput {
_p: Option<Hertz>,
_q: Option<Hertz>,
_r: Option<Hertz>,
}
#[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<Pll>, 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 }
}

View file

@ -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

View file

@ -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,

View file

@ -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));

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,