Merge pull request #2178 from embassy-rs/rcc-no-spaghetti

stm32/rcc: unify f2 into f4/f7.
This commit is contained in:
Dario Nieuwenhuis 2023-11-13 01:08:27 +00:00 committed by GitHub
commit ac7134ed0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 182 additions and 412 deletions

View File

@ -58,7 +58,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0" sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1" critical-section = "1.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c551c07bf12513dd8346a9fe0bc70cf79f2ea02f" } stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
vcell = "0.1.3" vcell = "0.1.3"
bxcan = "0.7.0" bxcan = "0.7.0"
nb = "1.0.0" nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies] [build-dependencies]
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c551c07bf12513dd8346a9fe0bc70cf79f2ea02f", default-features = false, features = ["metadata"]} stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b", default-features = false, features = ["metadata"]}
[features] [features]

View File

@ -1,9 +1,12 @@
use crate::pac::pwr::vals::Vos; use stm32_metapac::flash::vals::Latency;
pub use crate::pac::rcc::vals::{ pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource, Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
Ppre as APBPrescaler, Sw as Sysclk, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
}; };
use crate::pac::{FLASH, PWR, RCC}; #[cfg(any(stm32f4, stm32f7))]
use crate::pac::PWR;
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
@ -49,11 +52,27 @@ pub struct Pll {
pub mul: PllMul, pub mul: PllMul,
/// PLL P division factor. If None, PLL P output is disabled. /// PLL P division factor. If None, PLL P output is disabled.
pub divp: Option<Pllp>, pub divp: Option<PllPDiv>,
/// PLL Q division factor. If None, PLL Q output is disabled. /// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<Pllq>, pub divq: Option<PllQDiv>,
/// PLL R division factor. If None, PLL R output is disabled. /// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<Pllr>, pub divr: Option<PllRDiv>,
}
/// Voltage range of the power supply used.
///
/// Used to calculate flash waitstates. See
/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency
#[cfg(stm32f2)]
pub enum VoltageScale {
/// 2.7 to 3.6 V
Range0,
/// 2.4 to 2.7 V
Range1,
/// 2.1 to 2.4 V
Range2,
/// 1.8 to 2.1 V
Range3,
} }
/// Configuration of the core clocks /// Configuration of the core clocks
@ -66,7 +85,7 @@ pub struct Config {
pub pll_src: PllSource, pub pll_src: PllSource,
pub pll: Option<Pll>, pub pll: Option<Pll>,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
pub plli2s: Option<Pll>, pub plli2s: Option<Pll>,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pub pllsai: Option<Pll>, pub pllsai: Option<Pll>,
@ -76,6 +95,9 @@ pub struct Config {
pub apb2_pre: APBPrescaler, pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig, pub ls: super::LsConfig,
#[cfg(stm32f2)]
pub voltage: VoltageScale,
} }
impl Default for Config { impl Default for Config {
@ -86,7 +108,7 @@ impl Default for Config {
sys: Sysclk::HSI, sys: Sysclk::HSI,
pll_src: PllSource::HSI, pll_src: PllSource::HSI,
pll: None, pll: None,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
plli2s: None, plli2s: None,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pllsai: None, pllsai: None,
@ -96,6 +118,9 @@ impl Default for Config {
apb2_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1,
ls: Default::default(), ls: Default::default(),
#[cfg(stm32f2)]
voltage: VoltageScale::Range3,
} }
} }
} }
@ -103,14 +128,13 @@ impl Default for Config {
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
// set VOS to SCALE1, if use PLL // set VOS to SCALE1, if use PLL
// TODO: check real clock speed before set VOS // TODO: check real clock speed before set VOS
#[cfg(any(stm32f4, stm32f7))]
if config.pll.is_some() { if config.pll.is_some() {
PWR.cr1().modify(|w| w.set_vos(Vos::SCALE1)); PWR.cr1().modify(|w| w.set_vos(crate::pac::pwr::vals::Vos::SCALE1));
} }
// always enable overdrive for now. Make it configurable in the future. // always enable overdrive for now. Make it configurable in the future.
#[cfg(not(any( #[cfg(any(stm32f446, stm32f4x9, stm32f427, stm32f437, stm32f7))]
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
)))]
{ {
PWR.cr1().modify(|w| w.set_oden(true)); PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {} while !PWR.csr1().read().odrdy() {}
@ -158,7 +182,7 @@ pub(crate) unsafe fn init(config: Config) {
source: config.pll_src, source: config.pll_src,
}; };
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input); let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input);
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input); let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input);
@ -182,7 +206,48 @@ pub(crate) unsafe fn init(config: Config) {
let rtc = config.ls.init(); let rtc = config.ls.init();
flash_setup(hclk); #[cfg(stm32f2)]
let latency = match (config.voltage, hclk.0) {
(VoltageScale::Range3, ..=16_000_000) => Latency::WS0,
(VoltageScale::Range3, ..=32_000_000) => Latency::WS1,
(VoltageScale::Range3, ..=48_000_000) => Latency::WS2,
(VoltageScale::Range3, ..=64_000_000) => Latency::WS3,
(VoltageScale::Range3, ..=80_000_000) => Latency::WS4,
(VoltageScale::Range3, ..=96_000_000) => Latency::WS5,
(VoltageScale::Range3, ..=112_000_000) => Latency::WS6,
(VoltageScale::Range3, ..=120_000_000) => Latency::WS7,
(VoltageScale::Range2, ..=18_000_000) => Latency::WS0,
(VoltageScale::Range2, ..=36_000_000) => Latency::WS1,
(VoltageScale::Range2, ..=54_000_000) => Latency::WS2,
(VoltageScale::Range2, ..=72_000_000) => Latency::WS3,
(VoltageScale::Range2, ..=90_000_000) => Latency::WS4,
(VoltageScale::Range2, ..=108_000_000) => Latency::WS5,
(VoltageScale::Range2, ..=120_000_000) => Latency::WS6,
(VoltageScale::Range1, ..=24_000_000) => Latency::WS0,
(VoltageScale::Range1, ..=48_000_000) => Latency::WS1,
(VoltageScale::Range1, ..=72_000_000) => Latency::WS2,
(VoltageScale::Range1, ..=96_000_000) => Latency::WS3,
(VoltageScale::Range1, ..=120_000_000) => Latency::WS4,
(VoltageScale::Range0, ..=30_000_000) => Latency::WS0,
(VoltageScale::Range0, ..=60_000_000) => Latency::WS1,
(VoltageScale::Range0, ..=90_000_000) => Latency::WS2,
(VoltageScale::Range0, ..=120_000_000) => Latency::WS3,
_ => unreachable!(),
};
#[cfg(any(stm32f4, stm32f7))]
let latency = {
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
let latency = (hclk.0 - 1) / FLASH_LATENCY_STEP;
debug!("flash: latency={}", latency);
Latency::from_bits(latency as u8)
};
FLASH.acr().write(|w| w.set_latency(latency));
while FLASH.acr().read().latency() != latency {}
RCC.cfgr().modify(|w| { RCC.cfgr().modify(|w| {
w.set_sw(config.sys); w.set_sw(config.sys);
@ -232,7 +297,7 @@ struct PllOutput {
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]
enum PllInstance { enum PllInstance {
Pll, Pll,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
Plli2s, Plli2s,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
Pllsai, Pllsai,
@ -244,7 +309,7 @@ fn pll_enable(instance: PllInstance, enabled: bool) {
RCC.cr().modify(|w| w.set_pllon(enabled)); RCC.cr().modify(|w| w.set_pllon(enabled));
while RCC.cr().read().pllrdy() != enabled {} while RCC.cr().read().pllrdy() != enabled {}
} }
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
PllInstance::Plli2s => { PllInstance::Plli2s => {
RCC.cr().modify(|w| w.set_plli2son(enabled)); RCC.cr().modify(|w| w.set_plli2son(enabled));
while RCC.cr().read().plli2srdy() != enabled {} while RCC.cr().read().plli2srdy() != enabled {}
@ -275,6 +340,18 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
let vco_freq = in_freq * pll.mul; let vco_freq = in_freq * pll.mul;
assert!(max::PLL_VCO.contains(&vco_freq)); assert!(max::PLL_VCO.contains(&vco_freq));
// stm32f2 plls are like swiss cheese
#[cfg(stm32f2)]
match instance {
PllInstance::Pll => {
assert!(pll.divr.is_none());
}
PllInstance::Plli2s => {
assert!(pll.divp.is_none());
assert!(pll.divq.is_none());
}
}
let p = pll.divp.map(|div| vco_freq / div); let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div); let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div); let r = pll.divr.map(|div| vco_freq / div);
@ -288,6 +365,7 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
if let Some(divq) = pll.divq { if let Some(divq) = pll.divq {
$w.set_pllq(divq); $w.set_pllq(divq);
} }
#[cfg(any(stm32f4, stm32f7))]
if let Some(divr) = pll.divr { if let Some(divr) = pll.divr {
$w.set_pllr(divr); $w.set_pllr(divr);
} }
@ -304,6 +382,12 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
PllInstance::Plli2s => RCC.plli2scfgr().write(|w| { PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {
write_fields!(w); write_fields!(w);
}), }),
#[cfg(stm32f2)]
PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {
if let Some(divr) = pll.divr {
w.set_pllr(divr);
}
}),
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))] #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| { PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| {
write_fields!(w); write_fields!(w);
@ -316,22 +400,6 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
PllOutput { p, q, r } PllOutput { p, q, r }
} }
fn flash_setup(clk: Hertz) {
use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
let latency = (clk.0 - 1) / FLASH_LATENCY_STEP;
debug!("flash: latency={}", latency);
let latency = Latency::from_bits(latency as u8);
FLASH.acr().write(|w| {
w.set_latency(latency);
});
while FLASH.acr().read().latency() != latency {}
}
#[cfg(stm32f7)] #[cfg(stm32f7)]
mod max { mod max {
use core::ops::RangeInclusive; use core::ops::RangeInclusive;
@ -380,3 +448,22 @@ mod max {
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000); pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000); pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);
} }
#[cfg(stm32f2)]
mod max {
use core::ops::RangeInclusive;
use crate::time::Hertz;
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(26_000_000);
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(120_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0);
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 4);
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 2);
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(0_950_000)..=Hertz(2_100_000);
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(432_000_000);
}

