stm32/rcc: port L4 to the "flattened" API like h5/h7.

This commit is contained in:
Dario Nieuwenhuis 2023-10-15 03:08:14 +02:00
parent 7045c53170
commit 8a10948ce9
6 changed files with 275 additions and 194 deletions

View File

@ -1,9 +1,8 @@
use crate::pac::rcc::regs::Cfgr;
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, Ppre as APBPrescaler,
Pllr as PllRDiv, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc,
};
use crate::pac::rcc::vals::{Msirange, Pllsrc, Sw};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
@ -11,42 +10,47 @@ use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
MSI(MSIRange),
PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option<PllQDiv>),
HSE(Hertz),
HSI16,
}
pub struct Pll {
/// PLL pre-divider (DIVM).
pub prediv: PllPreDiv,
/// PLL clock input source
#[derive(Clone, Copy)]
pub enum PLLSource {
HSI16,
HSE(Hertz),
MSI(MSIRange),
}
/// PLL multiplication factor.
pub mul: PllMul,
impl From<PLLSource> for Pllsrc {
fn from(val: PLLSource) -> Pllsrc {
match val {
PLLSource::HSI16 => Pllsrc::HSI16,
PLLSource::HSE(_) => Pllsrc::HSE,
PLLSource::MSI(_) => Pllsrc::MSI,
}
}
/// PLL P division factor. If None, PLL P output is disabled.
pub divp: Option<PllPDiv>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<PllQDiv>,
/// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<PllRDiv>,
}
/// Clocks configutation
pub struct Config {
// base clock sources
pub msi: Option<MSIRange>,
pub hsi16: bool,
pub hse: Option<Hertz>,
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
pub hsi48: bool,
// pll
pub pll_src: PLLSource,
pub pll: Option<Pll>,
pub pllsai1: Option<Pll>,
#[cfg(any(
stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx
))]
pub pllsai2: Option<Pll>,
// sysclk, buses.
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub pllsai1: Option<(PllMul, PllPreDiv, Option<PllRDiv>, Option<PllQDiv>, Option<PllPDiv>)>,
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
pub hsi48: bool,
// low speed LSI/LSE/RTC
pub ls: super::LsConfig,
}
@ -54,11 +58,20 @@ impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::MSI(MSIRange::RANGE4M),
hse: None,
hsi16: false,
msi: Some(MSIRange::RANGE4M),
mux: ClockSrc::MSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
pll_src: PLLSource::NONE,
pll: None,
pllsai1: None,
#[cfg(any(
stm32l47x, stm32l48x, stm32l49x, stm32l4ax, stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx
))]
pllsai2: None,
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
hsi48: false,
ls: Default::default(),
@ -80,154 +93,204 @@ pub(crate) unsafe fn init(config: Config) {
// Wait until MSI is running
while !RCC.cr().read().msirdy() {}
}
if RCC.cfgr().read().sws() != Sw::MSI {
if RCC.cfgr().read().sws() != ClockSrc::MSI {
// Set MSI as a clock source, reset prescalers.
RCC.cfgr().write_value(Cfgr::default());
// Wait for clock switch status bits to change.
while RCC.cfgr().read().sws() != Sw::MSI {}
while RCC.cfgr().read().sws() != ClockSrc::MSI {}
}
let rtc = config.ls.init();
let (sys_clk, sw) = match config.mux {
ClockSrc::MSI(range) => {
// Enable MSI
RCC.cr().write(|w| {
w.set_msirange(range);
w.set_msirgsel(true);
w.set_msion(true);
let msi = config.msi.map(|range| {
// Enable MSI
RCC.cr().write(|w| {
w.set_msirange(range);
w.set_msirgsel(true);
w.set_msion(true);
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(config.ls.lse.is_some());
});
while !RCC.cr().read().msirdy() {}
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(config.ls.lse.is_some());
});
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);
});
}
(msirange_to_hertz(range), Sw::MSI)
// Enable as clock source for USB, RNG if running at 48 MHz
if range == MSIRange::RANGE48M {
RCC.ccipr().modify(|w| w.set_clk48sel(0b11));
}
ClockSrc::HSI16 => {
// Enable HSI16
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
(HSI_FREQ, Sw::HSI16)
}
ClockSrc::HSE(freq) => {
// Enable HSE
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
msirange_to_hertz(range)
});
(freq, Sw::HSE)
}
ClockSrc::PLL(src, divr, prediv, mul, divq) => {
let src_freq = match src {
PLLSource::HSE(freq) => {
// Enable HSE
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
freq
}
PLLSource::HSI16 => {
// Enable HSI
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
}
PLLSource::MSI(range) => {
// Enable MSI
RCC.cr().write(|w| {
w.set_msirange(range);
w.set_msipllen(false); // should be turned on if LSE is started
w.set_msirgsel(true);
w.set_msion(true);
});
while !RCC.cr().read().msirdy() {}
let hsi16 = config.hsi16.then(|| {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
msirange_to_hertz(range)
}
};
HSI_FREQ
});
// Disable PLL
RCC.cr().modify(|w| w.set_pllon(false));
while RCC.cr().read().pllrdy() {}
let hse = config.hse.map(|freq| {
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
let freq = src_freq / prediv * mul / divr;
#[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))]
assert!(freq.0 <= 120_000_000);
#[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))]
assert!(freq.0 <= 80_000_000);
RCC.pllcfgr().write(move |w| {
w.set_plln(mul);
w.set_pllm(prediv);
w.set_pllr(divr);
if let Some(divq) = divq {
w.set_pllq(divq);
w.set_pllqen(true);
}
w.set_pllsrc(src.into());
});
// Enable as clock source for USB, RNG if PLL48 divisor is provided
if let Some(divq) = divq {
let freq = src_freq / prediv * mul / divq;
assert!(freq.0 == 48_000_000);
RCC.ccipr().modify(|w| {
w.set_clk48sel(0b10);
});
}
if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 {
RCC.pllsai1cfgr().write(move |w| {
w.set_plln(mul);
w.set_pllm(prediv);
if let Some(r_div) = r_div {
w.set_pllr(r_div);
w.set_pllren(true);
}
if let Some(q_div) = q_div {
w.set_pllq(q_div);
w.set_pllqen(true);
let freq = src_freq / prediv * mul / q_div;
if freq.0 == 48_000_000 {
RCC.ccipr().modify(|w| {
w.set_clk48sel(0b1);
});
}
}
if let Some(p_div) = p_div {
w.set_pllp(p_div);
w.set_pllpen(true);
}
});
RCC.cr().modify(|w| w.set_pllsai1on(true));
}
// Enable PLL
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
RCC.pllcfgr().modify(|w| w.set_pllren(true));
(freq, Sw::PLL)
}
};
freq
});
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
if config.hsi48 {
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)
});
let pll_src = match config.pll_src {
PLLSource::NONE => None,
PLLSource::HSE => hse,
PLLSource::HSI16 => hsi16,
PLLSource::MSI => msi,
};
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() {}
}
#[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 sys_clk = match config.mux {
ClockSrc::HSE => hse.unwrap(),
ClockSrc::HSI16 => hsi16.unwrap(),
ClockSrc::MSI => msi.unwrap(),
ClockSrc::PLL => _pllr.unwrap(),
};
#[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))]
assert!(sys_clk.0 <= 120_000_000);
#[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))]
assert!(sys_clk.0 <= 80_000_000);
// Set flash wait states
FLASH.acr().modify(|w| {
w.set_latency(match sys_clk.0 {
@ -240,7 +303,7 @@ pub(crate) unsafe fn init(config: Config) {
});
RCC.cfgr().modify(|w| {
w.set_sw(sw);
w.set_sw(config.mux);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
@ -277,7 +340,7 @@ pub(crate) unsafe fn init(config: Config) {
});
}
fn msirange_to_hertz(range: Msirange) -> Hertz {
fn msirange_to_hertz(range: MSIRange) -> Hertz {
match range {
MSIRange::RANGE100K => Hertz(100_000),
MSIRange::RANGE200K => Hertz(200_000),

View File

@ -4,7 +4,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::rcc::{ClockSrc, PLLSource, PllMul, PllPreDiv, PllQDiv, PllRDiv};
use embassy_stm32::rcc::{ClockSrc, PLLSource, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv};
use embassy_stm32::rng::Rng;
use embassy_stm32::{bind_interrupts, peripherals, rng, Config};
use {defmt_rtt as _, panic_probe as _};
@ -16,14 +16,16 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
// 72Mhz clock (16 / 1 * 18 / 4)
config.rcc.mux = ClockSrc::PLL(
PLLSource::HSI16,
PllRDiv::DIV4,
PllPreDiv::DIV1,
PllMul::MUL18,
Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)
);
config.rcc.mux = ClockSrc::PLL;
config.rcc.hsi16 = true;
config.rcc.pll_src = PLLSource::HSI16;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL18,
divp: None,
divq: Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)
divr: Some(PllRDiv::DIV4), // sysclk 72Mhz clock (16 / 1 * 18 / 4)
});
let p = embassy_stm32::init(config);
info!("Hello World!");

View File

@ -5,7 +5,7 @@
use chrono::{NaiveDate, NaiveDateTime};
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::rcc::{ClockSrc, LsConfig, PLLSource, PllMul, PllPreDiv, PllRDiv};
use embassy_stm32::rcc::{ClockSrc, LsConfig, PLLSource, Pll, PllMul, PllPreDiv, PllRDiv};
use embassy_stm32::rtc::{Rtc, RtcConfig};
use embassy_stm32::time::Hertz;
use embassy_stm32::Config;
@ -14,18 +14,20 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = {
let mut config = Config::default();
config.rcc.mux = ClockSrc::PLL(
PLLSource::HSE(Hertz::mhz(8)),
PllRDiv::DIV2,
PllPreDiv::DIV1,
PllMul::MUL20,
None,
);
config.rcc.ls = LsConfig::default_lse();
embassy_stm32::init(config)
};
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 {
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL20,
divp: None,
divq: None,
divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2)
});
config.rcc.ls = LsConfig::default_lse();
let p = embassy_stm32::init(config);
info!("Hello World!");
let now = NaiveDate::from_ymd_opt(2020, 5, 15)

