mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 08:12:30 +00:00
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"
|
||||
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-c551c07bf12513dd8346a9fe0bc70cf79f2ea02f" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-fbb8f77326dd066aa6c0d66b3b46e76a569dda8b" }
|
||||
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-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]
|
||||
|
@ -1,9 +1,12 @@
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use stm32_metapac::flash::vals::Latency;
|
||||
|
||||
pub use crate::pac::rcc::vals::{
|
||||
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,
|
||||
};
|
||||
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::time::Hertz;
|
||||
|
||||
@ -56,6 +59,22 @@ pub struct Pll {
|
||||
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
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
@ -66,7 +85,7 @@ pub struct Config {
|
||||
pub pll_src: PllSource,
|
||||
|
||||
pub pll: Option<Pll>,
|
||||
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
pub plli2s: Option<Pll>,
|
||||
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||
pub pllsai: Option<Pll>,
|
||||
@ -76,6 +95,9 @@ pub struct Config {
|
||||
pub apb2_pre: APBPrescaler,
|
||||
|
||||
pub ls: super::LsConfig,
|
||||
|
||||
#[cfg(stm32f2)]
|
||||
pub voltage: VoltageScale,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -86,7 +108,7 @@ impl Default for Config {
|
||||
sys: Sysclk::HSI,
|
||||
pll_src: PllSource::HSI,
|
||||
pll: None,
|
||||
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
plli2s: None,
|
||||
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||
pllsai: None,
|
||||
@ -96,6 +118,9 @@ impl Default for Config {
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
|
||||
ls: Default::default(),
|
||||
|
||||
#[cfg(stm32f2)]
|
||||
voltage: VoltageScale::Range3,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,14 +128,13 @@ impl Default for Config {
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// set VOS to SCALE1, if use PLL
|
||||
// TODO: check real clock speed before set VOS
|
||||
#[cfg(any(stm32f4, stm32f7))]
|
||||
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.
|
||||
#[cfg(not(any(
|
||||
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
|
||||
)))]
|
||||
#[cfg(any(stm32f446, stm32f4x9, stm32f427, stm32f437, stm32f7))]
|
||||
{
|
||||
PWR.cr1().modify(|w| w.set_oden(true));
|
||||
while !PWR.csr1().read().odrdy() {}
|
||||
@ -158,7 +182,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
source: config.pll_src,
|
||||
};
|
||||
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);
|
||||
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||
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();
|
||||
|
||||
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| {
|
||||
w.set_sw(config.sys);
|
||||
@ -232,7 +297,7 @@ struct PllOutput {
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
enum PllInstance {
|
||||
Pll,
|
||||
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
Plli2s,
|
||||
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
|
||||
Pllsai,
|
||||
@ -244,7 +309,7 @@ fn pll_enable(instance: PllInstance, enabled: bool) {
|
||||
RCC.cr().modify(|w| w.set_pllon(enabled));
|
||||
while RCC.cr().read().pllrdy() != enabled {}
|
||||
}
|
||||
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
#[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]
|
||||
PllInstance::Plli2s => {
|
||||
RCC.cr().modify(|w| w.set_plli2son(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;
|
||||
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 q = pll.divq.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 {
|
||||
$w.set_pllq(divq);
|
||||
}
|
||||
#[cfg(any(stm32f4, stm32f7))]
|
||||
if let Some(divr) = pll.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| {
|
||||
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))]
|
||||
PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| {
|
||||
write_fields!(w);
|
||||
@ -316,22 +400,6 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
|
||||
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)]
|
||||
mod max {
|
||||
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_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::*;
|
||||
|
||||
#[cfg_attr(rcc_f0, path = "f0.rs")]
|
||||
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
|
||||
#[cfg_attr(rcc_f2, path = "f2.rs")]
|
||||
#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")]
|
||||
#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")]
|
||||
#[cfg_attr(any(stm32f1), path = "f1.rs")]
|
||||
#[cfg_attr(any(stm32f3), path = "f3.rs")]
|
||||
#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f.rs")]
|
||||
#[cfg_attr(rcc_c0, path = "c0.rs")]
|
||||
#[cfg_attr(rcc_g0, path = "g0.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(rcc_u5, path = "u5.rs")]
|
||||
#[cfg_attr(rcc_wba, path = "wba.rs")]
|
||||
|
@ -6,9 +6,6 @@ use core::convert::TryFrom;
|
||||
|
||||
use defmt::*;
|
||||
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::Config;
|
||||
use embassy_time::Timer;
|
||||
@ -19,29 +16,35 @@ async fn main(_spawner: Spawner) {
|
||||
// Example config for maximum performance on a NUCLEO-F207ZG board
|
||||
|
||||
let mut config = Config::default();
|
||||
// By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
|
||||
config.rcc.hse = Some(HSEConfig {
|
||||
frequency: Hertz(8_000_000),
|
||||
source: HSESrc::Bypass,
|
||||
});
|
||||
// PLL uses HSE as the clock source
|
||||
config.rcc.pll_mux = PllSource::HSE;
|
||||
config.rcc.pll = Pll {
|
||||
// 8 MHz clock source / 8 = 1 MHz PLL input
|
||||
pre_div: unwrap!(PllPreDiv::try_from(8)),
|
||||
// 1 MHz PLL input * 240 = 240 MHz PLL VCO
|
||||
mul: unwrap!(PllMul::try_from(240)),
|
||||
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output
|
||||
divp: PllPDiv::DIV2,
|
||||
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
|
||||
divq: PllQDiv::DIV5,
|
||||
};
|
||||
// System clock comes from PLL (= the 120 MHz main PLL output)
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
// 120 MHz / 4 = 30 MHz APB1 frequency
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV4;
|
||||
// 120 MHz / 2 = 60 MHz APB2 frequency
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV2;
|
||||
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
|
||||
// By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(8_000_000),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
// PLL uses HSE as the clock source
|
||||
config.rcc.pll_src = PllSource::HSE;
|
||||
config.rcc.pll = Some(Pll {
|
||||
// 8 MHz clock source / 8 = 1 MHz PLL input
|
||||
prediv: unwrap!(PllPreDiv::try_from(8)),
|
||||
// 1 MHz PLL input * 240 = 240 MHz PLL VCO
|
||||
mul: unwrap!(PllMul::try_from(240)),
|
||||
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output
|
||||
divp: Some(PllPDiv::DIV2),
|
||||
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
|
||||
divq: Some(PllQDiv::DIV5),
|
||||
divr: None,
|
||||
});
|
||||
// System clock comes from PLL (= the 120 MHz main PLL output)
|
||||
config.rcc.sys = Sysclk::PLL1_P;
|
||||
// 120 MHz / 4 = 30 MHz APB1 frequency
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV4;
|
||||
// 120 MHz / 2 = 60 MHz APB2 frequency
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV2;
|
||||
}
|
||||
|
||||
let _p = embassy_stm32::init(config);
|
||||
|
||||
|
@ -236,24 +236,25 @@ pub fn config() -> Config {
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
// By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
|
||||
config.rcc.hse = Some(HSEConfig {
|
||||
frequency: Hertz(8_000_000),
|
||||
source: HSESrc::Bypass,
|
||||
config.rcc.hse = Some(Hse {
|
||||
freq: Hertz(8_000_000),
|
||||
mode: HseMode::Bypass,
|
||||
});
|
||||
// PLL uses HSE as the clock source
|
||||
config.rcc.pll_mux = PllSource::HSE;
|
||||
config.rcc.pll = Pll {
|
||||
config.rcc.pll_src = PllSource::HSE;
|
||||
config.rcc.pll = Some(Pll {
|
||||
// 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
|
||||
mul: unwrap!(PllMul::try_from(240)),
|
||||
// 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
|
||||
divq: PllQDiv::DIV5,
|
||||
};
|
||||
divq: Some(PllQDiv::DIV5),
|
||||
divr: None,
|
||||
});
|
||||
// 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
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV4;
|
||||
// 120 MHz / 2 = 60 MHz APB2 frequency
|
||||
|
Loading…
Reference in New Issue
Block a user