View File

@ -1,320 +0,0 @@
use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::Sw;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllsrc as PllSource,
Ppre as APBPrescaler,
};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy)]
pub struct HSEConfig {
pub frequency: Hertz,
pub source: HSESrc,
}
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE,
HSI,
PLL,
}
/// HSE clock source
#[derive(Clone, Copy)]
pub enum HSESrc {
/// Crystal/ceramic resonator
Crystal,
/// External clock source, HSE bypassed
Bypass,
}
#[derive(Clone, Copy)]
pub struct Pll {
pub pre_div: PllPreDiv,
pub mul: PllMul,
pub divp: PllPDiv,
pub divq: PllQDiv,
}
impl Default for Pll {
fn default() -> Self {
Pll {
pre_div: PllPreDiv::DIV16,
mul: PllMul::MUL192,
divp: PllPDiv::DIV2,
divq: PllQDiv::DIV4,
}
}
}
impl Pll {
pub fn clocks(&self, src_freq: Hertz) -> PLLClocks {
let in_freq = src_freq / self.pre_div;
let vco_freq = src_freq / self.pre_div * self.mul;
let main_freq = vco_freq / self.divp;
let pll48_freq = vco_freq / self.divq;
PLLClocks {
in_freq,
vco_freq,
main_freq,
pll48_freq,
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub struct PLLClocks {
pub in_freq: Hertz,
pub vco_freq: Hertz,
pub main_freq: Hertz,
pub pll48_freq: Hertz,
}
/// Voltage range of the power supply used.
///
/// Used to calculate flash waitstates. See
/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency
pub enum VoltageScale {
/// 2.7 to 3.6 V
Range0,
/// 2.4 to 2.7 V
Range1,
/// 2.1 to 2.4 V
Range2,
/// 1.8 to 2.1 V
Range3,
}
impl VoltageScale {
const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> {
let ahb_freq = ahb_freq.0;
// Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock
// frequency
match self {
VoltageScale::Range3 => {
if ahb_freq <= 16_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 32_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 48_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 64_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 80_000_000 {
Some(Latency::WS4)
} else if ahb_freq <= 96_000_000 {
Some(Latency::WS5)
} else if ahb_freq <= 112_000_000 {
Some(Latency::WS6)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS7)
} else {
None
}
}
VoltageScale::Range2 => {
if ahb_freq <= 18_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 36_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 54_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 72_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 90_000_000 {
Some(Latency::WS4)
} else if ahb_freq <= 108_000_000 {
Some(Latency::WS5)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS6)
} else {
None
}
}
VoltageScale::Range1 => {
if ahb_freq <= 24_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 48_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 72_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 96_000_000 {
Some(Latency::WS3)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS4)
} else {
None
}
}
VoltageScale::Range0 => {
if ahb_freq <= 30_000_000 {
Some(Latency::WS0)
} else if ahb_freq <= 60_000_000 {
Some(Latency::WS1)
} else if ahb_freq <= 90_000_000 {
Some(Latency::WS2)
} else if ahb_freq <= 120_000_000 {
Some(Latency::WS3)
} else {
None
}
}
}
}
}
/// Clocks configuration
pub struct Config {
pub hse: Option<HSEConfig>,
pub hsi: bool,
pub pll_mux: PllSource,
pub pll: Pll,
pub mux: ClockSrc,
pub voltage: VoltageScale,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
hse: None,
hsi: true,
pll_mux: PllSource::HSI,
pll: Pll::default(),
voltage: VoltageScale::Range3,
mux: ClockSrc::HSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// Make sure HSI is enabled
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
if let Some(hse_config) = config.hse {
RCC.cr().modify(|w| {
w.set_hsebyp(match hse_config.source {
HSESrc::Bypass => true,
HSESrc::Crystal => false,
});
w.set_hseon(true)
});
while !RCC.cr().read().hserdy() {}
}
let pll_src_freq = match config.pll_mux {
PllSource::HSE => {
let hse_config = config
.hse
.unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
hse_config.frequency
}
PllSource::HSI => HSI_FREQ,
};
// Reference: STM32F215xx/217xx datasheet Table 33. Main PLL characteristics
let pll_clocks = config.pll.clocks(pll_src_freq);
assert!(Hertz(950_000) <= pll_clocks.in_freq && pll_clocks.in_freq <= Hertz(2_100_000));
assert!(Hertz(192_000_000) <= pll_clocks.vco_freq && pll_clocks.vco_freq <= Hertz(432_000_000));
assert!(Hertz(24_000_000) <= pll_clocks.main_freq && pll_clocks.main_freq <= Hertz(120_000_000));
// USB actually requires == 48 MHz, but other PLL48 peripherals are fine with <= 48MHz
assert!(pll_clocks.pll48_freq <= Hertz(48_000_000));
RCC.pllcfgr().write(|w| {
w.set_pllsrc(config.pll_mux);
w.set_pllm(config.pll.pre_div);
w.set_plln(config.pll.mul);
w.set_pllp(config.pll.divp);
w.set_pllq(config.pll.divq);
});
let (sys_clk, sw) = match config.mux {
ClockSrc::HSI => {
assert!(config.hsi, "HSI must be enabled to be used as system clock");
(HSI_FREQ, Sw::HSI)
}
ClockSrc::HSE => {
let hse_config = config
.hse
.unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
(hse_config.frequency, Sw::HSE)
}
ClockSrc::PLL => {
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
(pll_clocks.main_freq, Sw::PLL1_P)
}
};
// RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
// max output to be 120 MHz, so there's no way to get higher frequencies
assert!(sys_clk <= Hertz(120_000_000));
let ahb_freq = sys_clk / config.ahb_pre;
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(ahb_freq <= Hertz(120_000_000));
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, Hertz(freq.0 * 2))
}
};
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(apb1_freq <= Hertz(30_000_000));
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, Hertz(freq.0 * 2))
}
};
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(apb2_freq <= Hertz(60_000_000));
let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq));
FLASH.acr().modify(|w| w.set_latency(flash_ws));
RCC.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
// Turn off HSI to save power if we don't need it
if !config.hsi {
RCC.cr().modify(|w| w.set_hsion(false));
}
let rtc = config.ls.init();
set_freqs(Clocks {
sys: sys_clk,
hclk1: ahb_freq,
hclk2: ahb_freq,
hclk3: ahb_freq,
pclk1: apb1_freq,
pclk1_tim: apb1_tim_freq,
pclk2: apb2_freq,
pclk2_tim: apb2_tim_freq,
pll1_q: Some(pll_clocks.pll48_freq),
rtc,
});
}