View File

@ -48,7 +48,7 @@ use embassy_net_adin1110::{self, Device, Runner, ADIN1110};
use embedded_hal_bus::spi::ExclusiveDevice;
use hal::gpio::Pull;
use hal::i2c::Config as I2C_Config;
use hal::rcc::{ClockSrc, PLLSource, PllMul, PllPreDiv, PllRDiv};
use hal::rcc::{ClockSrc, PLLSource, Pll, PllMul, PllPreDiv, PllRDiv};
use hal::spi::{Config as SPI_Config, Spi};
use hal::time::Hertz;
@ -77,13 +77,16 @@ async fn main(spawner: Spawner) {
// 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2)
// 80MHz highest frequency for flash 0 wait.
config.rcc.mux = ClockSrc::PLL(
PLLSource::HSE(Hertz(8_000_000)),
PllRDiv::DIV2,
PllPreDiv::DIV1,
PllMul::MUL20,
None,
);
config.rcc.mux = ClockSrc::PLL;
config.rcc.hse = Some(Hertz::mhz(8));
config.rcc.pll_src = PLLSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL20,
divp: None,
divq: None,
divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2)
});
config.rcc.hsi48 = true; // needed for rng
let dp = embassy_stm32::init(config);

View File

@ -23,8 +23,17 @@ async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PllRDiv::DIV2, PllPreDiv::DIV1, PllMul::MUL10, None);
config.rcc.hsi48 = true;
config.rcc.mux = ClockSrc::PLL;
config.rcc.hsi16 = true;
config.rcc.pll_src = PLLSource::HSI16;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL10,
divp: None,
divq: None,
divr: Some(PllRDiv::DIV2), // sysclk 80Mhz (16 / 1 * 10 / 2)
});
let p = embassy_stm32::init(config);

View File

@ -284,17 +284,19 @@ pub fn config() -> Config {
config.rcc.adc_clock_source = AdcClockSource::PLL2_P;
}
#[cfg(any(feature = "stm32l4a6zg", feature = "stm32l4r5zi"))]
#[cfg(any(feature = "stm32l496zg", feature = "stm32l4a6zg", feature = "stm32l4r5zi"))]
{
use embassy_stm32::rcc::*;
config.rcc.mux = ClockSrc::PLL(
// 72Mhz clock (16 / 1 * 18 / 4)
PLLSource::HSI16,
PllRDiv::DIV4,
PllPreDiv::DIV1,
PllMul::MUL18,
Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)
);
config.rcc.mux = ClockSrc::PLL;
config.rcc.hsi16 = true;
config.rcc.pll_src = PLLSource::HSI16;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL18,
divp: None,
divq: Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)
divr: Some(PllRDiv::DIV4), // sysclk 72Mhz clock (16 / 1 * 18 / 4)
});
}
#[cfg(any(feature = "stm32l552ze"))]