stm32/rcc: unify f2 into f4/f7.
This commit is contained in:
parent
2376b3bdfa
commit
ace5221080
@ -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]
|
||||||
|
@ -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 as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
|
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
|
||||||
Pllsrc as PllSource, 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;
|
||||||
|
|
||||||
@ -56,6 +59,22 @@ pub struct Pll {
|
|||||||
pub divr: Option<PllRDiv>,
|
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
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -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);
|
||||||
|
}
|
@ -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,
|
|
||||||
});
|
|
||||||
}
|
|
@ -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")]
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user