View File

@ -15,14 +15,13 @@ mod hsi48;
pub use hsi48::*; pub use hsi48::*;
#[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(rcc_f0, path = "f0.rs")]
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(any(stm32f1), path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")] #[cfg_attr(any(stm32f3), path = "f3.rs")]
#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")] #[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")]
#[cfg_attr(rcc_c0, path = "c0.rs")] #[cfg_attr(rcc_c0, path = "c0.rs")]
#[cfg_attr(rcc_g0, path = "g0.rs")] #[cfg_attr(rcc_g0, path = "g0.rs")]
#[cfg_attr(rcc_g4, path = "g4.rs")] #[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(stm32h5, stm32h7), path = "h.rs")]
#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")] #[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")]
#[cfg_attr(rcc_u5, path = "u5.rs")] #[cfg_attr(rcc_u5, path = "u5.rs")]
#[cfg_attr(rcc_wba, path = "wba.rs")] #[cfg_attr(rcc_wba, path = "wba.rs")]

View File

@ -6,9 +6,6 @@ use core::convert::TryFrom;
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::rcc::{
APBPrescaler, ClockSrc, HSEConfig, HSESrc, Pll, PllMul, PllPDiv, PllPreDiv, PllQDiv, PllSource,
};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use embassy_stm32::Config; use embassy_stm32::Config;
use embassy_time::Timer; use embassy_time::Timer;
@ -19,29 +16,35 @@ async fn main(_spawner: Spawner) {
// Example config for maximum performance on a NUCLEO-F207ZG board // Example config for maximum performance on a NUCLEO-F207ZG board
let mut config = Config::default(); let mut config = Config::default();
{
use embassy_stm32::rcc::*;
// By default, HSE on the board comes from a 8 MHz clock signal (not a crystal) // By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
config.rcc.hse = Some(HSEConfig { config.rcc.hse = Some(Hse {
frequency: Hertz(8_000_000), freq: Hertz(8_000_000),
source: HSESrc::Bypass, mode: HseMode::Bypass,
}); });
// PLL uses HSE as the clock source // PLL uses HSE as the clock source
config.rcc.pll_mux = PllSource::HSE; config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Pll { config.rcc.pll = Some(Pll {
// 8 MHz clock source / 8 = 1 MHz PLL input // 8 MHz clock source / 8 = 1 MHz PLL input
pre_div: unwrap!(PllPreDiv::try_from(8)), prediv: unwrap!(PllPreDiv::try_from(8)),
// 1 MHz PLL input * 240 = 240 MHz PLL VCO // 1 MHz PLL input * 240 = 240 MHz PLL VCO
mul: unwrap!(PllMul::try_from(240)), mul: unwrap!(PllMul::try_from(240)),
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output // 240 MHz PLL VCO / 2 = 120 MHz main PLL output
divp: PllPDiv::DIV2, divp: Some(PllPDiv::DIV2),
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output // 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
divq: PllQDiv::DIV5, divq: Some(PllQDiv::DIV5),
}; divr: None,
});
// System clock comes from PLL (= the 120 MHz main PLL output) // System clock comes from PLL (= the 120 MHz main PLL output)
config.rcc.mux = ClockSrc::PLL; config.rcc.sys = Sysclk::PLL1_P;
// 120 MHz / 4 = 30 MHz APB1 frequency // 120 MHz / 4 = 30 MHz APB1 frequency
config.rcc.apb1_pre = APBPrescaler::DIV4; config.rcc.apb1_pre = APBPrescaler::DIV4;
// 120 MHz / 2 = 60 MHz APB2 frequency // 120 MHz / 2 = 60 MHz APB2 frequency
config.rcc.apb2_pre = APBPrescaler::DIV2; config.rcc.apb2_pre = APBPrescaler::DIV2;
}
let _p = embassy_stm32::init(config); let _p = embassy_stm32::init(config);

View File

@ -42,7 +42,7 @@ async fn main(spawner: Spawner) -> ! {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL180, mul: PllMul::MUL180,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divq: None, divq: None,
divr: None, divr: None,
}); });

View File

@ -30,8 +30,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168, mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None, divr: None,
}); });
config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -56,8 +56,8 @@ async fn main(spawner: Spawner) {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168, mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None, divr: None,
}); });
config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -85,8 +85,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168, mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None, divr: None,
}); });
config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -32,8 +32,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168, mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz. divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz. divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None, divr: None,
}); });
config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -43,7 +43,7 @@ async fn main(spawner: Spawner) -> ! {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216, mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: None, divq: None,
divr: None, divr: None,
}); });

View File

@ -26,8 +26,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216, mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz divq: Some(PllQDiv::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divr: None, divr: None,
}); });
config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -32,8 +32,8 @@ async fn main(_spawner: Spawner) {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216, mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz
divq: Some(Pllq::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz divq: Some(PllQDiv::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz
divr: None, divr: None,
}); });
config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.ahb_pre = AHBPrescaler::DIV1;

View File

@ -236,24 +236,25 @@ pub fn config() -> Config {
{ {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
// By default, HSE on the board comes from a 8 MHz clock signal (not a crystal) // By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
config.rcc.hse = Some(HSEConfig { config.rcc.hse = Some(Hse {
frequency: Hertz(8_000_000), freq: Hertz(8_000_000),
source: HSESrc::Bypass, mode: HseMode::Bypass,
}); });
// PLL uses HSE as the clock source // PLL uses HSE as the clock source
config.rcc.pll_mux = PllSource::HSE; config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Pll { config.rcc.pll = Some(Pll {
// 8 MHz clock source / 8 = 1 MHz PLL input // 8 MHz clock source / 8 = 1 MHz PLL input
pre_div: unwrap!(PllPreDiv::try_from(8)), prediv: unwrap!(PllPreDiv::try_from(8)),
// 1 MHz PLL input * 240 = 240 MHz PLL VCO // 1 MHz PLL input * 240 = 240 MHz PLL VCO
mul: unwrap!(PllMul::try_from(240)), mul: unwrap!(PllMul::try_from(240)),
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output // 240 MHz PLL VCO / 2 = 120 MHz main PLL output
divp: PllPDiv::DIV2, divp: Some(PllPDiv::DIV2),
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output // 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
divq: PllQDiv::DIV5, divq: Some(PllQDiv::DIV5),
}; divr: None,
});
// System clock comes from PLL (= the 120 MHz main PLL output) // System clock comes from PLL (= the 120 MHz main PLL output)
config.rcc.mux = ClockSrc::PLL; config.rcc.sys = Sysclk::PLL1_P;
// 120 MHz / 4 = 30 MHz APB1 frequency // 120 MHz / 4 = 30 MHz APB1 frequency
config.rcc.apb1_pre = APBPrescaler::DIV4; config.rcc.apb1_pre = APBPrescaler::DIV4;
// 120 MHz / 2 = 60 MHz APB2 frequency // 120 MHz / 2 = 60 MHz APB2 frequency
@ -271,7 +272,7 @@ pub fn config() -> Config {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL180, mul: PllMul::MUL180,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.
divq: None, divq: None,
divr: None, divr: None,
}); });
@ -292,7 +293,7 @@ pub fn config() -> Config {
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL216, mul: PllMul::MUL216,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz. divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz.
divq: None, divq: None,
divr: None, divr: None,
}); });