stm32/rcc: unify h5 and h7.

This commit is contained in:
Dario Nieuwenhuis 2023-09-19 04:22:57 +02:00
parent e313ca4ae8
commit 83b4c01273
26 changed files with 1195 additions and 1529 deletions

View File

@ -59,7 +59,7 @@ 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"
atomic-polyfill = "1.0.1" atomic-polyfill = "1.0.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2dba1f1ddee697e616aff2a4db57a6ffaf1b29b7" } stm32-metapac = { git = "https://ci.embassy.dev/jobs/7d2e0e63527f/artifacts/generated.git" }
vcell = "0.1.3" vcell = "0.1.3"
bxcan = "0.7.0" bxcan = "0.7.0"
nb = "1.0.0" nb = "1.0.0"
@ -78,7 +78,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-2dba1f1ddee697e616aff2a4db57a6ffaf1b29b7", default-features = false, features = ["metadata"]} stm32-metapac = { git = "https://ci.embassy.dev/jobs/7d2e0e63527f/artifacts/generated.git", default-features = false, features = ["metadata"]}
[features] [features]
default = ["rt"] default = ["rt"]

777
embassy-stm32/src/rcc/h.rs Normal file
View File

@ -0,0 +1,777 @@
use core::ops::RangeInclusive;
use crate::pac;
use crate::pac::pwr::vals::Vos;
#[cfg(stm32h5)]
pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource;
#[cfg(stm32h7)]
pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
pub use crate::pac::rcc::vals::Ckpersel as PerClockSource;
use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
/// CSI speed
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
/// HSI48 speed
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
/// LSI speed
pub const LSI_FREQ: Hertz = Hertz(32_000);
const VCO_RANGE: RangeInclusive<u32> = 150_000_000..=420_000_000;
#[cfg(any(stm32h5, pwr_h7rm0455))]
const VCO_WIDE_RANGE: RangeInclusive<u32> = 128_000_000..=560_000_000;
#[cfg(pwr_h7rm0468)]
const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=836_000_000;
#[cfg(any(pwr_h7rm0399, pwr_h7rm0433))]
const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=960_000_000;
pub use super::bus::{AHBPrescaler, APBPrescaler};
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum VoltageScale {
Scale0,
Scale1,
Scale2,
Scale3,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0)
Oscillator,
/// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
Bypass,
/// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
#[cfg(any(rcc_h5, rcc_h50))]
BypassDigital,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
/// HSE frequency.
pub freq: Hertz,
/// HSE mode.
pub mode: HseMode,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Hsi {
/// 64Mhz
Mhz64,
/// 32Mhz (divided by 2)
Mhz32,
/// 16Mhz (divided by 4)
Mhz16,
/// 8Mhz (divided by 8)
Mhz8,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Sysclk {
/// HSI selected as sysclk
HSI,
/// HSE selected as sysclk
HSE,
/// CSI selected as sysclk
CSI,
/// PLL1_P selected as sysclk
Pll1P,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSource {
Hsi,
Csi,
Hse,
}
#[derive(Clone, Copy)]
pub struct Pll {
/// Source clock selection.
#[cfg(stm32h5)]
pub source: PllSource,
/// PLL pre-divider (DIVM). Must be between 1 and 63.
pub prediv: u8,
/// PLL multiplication factor. Must be between 4 and 512.
pub mul: u16,
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
/// On PLL1, it must be even (in particular, it cannot be 1.)
pub divp: Option<u16>,
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
pub divq: Option<u16>,
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
pub divr: Option<u16>,
}
fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {
match (tim, apb) {
(TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32,
_ => unreachable!(),
}
}
/// Timer prescaler
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum TimerPrescaler {
/// The timers kernel clock is equal to hclk if PPREx corresponds to a
/// division by 1 or 2, else it is equal to 2*pclk
DefaultX2,
/// The timers kernel clock is equal to hclk if PPREx corresponds to a
/// division by 1, 2 or 4, else it is equal to 4*pclk
DefaultX4,
}
impl From<TimerPrescaler> for Timpre {
fn from(value: TimerPrescaler) -> Self {
match value {
TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2,
TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4,
}
}
}
/// Configuration of the core clocks
#[non_exhaustive]
pub struct Config {
pub hsi: Option<Hsi>,
pub hse: Option<Hse>,
pub csi: bool,
pub hsi48: bool,
pub sys: Sysclk,
#[cfg(stm32h7)]
pub pll_src: PllSource,
pub pll1: Option<Pll>,
pub pll2: Option<Pll>,
#[cfg(any(rcc_h5, stm32h7))]
pub pll3: Option<Pll>,
pub d1c_pre: AHBPrescaler,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub apb3_pre: APBPrescaler,
#[cfg(stm32h7)]
pub apb4_pre: APBPrescaler,
pub per_clock_source: PerClockSource,
pub adc_clock_source: AdcClockSource,
pub timer_prescaler: TimerPrescaler,
pub voltage_scale: VoltageScale,
}
impl Default for Config {
fn default() -> Self {
Self {
hsi: Some(Hsi::Mhz64),
hse: None,
csi: false,
hsi48: false,
sys: Sysclk::HSI,
#[cfg(stm32h7)]
pll_src: PllSource::Hsi,
pll1: None,
pll2: None,
#[cfg(any(rcc_h5, stm32h7))]
pll3: None,
d1c_pre: AHBPrescaler::DIV1,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1,
#[cfg(stm32h7)]
apb4_pre: APBPrescaler::DIV1,
per_clock_source: PerClockSource::HSI,
adc_clock_source: AdcClockSource::from_bits(0), // PLL2_P on H7, HCLK on H5
timer_prescaler: TimerPrescaler::DefaultX2,
voltage_scale: VoltageScale::Scale0,
}
}
}
pub(crate) unsafe fn init(config: Config) {
#[cfg(stm32h7)]
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
#[cfg(stm32h5)]
RCC.apb3enr().modify(|w| w.set_sbsen(true));
// NB. The lower bytes of CR3 can only be written once after
// POR, and must be written with a valid combination. Refer to
// RM0433 Rev 7 6.8.4. This is partially enforced by dropping
// `self` at the end of this method, but of course we cannot
// know what happened between the previous POR and here.
#[cfg(pwr_h7rm0433)]
PWR.cr3().modify(|w| {
w.set_scuen(true);
w.set_ldoen(true);
w.set_bypass(false);
});
#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
PWR.cr3().modify(|w| {
// hardcode "Direct SPMS" for now, this is what works on nucleos with the
// default solderbridge configuration.
w.set_sden(true);
w.set_ldoen(false);
});
// Validate the supply configuration. If you are stuck here, it is
// because the voltages on your board do not match those specified
// in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset
// VOS = Scale 3, so check that the voltage on the VCAP pins =
// 1.0V.
#[cfg(any(pwr_h7rm0433, pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
while !PWR.csr1().read().actvosrdy() {}
// Configure voltage scale.
#[cfg(any(pwr_h5, pwr_h50))]
{
PWR.voscr().modify(|w| {
w.set_vos(match config.voltage_scale {
VoltageScale::Scale0 => Vos::SCALE0,
VoltageScale::Scale1 => Vos::SCALE1,
VoltageScale::Scale2 => Vos::SCALE2,
VoltageScale::Scale3 => Vos::SCALE3,
})
});
while !PWR.vossr().read().vosrdy() {}
}
#[cfg(syscfg_h7)]
{
// in chips without the overdrive bit, we can go from any scale to any scale directly.
PWR.d3cr().modify(|w| {
w.set_vos(match config.voltage_scale {
VoltageScale::Scale0 => Vos::SCALE0,
VoltageScale::Scale1 => Vos::SCALE1,
VoltageScale::Scale2 => Vos::SCALE2,
VoltageScale::Scale3 => Vos::SCALE3,
})
});
while !PWR.d3cr().read().vosrdy() {}
}
#[cfg(syscfg_h7od)]
{
match config.voltage_scale {
VoltageScale::Scale0 => {
// to go to scale0, we must go to Scale1 first...
PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1));
while !PWR.d3cr().read().vosrdy() {}
// Then enable overdrive.
critical_section::with(|_| pac::SYSCFG.pwrcr().modify(|w| w.set_oden(1)));
while !PWR.d3cr().read().vosrdy() {}
}
_ => {
// for all other scales, we can go directly.
PWR.d3cr().modify(|w| {
w.set_vos(match config.voltage_scale {
VoltageScale::Scale0 => unreachable!(),
VoltageScale::Scale1 => Vos::SCALE1,
VoltageScale::Scale2 => Vos::SCALE2,
VoltageScale::Scale3 => Vos::SCALE3,
})
});
while !PWR.d3cr().read().vosrdy() {}
}
}
}
// Configure HSI
let hsi = match config.hsi {
None => {
RCC.cr().modify(|w| w.set_hsion(false));
None
}
Some(hsi) => {
let (freq, hsidiv) = match hsi {
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
};
RCC.cr().modify(|w| {
w.set_hsidiv(hsidiv);
w.set_hsion(true);
});
while !RCC.cr().read().hsirdy() {}
Some(freq)
}
};
// Configure HSE
let hse = match config.hse {
None => {
RCC.cr().modify(|w| w.set_hseon(false));
None
}
Some(hse) => {
RCC.cr().modify(|w| {
w.set_hsebyp(hse.mode != HseMode::Oscillator);
#[cfg(any(rcc_h5, rcc_h50))]
w.set_hseext(match hse.mode {
HseMode::Oscillator | HseMode::Bypass => pac::rcc::vals::Hseext::ANALOG,
HseMode::BypassDigital => pac::rcc::vals::Hseext::DIGITAL,
});
});
RCC.cr().modify(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
Some(hse.freq)
}
};
// Configure HSI48.
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
let _hsi48 = match config.hsi48 {
false => None,
true => {
while !RCC.cr().read().hsi48rdy() {}
Some(CSI_FREQ)
}
};
// Configure CSI.
RCC.cr().modify(|w| w.set_csion(config.csi));
let csi = match config.csi {
false => None,
true => {
while !RCC.cr().read().csirdy() {}
Some(CSI_FREQ)
}
};
// Configure PLLs.
let pll_input = PllInput {
csi,
hse,
hsi,
#[cfg(stm32h7)]
source: config.pll_src,
};
let pll1 = init_pll(0, config.pll1, &pll_input);
let pll2 = init_pll(1, config.pll2, &pll_input);
#[cfg(any(rcc_h5, stm32h7))]
let _pll3 = init_pll(2, config.pll3, &pll_input);
// Configure sysclk
let (sys, sw) = match config.sys {
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
};
// Check limits.
#[cfg(stm32h5)]
let (hclk_max, pclk_max) = match config.voltage_scale {
VoltageScale::Scale0 => (Hertz(250_000_000), Hertz(250_000_000)),
VoltageScale::Scale1 => (Hertz(200_000_000), Hertz(200_000_000)),
VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)),
VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)),
};
#[cfg(stm32h7)]
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)),
VoltageScale::Scale3 => (Hertz(200_000_000), Hertz(100_000_000), Hertz(50_000_000)),
};
#[cfg(stm32h7)]
let hclk = {
let d1cpre_clk = sys / config.d1c_pre;
assert!(d1cpre_clk <= d1cpre_clk_max);
sys / config.ahb_pre
};
#[cfg(stm32h5)]
let hclk = sys / config.ahb_pre;
assert!(hclk <= hclk_max);
let apb1 = hclk / config.apb1_pre;
let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler);
assert!(apb1 <= pclk_max);
let apb2 = hclk / config.apb2_pre;
let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler);
assert!(apb2 <= pclk_max);
let apb3 = hclk / config.apb3_pre;
assert!(apb3 <= pclk_max);
#[cfg(stm32h7)]
let apb4 = hclk / config.apb4_pre;
#[cfg(stm32h7)]
assert!(apb4 <= pclk_max);
let _per_ck = match config.per_clock_source {
Ckpersel::HSI => hsi,
Ckpersel::CSI => csi,
Ckpersel::HSE => hse,
_ => unreachable!(),
};
#[cfg(stm32h7)]
let adc = match config.adc_clock_source {
AdcClockSource::PLL2_P => pll2.p,
AdcClockSource::PLL3_R => _pll3.r,
AdcClockSource::PER => _per_ck,
_ => unreachable!(),
};
#[cfg(stm32h5)]
let adc = match config.adc_clock_source {
AdcClockSource::HCLK => Some(hclk),
AdcClockSource::SYSCLK => Some(sys),
AdcClockSource::PLL2_R => pll2.r,
AdcClockSource::HSE => hse,
AdcClockSource::HSI_KER => hsi,
AdcClockSource::CSI_KER => csi,
_ => unreachable!(),
};
flash_setup(hclk, config.voltage_scale);
#[cfg(stm32h7)]
{
RCC.d1cfgr().modify(|w| {
w.set_d1cpre(config.d1c_pre);
w.set_d1ppre(config.apb3_pre);
w.set_hpre(config.ahb_pre);
});
// Ensure core prescaler value is valid before future lower core voltage
while RCC.d1cfgr().read().d1cpre() != config.d1c_pre {}
RCC.d2cfgr().modify(|w| {
w.set_d2ppre1(config.apb1_pre);
w.set_d2ppre2(config.apb2_pre);
});
RCC.d3cfgr().modify(|w| {
w.set_d3ppre(config.apb4_pre);
});
RCC.d1ccipr().modify(|w| {
w.set_ckpersel(config.per_clock_source);
});
RCC.d3ccipr().modify(|w| {
w.set_adcsel(config.adc_clock_source);
});
}
#[cfg(stm32h5)]
{
// Set hpre
RCC.cfgr2().modify(|w| w.set_hpre(config.ahb_pre));
while RCC.cfgr2().read().hpre() != config.ahb_pre {}
// set ppre
RCC.cfgr2().modify(|w| {
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
w.set_ppre3(config.apb3_pre);
});
RCC.ccipr5().modify(|w| {
w.set_ckpersel(config.per_clock_source);
w.set_adcdacsel(config.adc_clock_source)
});
}
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
RCC.cfgr().modify(|w| w.set_sw(sw));
while RCC.cfgr().read().sws() != sw {}
// IO compensation cell - Requires CSI clock and SYSCFG
#[cfg(stm32h7)] // TODO h5
if csi.is_some() {
// Enable the compensation cell, using back-bias voltage code
// provide by the cell.
critical_section::with(|_| {
pac::SYSCFG.cccsr().modify(|w| {
w.set_en(true);
w.set_cs(false);
w.set_hslv(false);
})
});
while !pac::SYSCFG.cccsr().read().ready() {}
}
set_freqs(Clocks {
sys,
ahb1: hclk,
ahb2: hclk,
ahb3: hclk,
ahb4: hclk,
apb1,
apb2,
apb3,
#[cfg(stm32h7)]
apb4,
apb1_tim,
apb2_tim,
adc: adc,
});
}
struct PllInput {
hsi: Option<Hertz>,
hse: Option<Hertz>,
csi: Option<Hertz>,
#[cfg(stm32h7)]
source: PllSource,
}
struct PllOutput {
p: Option<Hertz>,
#[allow(dead_code)]
q: Option<Hertz>,
#[allow(dead_code)]
r: Option<Hertz>,
}
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
let Some(config) = config else {
// Stop PLL
RCC.cr().modify(|w| w.set_pllon(num, false));
while RCC.cr().read().pllrdy(num) {}
// "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
#[cfg(stm32h7)]
RCC.pllckselr().write(|w| w.set_divm(num, 0));
#[cfg(stm32h5)]
RCC.pllcfgr(num).write(|w| w.set_divm(0));
return PllOutput {
p: None,
q: None,
r: None,
};
};
assert!(1 <= config.prediv && config.prediv <= 63);
assert!(4 <= config.mul && config.mul <= 512);
#[cfg(stm32h5)]
let source = config.source;
#[cfg(stm32h7)]
let source = input.source;
let (in_clk, src) = match source {
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
};
let ref_clk = in_clk / config.prediv as u32;
let ref_range = match ref_clk.0 {
..=1_999_999 => Pllrge::RANGE1,
..=3_999_999 => Pllrge::RANGE2,
..=7_999_999 => Pllrge::RANGE4,
..=16_000_000 => Pllrge::RANGE8,
x => panic!("pll ref_clk out of range: {} mhz", x),
};
// The smaller range (150 to 420 MHz) must
// be chosen when the reference clock frequency is lower than 2 MHz.
let wide_allowed = ref_range != Pllrge::RANGE1;
let vco_clk = ref_clk * config.mul;
let vco_range = if VCO_RANGE.contains(&vco_clk.0) {
Pllvcosel::MEDIUMVCO
} else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk.0) {
Pllvcosel::WIDEVCO
} else {
panic!("pll vco_clk out of range: {} mhz", vco_clk.0)
};
let p = config.divp.map(|div| {
assert!(1 <= div && div <= 128);
if num == 0 {
// on PLL1, DIVP must be even.
assert!(div % 2 == 0);
}
vco_clk / div
});
let q = config.divq.map(|div| {
assert!(1 <= div && div <= 128);
vco_clk / div
});
let r = config.divr.map(|div| {
assert!(1 <= div && div <= 128);
vco_clk / div
});
#[cfg(stm32h5)]
RCC.pllcfgr(num).write(|w| {
w.set_pllsrc(src);
w.set_divm(config.prediv);
w.set_pllvcosel(vco_range);
w.set_pllrge(ref_range);
w.set_pllfracen(false);
w.set_pllpen(p.is_some());
w.set_pllqen(q.is_some());
w.set_pllren(r.is_some());
});
#[cfg(stm32h7)]
{
RCC.pllckselr().modify(|w| {
w.set_divm(num, config.prediv);
w.set_pllsrc(src);
});
RCC.pllcfgr().modify(|w| {
w.set_pllvcosel(num, vco_range);
w.set_pllrge(num, ref_range);
w.set_pllfracen(num, false);
w.set_divpen(num, p.is_some());
w.set_divqen(num, q.is_some());
w.set_divren(num, r.is_some());
});
}
RCC.plldivr(num).write(|w| {
w.set_plln(config.mul - 1);
w.set_pllp((config.divp.unwrap_or(1) - 1) as u8);
w.set_pllq((config.divq.unwrap_or(1) - 1) as u8);
w.set_pllr((config.divr.unwrap_or(1) - 1) as u8);
});
RCC.cr().modify(|w| w.set_pllon(num, true));
while !RCC.cr().read().pllrdy(num) {}
PllOutput { p, q, r }
}
fn flash_setup(clk: Hertz, vos: VoltageScale) {
// RM0481 Rev 1, table 37
// LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0
// 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz
// 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz
// 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz
// 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz
// 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz
// 5 2 170 to 200 MHz 210 to 250 MHz
#[cfg(stm32h5)]
let (latency, wrhighfreq) = match (vos, clk.0) {
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
(VoltageScale::Scale0, ..=250_000_000) => (5, 2),
(VoltageScale::Scale1, ..=34_000_000) => (0, 0),
(VoltageScale::Scale1, ..=68_000_000) => (1, 0),
(VoltageScale::Scale1, ..=102_000_000) => (2, 1),
(VoltageScale::Scale1, ..=136_000_000) => (3, 1),
(VoltageScale::Scale1, ..=170_000_000) => (4, 2),
(VoltageScale::Scale1, ..=200_000_000) => (5, 2),
(VoltageScale::Scale2, ..=30_000_000) => (0, 0),
(VoltageScale::Scale2, ..=60_000_000) => (1, 0),
(VoltageScale::Scale2, ..=90_000_000) => (2, 1),
(VoltageScale::Scale2, ..=120_000_000) => (3, 1),
(VoltageScale::Scale2, ..=150_000_000) => (4, 2),
(VoltageScale::Scale3, ..=20_000_000) => (0, 0),
(VoltageScale::Scale3, ..=40_000_000) => (1, 0),
(VoltageScale::Scale3, ..=60_000_000) => (2, 1),
(VoltageScale::Scale3, ..=80_000_000) => (3, 1),
(VoltageScale::Scale3, ..=100_000_000) => (4, 2),
_ => unreachable!(),
};
#[cfg(flash_h7)]
let (latency, wrhighfreq) = match (vos, clk.0) {
// VOS 0 range VCORE 1.26V - 1.40V
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
(VoltageScale::Scale0, ..=140_000_000) => (1, 1),
(VoltageScale::Scale0, ..=185_000_000) => (2, 1),
(VoltageScale::Scale0, ..=210_000_000) => (2, 2),
(VoltageScale::Scale0, ..=225_000_000) => (3, 2),
(VoltageScale::Scale0, ..=240_000_000) => (4, 2),
// VOS 1 range VCORE 1.15V - 1.26V
(VoltageScale::Scale1, ..=70_000_000) => (0, 0),
(VoltageScale::Scale1, ..=140_000_000) => (1, 1),
(VoltageScale::Scale1, ..=185_000_000) => (2, 1),
(VoltageScale::Scale1, ..=210_000_000) => (2, 2),
(VoltageScale::Scale1, ..=225_000_000) => (3, 2),
// VOS 2 range VCORE 1.05V - 1.15V
(VoltageScale::Scale2, ..=55_000_000) => (0, 0),
(VoltageScale::Scale2, ..=110_000_000) => (1, 1),
(VoltageScale::Scale2, ..=165_000_000) => (2, 1),
(VoltageScale::Scale2, ..=224_000_000) => (3, 2),
// VOS 3 range VCORE 0.95V - 1.05V
(VoltageScale::Scale3, ..=45_000_000) => (0, 0),
(VoltageScale::Scale3, ..=90_000_000) => (1, 1),
(VoltageScale::Scale3, ..=135_000_000) => (2, 1),
(VoltageScale::Scale3, ..=180_000_000) => (3, 2),
(VoltageScale::Scale3, ..=224_000_000) => (4, 2),
_ => unreachable!(),
};
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
// states and programming delay
#[cfg(flash_h7ab)]
let (latency, wrhighfreq) = match (vos, clk.0) {
// VOS 0 range VCORE 1.25V - 1.35V
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
(VoltageScale::Scale0, ..=252_000_000) => (5, 2),
(VoltageScale::Scale0, ..=280_000_000) => (6, 3),
// VOS 1 range VCORE 1.15V - 1.25V
(VoltageScale::Scale1, ..=38_000_000) => (0, 0),
(VoltageScale::Scale1, ..=76_000_000) => (1, 0),
(VoltageScale::Scale1, ..=114_000_000) => (2, 1),
(VoltageScale::Scale1, ..=152_000_000) => (3, 1),
(VoltageScale::Scale1, ..=190_000_000) => (4, 2),
(VoltageScale::Scale1, ..=225_000_000) => (5, 2),
// VOS 2 range VCORE 1.05V - 1.15V
(VoltageScale::Scale2, ..=34) => (0, 0),
(VoltageScale::Scale2, ..=68) => (1, 0),
(VoltageScale::Scale2, ..=102) => (2, 1),
(VoltageScale::Scale2, ..=136) => (3, 1),
(VoltageScale::Scale2, ..=160) => (4, 2),
// VOS 3 range VCORE 0.95V - 1.05V
(VoltageScale::Scale3, ..=22) => (0, 0),
(VoltageScale::Scale3, ..=44) => (1, 0),
(VoltageScale::Scale3, ..=66) => (2, 1),
(VoltageScale::Scale3, ..=88) => (3, 1),
_ => unreachable!(),
};
debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
FLASH.acr().write(|w| {
w.set_wrhighfreq(wrhighfreq);
w.set_latency(latency);
});
while FLASH.acr().read().latency() != latency {}
}

View File

@ -1,511 +0,0 @@
use core::marker::PhantomData;
use stm32_metapac::rcc::vals::Timpre;
use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
use crate::{peripherals, Peripheral};
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
/// CSI speed
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
/// HSI48 speed
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
/// LSI speed
pub const LSI_FREQ: Hertz = Hertz(32_000);
const VCO_MIN: u32 = 150_000_000;
const VCO_MAX: u32 = 420_000_000;
const VCO_WIDE_MIN: u32 = 128_000_000;
const VCO_WIDE_MAX: u32 = 560_000_000;
pub use super::bus::{AHBPrescaler, APBPrescaler};
pub use crate::pac::pwr::vals::Vos as VoltageScale;
pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0)
Oscillator,
/// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
BypassAnalog,
/// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
BypassDigital,
}
pub struct Hse {
/// HSE frequency.
pub freq: Hertz,
/// HSE mode.
pub mode: HseMode,
}
pub enum Hsi {
/// 64Mhz
Mhz64,
/// 32Mhz (divided by 2)
Mhz32,
/// 16Mhz (divided by 4)
Mhz16,
/// 8Mhz (divided by 8)
Mhz8,
}
pub enum Sysclk {
/// HSI selected as sysclk
HSI,
/// HSE selected as sysclk
HSE,
/// CSI selected as sysclk
CSI,
/// PLL1_P selected as sysclk
Pll1P,
}
pub enum PllSource {
Hsi,
Csi,
Hse,
}
pub struct Pll {
/// Source clock selection.
pub source: PllSource,
/// PLL pre-divider (DIVM). Must be between 1 and 63.
pub prediv: u8,
/// PLL multiplication factor. Must be between 4 and 512.
pub mul: u16,
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
/// On PLL1, it must be even (in particular, it cannot be 1.)
pub divp: Option<u16>,
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
pub divq: Option<u16>,
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
pub divr: Option<u16>,
}
fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {
match (tim, apb) {
// The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a
// division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2
(TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32,
(TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32,
// The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2
// corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2
// this makes NO SENSE and is different than in the H7. Mistake in the RM??
(TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk * 2u32,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk / 2u32,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32,
(TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32,
_ => unreachable!(),
}
}
/// APB prescaler
#[derive(Clone, Copy)]
pub enum TimerPrescaler {
DefaultX2,
DefaultX4,
}
impl From<TimerPrescaler> for Timpre {
fn from(value: TimerPrescaler) -> Self {
match value {
TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2,
TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4,
}
}
}
/// Configuration of the core clocks
#[non_exhaustive]
pub struct Config {
pub hsi: Option<Hsi>,
pub hse: Option<Hse>,
pub csi: bool,
pub hsi48: bool,
pub sys: Sysclk,
pub pll1: Option<Pll>,
pub pll2: Option<Pll>,
#[cfg(rcc_h5)]
pub pll3: Option<Pll>,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub apb3_pre: APBPrescaler,
pub timer_prescaler: TimerPrescaler,
pub voltage_scale: VoltageScale,
}
impl Default for Config {
fn default() -> Self {
Self {
hsi: Some(Hsi::Mhz64),
hse: None,
csi: false,
hsi48: false,
sys: Sysclk::HSI,
pll1: None,
pll2: None,
#[cfg(rcc_h5)]
pll3: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1,
timer_prescaler: TimerPrescaler::DefaultX2,
voltage_scale: VoltageScale::SCALE3,
}
}
}
pub(crate) mod sealed {
pub trait McoInstance {
type Source;
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
}
}
pub trait McoInstance: sealed::McoInstance + 'static {}
pin_trait!(McoPin, McoInstance);
macro_rules! impl_peri {
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
impl sealed::McoInstance for peripherals::$peri {
type Source = $source;
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
RCC.cfgr().modify(|w| {
w.$set_source(source);
w.$set_prescaler(prescaler);
});
}
}
impl McoInstance for peripherals::$peri {}
};
}
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
pub struct Mco<'d, T: McoInstance> {
phantom: PhantomData<&'d mut T>,
}
impl<'d, T: McoInstance> Mco<'d, T> {
pub fn new(
_peri: impl Peripheral<P = T> + 'd,
_pin: impl Peripheral<P = impl McoPin<T>> + 'd,
_source: T::Source,
) -> Self {
todo!();
}
}
pub(crate) unsafe fn init(config: Config) {
let max_clk = match config.voltage_scale {
VoltageScale::SCALE0 => Hertz(250_000_000),
VoltageScale::SCALE1 => Hertz(200_000_000),
VoltageScale::SCALE2 => Hertz(150_000_000),
VoltageScale::SCALE3 => Hertz(100_000_000),
};
// Configure voltage scale.
PWR.voscr().modify(|w| w.set_vos(config.voltage_scale));
while !PWR.vossr().read().vosrdy() {}
// Configure HSI
let hsi = match config.hsi {
None => {
RCC.cr().modify(|w| w.set_hsion(false));
None
}
Some(hsi) => {
let (freq, hsidiv) = match hsi {
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
};
RCC.cr().modify(|w| {
w.set_hsidiv(hsidiv);
w.set_hsion(true);
});
while !RCC.cr().read().hsirdy() {}
Some(freq)
}
};
// Configure HSE
let hse = match config.hse {
None => {
RCC.cr().modify(|w| w.set_hseon(false));
None
}
Some(hse) => {
let (byp, ext) = match hse.mode {
HseMode::Oscillator => (false, Hseext::ANALOG),
HseMode::BypassAnalog => (true, Hseext::ANALOG),
HseMode::BypassDigital => (true, Hseext::DIGITAL),
};
RCC.cr().modify(|w| {
w.set_hsebyp(byp);
w.set_hseext(ext);
});
RCC.cr().modify(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
Some(hse.freq)
}
};
// Configure HSI48.
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
let _hsi48 = match config.hsi48 {
false => None,
true => {
while !RCC.cr().read().hsi48rdy() {}
Some(CSI_FREQ)
}
};
// Configure CSI.
RCC.cr().modify(|w| w.set_csion(config.csi));
let csi = match config.csi {
false => None,
true => {
while !RCC.cr().read().csirdy() {}
Some(CSI_FREQ)
}
};
// Configure PLLs.
let pll_input = PllInput { csi, hse, hsi };
let pll1 = init_pll(0, config.pll1, &pll_input);
let _pll2 = init_pll(1, config.pll2, &pll_input);
#[cfg(rcc_h5)]
let _pll3 = init_pll(2, config.pll3, &pll_input);
// Configure sysclk
let (sys, sw) = match config.sys {
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
};
assert!(sys <= max_clk);
let hclk = sys / config.ahb_pre;
let apb1 = hclk / config.apb1_pre;
let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler);
let apb2 = hclk / config.apb2_pre;
let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler);
let apb3 = hclk / config.apb3_pre;
flash_setup(hclk, config.voltage_scale);
// Set hpre
let hpre = config.ahb_pre.into();
RCC.cfgr2().modify(|w| w.set_hpre(hpre));
while RCC.cfgr2().read().hpre() != hpre {}
// set ppre
RCC.cfgr2().modify(|w| {
w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(config.apb2_pre.into());
w.set_ppre3(config.apb3_pre.into());
});
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
RCC.cfgr().modify(|w| w.set_sw(sw));
while RCC.cfgr().read().sws() != sw {}
set_freqs(Clocks {
sys,
ahb1: hclk,
ahb2: hclk,
ahb3: hclk,
ahb4: hclk,
apb1,
apb2,
apb3,
apb1_tim,
apb2_tim,
adc: None,
});
}
struct PllInput {
hsi: Option<Hertz>,
hse: Option<Hertz>,
csi: Option<Hertz>,
}
struct PllOutput {
p: Option<Hertz>,
#[allow(dead_code)]
q: Option<Hertz>,
#[allow(dead_code)]
r: Option<Hertz>,
}
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
let Some(config) = config else {
// Stop PLL
RCC.cr().modify(|w| w.set_pllon(num, false));
while RCC.cr().read().pllrdy(num) {}
// "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
RCC.pllcfgr(num).write(|w| {
w.set_divm(0);
});
return PllOutput {
p: None,
q: None,
r: None,
};
};
assert!(1 <= config.prediv && config.prediv <= 63);
assert!(4 <= config.mul && config.mul <= 512);
let (in_clk, src) = match config.source {
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
};
let ref_clk = in_clk / config.prediv as u32;
let ref_range = match ref_clk.0 {
..=1_999_999 => Pllrge::RANGE1,
..=3_999_999 => Pllrge::RANGE2,
..=7_999_999 => Pllrge::RANGE4,
..=16_000_000 => Pllrge::RANGE8,
x => panic!("pll ref_clk out of range: {} mhz", x),
};
// The smaller range (150 to 420 MHz) must
// be chosen when the reference clock frequency is lower than 2 MHz.
let wide_allowed = ref_range != Pllrge::RANGE1;
let vco_clk = ref_clk * config.mul;
let vco_range = match vco_clk.0 {
VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO,
VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO,
x => panic!("pll vco_clk out of range: {} mhz", x),
};
let p = config.divp.map(|div| {
assert!(1 <= div && div <= 128);
if num == 0 {
// on PLL1, DIVP must be even.
assert!(div % 2 == 0);
}
vco_clk / div
});
let q = config.divq.map(|div| {
assert!(1 <= div && div <= 128);
vco_clk / div
});
let r = config.divr.map(|div| {
assert!(1 <= div && div <= 128);
vco_clk / div
});
RCC.pllcfgr(num).write(|w| {
w.set_pllsrc(src);
w.set_divm(config.prediv);
w.set_pllvcosel(vco_range);
w.set_pllrge(ref_range);
w.set_pllfracen(false);
w.set_pllpen(p.is_some());
w.set_pllqen(q.is_some());
w.set_pllren(r.is_some());
});
RCC.plldivr(num).write(|w| {
w.set_plln(config.mul - 1);
w.set_pllp((config.divp.unwrap_or(1) - 1) as u8);
w.set_pllq((config.divq.unwrap_or(1) - 1) as u8);
w.set_pllr((config.divr.unwrap_or(1) - 1) as u8);
});
RCC.cr().modify(|w| w.set_pllon(num, true));
while !RCC.cr().read().pllrdy(num) {}
PllOutput { p, q, r }
}
fn flash_setup(clk: Hertz, vos: VoltageScale) {
// RM0481 Rev 1, table 37
// LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0
// 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz
// 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz
// 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz
// 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz
// 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz
// 5 2 170 to 200 MHz 210 to 250 MHz
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait
// states and programming delay
let (latency, wrhighfreq) = match (vos, clk.0) {
(VoltageScale::SCALE0, ..=42_000_000) => (0, 0),
(VoltageScale::SCALE0, ..=84_000_000) => (1, 0),
(VoltageScale::SCALE0, ..=126_000_000) => (2, 1),
(VoltageScale::SCALE0, ..=168_000_000) => (3, 1),
(VoltageScale::SCALE0, ..=210_000_000) => (4, 2),
(VoltageScale::SCALE0, ..=250_000_000) => (5, 2),
(VoltageScale::SCALE1, ..=34_000_000) => (0, 0),
(VoltageScale::SCALE1, ..=68_000_000) => (1, 0),
(VoltageScale::SCALE1, ..=102_000_000) => (2, 1),
(VoltageScale::SCALE1, ..=136_000_000) => (3, 1),
(VoltageScale::SCALE1, ..=170_000_000) => (4, 2),
(VoltageScale::SCALE1, ..=200_000_000) => (5, 2),
(VoltageScale::SCALE2, ..=30_000_000) => (0, 0),
(VoltageScale::SCALE2, ..=60_000_000) => (1, 0),
(VoltageScale::SCALE2, ..=90_000_000) => (2, 1),
(VoltageScale::SCALE2, ..=120_000_000) => (3, 1),
(VoltageScale::SCALE2, ..=150_000_000) => (4, 2),
(VoltageScale::SCALE3, ..=20_000_000) => (0, 0),
(VoltageScale::SCALE3, ..=40_000_000) => (1, 0),
(VoltageScale::SCALE3, ..=60_000_000) => (2, 1),
(VoltageScale::SCALE3, ..=80_000_000) => (3, 1),
(VoltageScale::SCALE3, ..=100_000_000) => (4, 2),
_ => unreachable!(),
};
debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
FLASH.acr().write(|w| {
w.set_wrhighfreq(wrhighfreq);
w.set_latency(latency);
});
while FLASH.acr().read().latency() != latency {}
}

View File

@ -1,934 +0,0 @@
use core::marker::PhantomData;
use embassy_hal_internal::into_ref;
use stm32_metapac::pwr::vals::Vos;
use stm32_metapac::rcc::vals::{Mco1, Mco2};
pub use self::pll::PllConfig;
use crate::gpio::sealed::AFType;
use crate::gpio::Speed;
use crate::pac::rcc::vals::{Adcsel, Ckpersel, Hpre, Hsidiv, Pllsrc, Ppre, Sw, Timpre};
use crate::pac::{PWR, RCC, SYSCFG};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
use crate::{peripherals, Peripheral};
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
/// CSI speed
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
/// HSI48 speed
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
/// LSI speed
pub const LSI_FREQ: Hertz = Hertz(32_000);
#[derive(Clone, Copy)]
pub enum VoltageScale {
Scale0,
Scale1,
Scale2,
Scale3,
}
#[derive(Clone, Copy)]
pub enum AdcClockSource {
Pll2PCk,
Pll3RCk,
PerCk,
}
impl AdcClockSource {
pub fn adcsel(&self) -> Adcsel {
match self {
AdcClockSource::Pll2PCk => Adcsel::PLL2_P,
AdcClockSource::Pll3RCk => Adcsel::PLL3_R,
AdcClockSource::PerCk => Adcsel::PER,
}
}
}
impl Default for AdcClockSource {
fn default() -> Self {
Self::Pll2PCk
}
}
/// Core clock frequencies
#[derive(Clone, Copy)]
pub struct CoreClocks {
pub hclk: Hertz,
pub pclk1: Hertz,
pub pclk2: Hertz,
pub pclk3: Hertz,
pub pclk4: Hertz,
pub ppre1: u8,
pub ppre2: u8,
pub ppre3: u8,
pub ppre4: u8,
pub csi_ck: Option<Hertz>,
pub hsi_ck: Option<Hertz>,
pub hsi48_ck: Option<Hertz>,
pub lsi_ck: Option<Hertz>,
pub per_ck: Option<Hertz>,
pub hse_ck: Option<Hertz>,
pub pll1_p_ck: Option<Hertz>,
pub pll1_q_ck: Option<Hertz>,
pub pll1_r_ck: Option<Hertz>,
pub pll2_p_ck: Option<Hertz>,
pub pll2_q_ck: Option<Hertz>,
pub pll2_r_ck: Option<Hertz>,
pub pll3_p_ck: Option<Hertz>,
pub pll3_q_ck: Option<Hertz>,
pub pll3_r_ck: Option<Hertz>,
pub timx_ker_ck: Option<Hertz>,
pub timy_ker_ck: Option<Hertz>,
pub adc_ker_ck: Option<Hertz>,
pub sys_ck: Hertz,
pub c_ck: Hertz,
}
/// Configuration of the core clocks
#[non_exhaustive]
pub struct Config {
pub hse: Option<Hertz>,
pub bypass_hse: bool,
pub sys_ck: Option<Hertz>,
pub per_ck: Option<Hertz>,
pub hclk: Option<Hertz>,
pub pclk1: Option<Hertz>,
pub pclk2: Option<Hertz>,
pub pclk3: Option<Hertz>,
pub pclk4: Option<Hertz>,
pub pll1: PllConfig,
pub pll2: PllConfig,
pub pll3: PllConfig,
pub adc_clock_source: AdcClockSource,
pub voltage_scale: VoltageScale,
}
impl Default for Config {
fn default() -> Self {
Self {
hse: None,
bypass_hse: false,
sys_ck: None,
per_ck: None,
hclk: None,
pclk1: None,
pclk2: None,
pclk3: None,
pclk4: None,
pll1: Default::default(),
pll2: Default::default(),
pll3: Default::default(),
adc_clock_source: Default::default(),
voltage_scale: VoltageScale::Scale1,
}
}
}
/// Setup traceclk
/// Returns a pll1_r_ck
fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) {
let pll1_r_ck = match (sys_use_pll1_p, config.pll1.r_ck) {
// pll1_p_ck selected as system clock but pll1_r_ck not
// set. The traceclk mux is synchronous with the system
// clock mux, but has pll1_r_ck as an input. In order to
// keep traceclk running, we force a pll1_r_ck.
(true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)),
// Either pll1 not selected as system clock, free choice
// of pll1_r_ck. Or pll1 is selected, assume user has set
// a suitable pll1_r_ck frequency.
_ => config.pll1.r_ck,
};
config.pll1.r_ck = pll1_r_ck;
}
/// Divider calculator for pclk 1 - 4
///
/// Returns real pclk, bits, ppre and the timer kernel clock
fn ppre_calculate(
requested_pclk: u32,
hclk: u32,
max_pclk: u32,
tim_pre: Option<Timpre>,
) -> (u32, u8, u8, Option<u32>) {
let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk {
0 => panic!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let real_pclk = hclk / u32::from(ppre);
assert!(real_pclk <= max_pclk);
let tim_ker_clk = if let Some(tim_pre) = tim_pre {
let clk = match (bits, tim_pre) {
(0b101, Timpre::DEFAULTX2) => hclk / 2,
(0b110, Timpre::DEFAULTX4) => hclk / 2,
(0b110, Timpre::DEFAULTX2) => hclk / 4,
(0b111, Timpre::DEFAULTX4) => hclk / 4,
(0b111, Timpre::DEFAULTX2) => hclk / 8,
_ => hclk,
};
Some(clk)
} else {
None
};
(real_pclk, bits, ppre, tim_ker_clk)
}
/// Setup sys_ck
/// Returns sys_ck frequency, and a pll1_p_ck
fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) {
// Compare available with wanted clocks
let sys_ck = config.sys_ck.unwrap_or(srcclk);
if sys_ck != srcclk {
// The requested system clock is not the immediately available
// HSE/HSI clock. Perhaps there are other ways of obtaining
// the requested system clock (such as `HSIDIV`) but we will
// ignore those for now.
//
// Therefore we must use pll1_p_ck
let pll1_p_ck = match config.pll1.p_ck {
Some(p_ck) => {
assert!(
p_ck == sys_ck,
"Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck"
);
Some(p_ck)
}
None => Some(sys_ck),
};
config.pll1.p_ck = pll1_p_ck;
(sys_ck, true)
} else {
// sys_ck is derived directly from a source clock
// (HSE/HSI). pll1_p_ck can be as requested
(sys_ck, false)
}
}
fn flash_setup(rcc_aclk: u32, vos: VoltageScale) {
use crate::pac::FLASH;
// ACLK in MHz, round down and subtract 1 from integers. eg.
// 61_999_999 -> 61MHz
// 62_000_000 -> 61MHz
// 62_000_001 -> 62MHz
let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000;
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait
// states and programming delay
#[cfg(flash_h7)]
let (wait_states, progr_delay) = match vos {
// VOS 0 range VCORE 1.26V - 1.40V
VoltageScale::Scale0 => match rcc_aclk_mhz {
0..=69 => (0, 0),
70..=139 => (1, 1),
140..=184 => (2, 1),
185..=209 => (2, 2),
210..=224 => (3, 2),
225..=239 => (4, 2),
_ => (7, 3),
},
// VOS 1 range VCORE 1.15V - 1.26V
VoltageScale::Scale1 => match rcc_aclk_mhz {
0..=69 => (0, 0),
70..=139 => (1, 1),
140..=184 => (2, 1),
185..=209 => (2, 2),
210..=224 => (3, 2),
_ => (7, 3),
},
// VOS 2 range VCORE 1.05V - 1.15V
VoltageScale::Scale2 => match rcc_aclk_mhz {
0..=54 => (0, 0),
55..=109 => (1, 1),
110..=164 => (2, 1),
165..=224 => (3, 2),
_ => (7, 3),
},
// VOS 3 range VCORE 0.95V - 1.05V
VoltageScale::Scale3 => match rcc_aclk_mhz {
0..=44 => (0, 0),
45..=89 => (1, 1),
90..=134 => (2, 1),
135..=179 => (3, 2),
180..=224 => (4, 2),
_ => (7, 3),
},
};
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
// states and programming delay
#[cfg(flash_h7ab)]
let (wait_states, progr_delay) = match vos {
// VOS 0 range VCORE 1.25V - 1.35V
VoltageScale::Scale0 => match rcc_aclk_mhz {
0..=42 => (0, 0),
43..=84 => (1, 0),
85..=126 => (2, 1),
127..=168 => (3, 1),
169..=210 => (4, 2),
211..=252 => (5, 2),
253..=280 => (6, 3),
_ => (7, 3),
},
// VOS 1 range VCORE 1.15V - 1.25V
VoltageScale::Scale1 => match rcc_aclk_mhz {
0..=38 => (0, 0),
39..=76 => (1, 0),
77..=114 => (2, 1),
115..=152 => (3, 1),
153..=190 => (4, 2),
191..=225 => (5, 2),
_ => (7, 3),
},
// VOS 2 range VCORE 1.05V - 1.15V
VoltageScale::Scale2 => match rcc_aclk_mhz {
0..=34 => (0, 0),
35..=68 => (1, 0),
69..=102 => (2, 1),
103..=136 => (3, 1),
137..=160 => (4, 2),
_ => (7, 3),
},
// VOS 3 range VCORE 0.95V - 1.05V
VoltageScale::Scale3 => match rcc_aclk_mhz {
0..=22 => (0, 0),
23..=44 => (1, 0),
45..=66 => (2, 1),
67..=88 => (3, 1),
_ => (7, 3),
},
};
FLASH.acr().write(|w| {
w.set_wrhighfreq(progr_delay);
w.set_latency(wait_states)
});
while FLASH.acr().read().latency() != wait_states {}
}
pub enum McoClock {
Disabled,
Bypassed,
Divided(u8),
}
impl McoClock {
fn into_raw(&self) -> u8 {
match self {
McoClock::Disabled => 0,
McoClock::Bypassed => 1,
McoClock::Divided(divisor) => {
if *divisor > 15 {
panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.")
}
*divisor
}
}
}
}
#[derive(Copy, Clone)]
pub enum Mco1Source {
Hsi,
Lse,
Hse,
Pll1Q,
Hsi48,
}
impl Default for Mco1Source {
fn default() -> Self {
Self::Hsi
}
}
pub trait McoSource {
type Raw;
fn into_raw(&self) -> Self::Raw;
}
impl McoSource for Mco1Source {
type Raw = Mco1;
fn into_raw(&self) -> Self::Raw {
match self {
Mco1Source::Hsi => Mco1::HSI,
Mco1Source::Lse => Mco1::LSE,
Mco1Source::Hse => Mco1::HSE,
Mco1Source::Pll1Q => Mco1::PLL1_Q,
Mco1Source::Hsi48 => Mco1::HSI48,
}
}
}
#[derive(Copy, Clone)]
pub enum Mco2Source {
SysClk,
Pll2Q,
Hse,
Pll1Q,
Csi,
Lsi,
}
impl Default for Mco2Source {
fn default() -> Self {
Self::SysClk
}
}
impl McoSource for Mco2Source {
type Raw = Mco2;
fn into_raw(&self) -> Self::Raw {
match self {
Mco2Source::SysClk => Mco2::SYSCLK,
Mco2Source::Pll2Q => Mco2::PLL2_P,
Mco2Source::Hse => Mco2::HSE,
Mco2Source::Pll1Q => Mco2::PLL1_P,
Mco2Source::Csi => Mco2::CSI,
Mco2Source::Lsi => Mco2::LSI,
}
}
}
pub(crate) mod sealed {
pub trait McoInstance {
type Source;
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
}
}
pub trait McoInstance: sealed::McoInstance + 'static {}
pin_trait!(McoPin, McoInstance);
macro_rules! impl_peri {
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
impl sealed::McoInstance for peripherals::$peri {
type Source = $source;
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
RCC.cfgr().modify(|w| {
w.$set_source(source);
w.$set_prescaler(prescaler);
});
}
}
impl McoInstance for peripherals::$peri {}
};
}
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
pub struct Mco<'d, T: McoInstance> {
phantom: PhantomData<&'d mut T>,
}
impl<'d, T: McoInstance> Mco<'d, T> {
pub fn new(
_peri: impl Peripheral<P = T> + 'd,
pin: impl Peripheral<P = impl McoPin<T>> + 'd,
source: impl McoSource<Raw = T::Source>,
prescaler: McoClock,
) -> Self {
into_ref!(pin);
critical_section::with(|_| unsafe {
T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
pin.set_speed(Speed::VeryHigh);
});
Self { phantom: PhantomData }
}
}
pub(crate) unsafe fn init(mut config: Config) {
// NB. The lower bytes of CR3 can only be written once after
// POR, and must be written with a valid combination. Refer to
// RM0433 Rev 7 6.8.4. This is partially enforced by dropping
// `self` at the end of this method, but of course we cannot
// know what happened between the previous POR and here.
#[cfg(pwr_h7rm0433)]
PWR.cr3().modify(|w| {
w.set_scuen(true);
w.set_ldoen(true);
w.set_bypass(false);
});
#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
PWR.cr3().modify(|w| {
// hardcode "Direct SPMS" for now, this is what works on nucleos with the
// default solderbridge configuration.
w.set_sden(true);
w.set_ldoen(false);
});
// Validate the supply configuration. If you are stuck here, it is
// because the voltages on your board do not match those specified
// in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset
// VOS = Scale 3, so check that the voltage on the VCAP pins =
// 1.0V.
info!("a");
while !PWR.csr1().read().actvosrdy() {}
info!("b");
#[cfg(syscfg_h7)]
{
// in chips without the overdrive bit, we can go from any scale to any scale directly.
PWR.d3cr().modify(|w| {
w.set_vos(match config.voltage_scale {
VoltageScale::Scale0 => Vos::SCALE0,
VoltageScale::Scale1 => Vos::SCALE1,
VoltageScale::Scale2 => Vos::SCALE2,
VoltageScale::Scale3 => Vos::SCALE3,
})
});
while !PWR.d3cr().read().vosrdy() {}
}
#[cfg(syscfg_h7od)]
{
match config.voltage_scale {
VoltageScale::Scale0 => {
// to go to scale0, we must go to Scale1 first...
PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1));
while !PWR.d3cr().read().vosrdy() {}
// Then enable overdrive.
critical_section::with(|_| {
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
SYSCFG.pwrcr().modify(|w| w.set_oden(1));
});
while !PWR.d3cr().read().vosrdy() {}
}
_ => {
// for all other scales, we can go directly.
PWR.d3cr().modify(|w| {
w.set_vos(match config.voltage_scale {
VoltageScale::Scale0 => unreachable!(),
VoltageScale::Scale1 => Vos::SCALE1,
VoltageScale::Scale2 => Vos::SCALE2,
VoltageScale::Scale3 => Vos::SCALE3,
})
});
while !PWR.d3cr().read().vosrdy() {}
}
}
}
// Freeze the core clocks, returning a Core Clocks Distribution
// and Reset (CCDR) structure. The actual frequency of the clocks
// configured is returned in the `clocks` member of the CCDR
// structure.
//
// Note that `freeze` will never result in a clock _faster_ than
// that specified. It may result in a clock that is a factor of [1,
// 2) slower.
//
// `syscfg` is required to enable the I/O compensation cell.
//
// # Panics
//
// If a clock specification cannot be achieved within the
// hardware specification then this function will panic. This
// function may also panic if a clock specification can be
// achieved, but the mechanism for doing so is not yet
// implemented here.
let srcclk = config.hse.unwrap_or(HSI_FREQ); // Available clocks
let (sys_ck, sys_use_pll1_p) = sys_ck_setup(&mut config, srcclk);
// Configure traceclk from PLL if needed
traceclk_setup(&mut config, sys_use_pll1_p);
let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0);
let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1);
let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2);
let sys_ck = if sys_use_pll1_p {
Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup
} else {
sys_ck
};
// This routine does not support HSIDIV != 1. To
// do so it would need to ensure all PLLxON bits are clear
// before changing the value of HSIDIV
let cr = RCC.cr().read();
assert!(cr.hsion());
assert!(cr.hsidiv() == Hsidiv::DIV1);
RCC.csr().modify(|w| w.set_lsion(true));
while !RCC.csr().read().lsirdy() {}
// per_ck from HSI by default
let (per_ck, ckpersel) = match (config.per_ck == config.hse, config.per_ck) {
(true, Some(hse)) => (hse, Ckpersel::HSE), // HSE
(_, Some(CSI_FREQ)) => (CSI_FREQ, Ckpersel::CSI), // CSI
_ => (HSI_FREQ, Ckpersel::HSI), // HSI
};
// D1 Core Prescaler
// Set to 1
let d1cpre_bits = 0;
let d1cpre_div = 1;
let sys_d1cpre_ck = sys_ck.0 / d1cpre_div;
// Refer to part datasheet "General operating conditions"
// table for (rev V). We do not assert checks for earlier
// revisions which may have lower limits.
let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match config.voltage_scale {
VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000),
VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000),
VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000),
VoltageScale::Scale3 => (200_000_000, 100_000_000, 50_000_000),
};
assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max);
let rcc_hclk = config.hclk.map(|v| v.0).unwrap_or(sys_d1cpre_ck / 2);
assert!(rcc_hclk <= rcc_hclk_max);
// Estimate divisor
let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk {
0 => panic!(),
1 => (Hpre::DIV1, 1),
2 => (Hpre::DIV2, 2),
3..=5 => (Hpre::DIV4, 4),
6..=11 => (Hpre::DIV8, 8),
12..=39 => (Hpre::DIV16, 16),
40..=95 => (Hpre::DIV64, 64),
96..=191 => (Hpre::DIV128, 128),
192..=383 => (Hpre::DIV256, 256),
_ => (Hpre::DIV512, 512),
};
// Calculate real AXI and AHB clock
let rcc_hclk = sys_d1cpre_ck / hpre_div;
assert!(rcc_hclk <= rcc_hclk_max);
let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7
// Timer prescaler selection
let timpre = Timpre::DEFAULTX2;
let requested_pclk1 = config.pclk1.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
let (rcc_pclk1, ppre1_bits, ppre1, rcc_timerx_ker_ck) =
ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre));
let requested_pclk2 = config.pclk2.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
let (rcc_pclk2, ppre2_bits, ppre2, rcc_timery_ker_ck) =
ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre));
let requested_pclk3 = config.pclk3.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
let (rcc_pclk3, ppre3_bits, ppre3, _) = ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None);
let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None);
// Start switching clocks -------------------
// Ensure CSI is on and stable
RCC.cr().modify(|w| w.set_csion(true));
while !RCC.cr().read().csirdy() {}
// Ensure HSI48 is on and stable
RCC.cr().modify(|w| w.set_hsi48on(true));
while !RCC.cr().read().hsi48on() {}
// XXX: support MCO ?
let hse_ck = match config.hse {
Some(hse) => {
// Ensure HSE is on and stable
RCC.cr().modify(|w| {
w.set_hseon(true);
w.set_hsebyp(config.bypass_hse);
});
while !RCC.cr().read().hserdy() {}
Some(hse)
}
None => None,
};
let pllsrc = if config.hse.is_some() { Pllsrc::HSE } else { Pllsrc::HSI };
RCC.pllckselr().modify(|w| w.set_pllsrc(pllsrc));
let enable_pll = |pll| {
RCC.cr().modify(|w| w.set_pllon(pll, true));
while !RCC.cr().read().pllrdy(pll) {}
};
if pll1_p_ck.is_some() {
enable_pll(0);
}
if pll2_p_ck.is_some() {
enable_pll(1);
}
if pll3_p_ck.is_some() {
enable_pll(2);
}
// Core Prescaler / AHB Prescaler / APB3 Prescaler
RCC.d1cfgr().modify(|w| {
w.set_d1cpre(Hpre::from_bits(d1cpre_bits));
w.set_d1ppre(Ppre::from_bits(ppre3_bits));
w.set_hpre(hpre_bits)
});
// Ensure core prescaler value is valid before future lower
// core voltage
while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
flash_setup(rcc_aclk, config.voltage_scale);
// APB1 / APB2 Prescaler
RCC.d2cfgr().modify(|w| {
w.set_d2ppre1(Ppre::from_bits(ppre1_bits));
w.set_d2ppre2(Ppre::from_bits(ppre2_bits));
});
// APB4 Prescaler
RCC.d3cfgr().modify(|w| w.set_d3ppre(Ppre::from_bits(ppre4_bits)));
// Peripheral Clock (per_ck)
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
// ADC clock MUX
RCC.d3ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel()));
let adc_ker_ck = match config.adc_clock_source {
AdcClockSource::Pll2PCk => pll2_p_ck.map(Hertz),
AdcClockSource::Pll3RCk => pll3_r_ck.map(Hertz),
AdcClockSource::PerCk => Some(per_ck),
};
// Set timer clocks prescaler setting
RCC.cfgr().modify(|w| w.set_timpre(timpre));
// Select system clock source
let sw = match (sys_use_pll1_p, config.hse.is_some()) {
(true, _) => Sw::PLL1,
(false, true) => Sw::HSE,
_ => Sw::HSI,
};
RCC.cfgr().modify(|w| w.set_sw(sw));
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
// IO compensation cell - Requires CSI clock and SYSCFG
assert!(RCC.cr().read().csirdy());
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
// Enable the compensation cell, using back-bias voltage code
// provide by the cell.
critical_section::with(|_| {
SYSCFG.cccsr().modify(|w| {
w.set_en(true);
w.set_cs(false);
w.set_hslv(false);
})
});
while !SYSCFG.cccsr().read().ready() {}
let core_clocks = CoreClocks {
hclk: Hertz(rcc_hclk),
pclk1: Hertz(rcc_pclk1),
pclk2: Hertz(rcc_pclk2),
pclk3: Hertz(rcc_pclk3),
pclk4: Hertz(rcc_pclk4),
ppre1,
ppre2,
ppre3,
ppre4,
csi_ck: Some(CSI_FREQ),
hsi_ck: Some(HSI_FREQ),
hsi48_ck: Some(HSI48_FREQ),
lsi_ck: Some(LSI_FREQ),
per_ck: Some(per_ck),
hse_ck,
pll1_p_ck: pll1_p_ck.map(Hertz),
pll1_q_ck: pll1_q_ck.map(Hertz),
pll1_r_ck: pll1_r_ck.map(Hertz),
pll2_p_ck: pll2_p_ck.map(Hertz),
pll2_q_ck: pll2_q_ck.map(Hertz),
pll2_r_ck: pll2_r_ck.map(Hertz),
pll3_p_ck: pll3_p_ck.map(Hertz),
pll3_q_ck: pll3_q_ck.map(Hertz),
pll3_r_ck: pll3_r_ck.map(Hertz),
timx_ker_ck: rcc_timerx_ker_ck.map(Hertz),
timy_ker_ck: rcc_timery_ker_ck.map(Hertz),
adc_ker_ck,
sys_ck,
c_ck: Hertz(sys_d1cpre_ck),
};
set_freqs(Clocks {
sys: core_clocks.c_ck,
ahb1: core_clocks.hclk,
ahb2: core_clocks.hclk,
ahb3: core_clocks.hclk,
ahb4: core_clocks.hclk,
apb1: core_clocks.pclk1,
apb2: core_clocks.pclk2,
apb4: core_clocks.pclk4,
apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1),
apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2),
adc: core_clocks.adc_ker_ck,
});
}
mod pll {
use super::{Hertz, RCC};
const VCO_MIN: u32 = 150_000_000;
const VCO_MAX: u32 = 420_000_000;
#[derive(Default)]
pub struct PllConfig {
pub p_ck: Option<Hertz>,
pub q_ck: Option<Hertz>,
pub r_ck: Option<Hertz>,
}
pub(super) struct PllConfigResults {
pub ref_x_ck: u32,
pub pll_x_m: u32,
pub pll_x_p: u32,
pub vco_ck_target: u32,
}
fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) {
let pll_x_p = if plln == 0 {
if output > VCO_MAX / 2 {
1
} else {
((VCO_MAX / output) | 1) - 1 // Must be even or unity
}
} else {
// Specific to PLL2/3, will subtract 1 later
if output > VCO_MAX / 2 {
1
} else {
VCO_MAX / output
}
};
let vco_ck = output * pll_x_p;
assert!(pll_x_p < 128);
assert!(vco_ck >= VCO_MIN);
assert!(vco_ck <= VCO_MAX);
(vco_ck, pll_x_p)
}
/// # Safety
///
/// Must have exclusive access to the RCC register block
fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults {
use crate::pac::rcc::vals::{Pllrge, Pllvcosel};
let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln);
// Input divisor, resulting in a reference clock in the range
// 1 to 2 MHz. Choose the highest reference clock (lowest m)
let pll_x_m = (pll_src + 1_999_999) / 2_000_000;
assert!(pll_x_m < 64);
// Calculate resulting reference clock
let ref_x_ck = pll_src / pll_x_m;
assert!((1_000_000..=2_000_000).contains(&ref_x_ck));
RCC.pllcfgr().modify(|w| {
w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO);
w.set_pllrge(plln, Pllrge::RANGE1);
});
PllConfigResults {
ref_x_ck,
pll_x_m,
pll_x_p,
vco_ck_target,
}
}
/// # Safety
///
/// Must have exclusive access to the RCC register block
pub(super) fn pll_setup(pll_src: u32, config: &PllConfig, plln: usize) -> (Option<u32>, Option<u32>, Option<u32>) {
use crate::pac::rcc::vals::Divp;
match config.p_ck {
Some(requested_output) => {
let config_results = vco_setup(pll_src, requested_output.0, plln);
let PllConfigResults {
ref_x_ck,
pll_x_m,
pll_x_p,
vco_ck_target,
} = config_results;
RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8));
// Feedback divider. Integer only
let pll_x_n = vco_ck_target / ref_x_ck;
assert!(pll_x_n >= 4);
assert!(pll_x_n <= 512);
RCC.plldivr(plln).modify(|w| w.set_divn1((pll_x_n - 1) as u16));
// No FRACN
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
let vco_ck = ref_x_ck * pll_x_n;
RCC.plldivr(plln)
.modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8)));
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
// Calulate additional output dividers
let q_ck = match config.q_ck {
Some(Hertz(ck)) if ck > 0 => {
let div = (vco_ck + ck - 1) / ck;
RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8));
RCC.pllcfgr().modify(|w| w.set_divqen(plln, true));
Some(vco_ck / div)
}
_ => None,
};
let r_ck = match config.r_ck {
Some(Hertz(ck)) if ck > 0 => {
let div = (vco_ck + ck - 1) / ck;
RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8));
RCC.pllcfgr().modify(|w| w.set_divren(plln, true));
Some(vco_ck / div)
}
_ => None,
};
(Some(vco_ck / pll_x_p), q_ck, r_ck)
}
None => {
assert!(
config.q_ck.is_none(),
"Must set PLL P clock for Q clock to take effect!"
);
assert!(
config.r_ck.is_none(),
"Must set PLL P clock for R clock to take effect!"
);
(None, None, None)
}
}
}
}

View File

@ -0,0 +1,71 @@
use core::marker::PhantomData;
use embassy_hal_internal::into_ref;
use crate::gpio::sealed::AFType;
use crate::gpio::Speed;
pub use crate::pac::rcc::vals::{Mco1 as Mco1Source, Mco2 as Mco2Source};
use crate::pac::RCC;
use crate::{peripherals, Peripheral};
pub(crate) mod sealed {
pub trait McoInstance {
type Source;
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
}
}
pub trait McoInstance: sealed::McoInstance + 'static {}
pin_trait!(McoPin, McoInstance);
macro_rules! impl_peri {
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
impl sealed::McoInstance for peripherals::$peri {
type Source = $source;
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
RCC.cfgr().modify(|w| {
w.$set_source(source);
w.$set_prescaler(prescaler);
});
}
}
impl McoInstance for peripherals::$peri {}
};
}
impl_peri!(MCO1, Mco1Source, set_mco1, set_mco1pre);
impl_peri!(MCO2, Mco2Source, set_mco2, set_mco2pre);
pub struct Mco<'d, T: McoInstance> {
phantom: PhantomData<&'d mut T>,
}
impl<'d, T: McoInstance> Mco<'d, T> {
/// Create a new MCO instance.
///
/// `prescaler` must be between 1 and 15.
pub fn new(
_peri: impl Peripheral<P = T> + 'd,
pin: impl Peripheral<P = impl McoPin<T>> + 'd,
source: T::Source,
prescaler: u8,
) -> Self {
into_ref!(pin);
assert!(
1 <= prescaler && prescaler <= 15,
"Mco prescaler must be between 1 and 15. Refer to the reference manual for more information."
);
critical_section::with(|_| unsafe {
T::apply_clock_settings(source, prescaler);
pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
pin.set_speed(Speed::VeryHigh);
});
Self { phantom: PhantomData }
}
}

View File

@ -1,12 +1,17 @@
#![macro_use] #![macro_use]
pub(crate) mod bd;
pub mod bus;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
pub use crate::rcc::bd::RtcClockSource; pub use crate::rcc::bd::RtcClockSource;
use crate::time::Hertz; use crate::time::Hertz;
pub(crate) mod bd;
mod bus;
#[cfg(any(stm32h5, stm32h7))]
mod mco;
#[cfg(any(stm32h5, stm32h7))]
pub use mco::*;
#[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(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")]
@ -16,7 +21,7 @@ use crate::time::Hertz;
#[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_h7, rcc_h7ab), path = "h7.rs")] #[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab), path = "h.rs")]
#[cfg_attr(rcc_l0, path = "l0.rs")] #[cfg_attr(rcc_l0, path = "l0.rs")]
#[cfg_attr(rcc_l1, path = "l1.rs")] #[cfg_attr(rcc_l1, path = "l1.rs")]
#[cfg_attr(rcc_l4, path = "l4.rs")] #[cfg_attr(rcc_l4, path = "l4.rs")]
@ -25,7 +30,6 @@ use crate::time::Hertz;
#[cfg_attr(rcc_wb, path = "wb.rs")] #[cfg_attr(rcc_wb, path = "wb.rs")]
#[cfg_attr(rcc_wba, path = "wba.rs")] #[cfg_attr(rcc_wba, path = "wba.rs")]
#[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")]
#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")]
mod _version; mod _version;
pub use _version::*; pub use _version::*;
#[cfg(feature = "low-power")] #[cfg(feature = "low-power")]
@ -53,7 +57,7 @@ pub struct Clocks {
pub apb2: Hertz, pub apb2: Hertz,
#[cfg(not(any(rcc_c0, rcc_g0)))] #[cfg(not(any(rcc_c0, rcc_g0)))]
pub apb2_tim: Hertz, pub apb2_tim: Hertz,
#[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))] #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5))]
pub apb3: Hertz, pub apb3: Hertz,
#[cfg(any(rcc_h7, rcc_h7ab))] #[cfg(any(rcc_h7, rcc_h7ab))]
pub apb4: Hertz, pub apb4: Hertz,

View File

@ -100,6 +100,7 @@ async fn main(spawner: Spawner) -> ! {
let r = socket.connect(remote_endpoint).await; let r = socket.connect(remote_endpoint).await;
if let Err(e) = r { if let Err(e) = r {
info!("connect error: {:?}", e); info!("connect error: {:?}", e);
Timer::after(Duration::from_secs(1)).await;
continue; continue;
} }
info!("connected!"); info!("connected!");
@ -108,7 +109,7 @@ async fn main(spawner: Spawner) -> ! {
let r = socket.write_all(&buf).await; let r = socket.write_all(&buf).await;
if let Err(e) = r { if let Err(e) = r {
info!("write error: {:?}", e); info!("write error: {:?}", e);
continue; break;
} }
Timer::after(Duration::from_secs(1)).await; Timer::after(Duration::from_secs(1)).await;
} }

View File

@ -101,6 +101,7 @@ async fn main(spawner: Spawner) -> ! {
let r = socket.connect(remote_endpoint).await; let r = socket.connect(remote_endpoint).await;
if let Err(e) = r { if let Err(e) = r {
info!("connect error: {:?}", e); info!("connect error: {:?}", e);
Timer::after(Duration::from_secs(1)).await;
continue; continue;
} }
info!("connected!"); info!("connected!");
@ -109,7 +110,7 @@ async fn main(spawner: Spawner) -> ! {
let r = socket.write_all(&buf).await; let r = socket.write_all(&buf).await;
if let Err(e) = r { if let Err(e) = r {
info!("write error: {:?}", e); info!("write error: {:?}", e);
continue; break;
} }
Timer::after(Duration::from_secs(1)).await; Timer::after(Duration::from_secs(1)).await;
} }

View File

@ -53,7 +53,7 @@ async fn main(spawner: Spawner) -> ! {
config.rcc.apb2_pre = APBPrescaler::DIV1; config.rcc.apb2_pre = APBPrescaler::DIV1;
config.rcc.apb3_pre = APBPrescaler::DIV1; config.rcc.apb3_pre = APBPrescaler::DIV1;
config.rcc.sys = Sysclk::Pll1P; config.rcc.sys = Sysclk::Pll1P;
config.rcc.voltage_scale = VoltageScale::SCALE0; config.rcc.voltage_scale = VoltageScale::Scale0;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");
@ -128,7 +128,7 @@ async fn main(spawner: Spawner) -> ! {
let r = socket.write_all(b"Hello\n").await; let r = socket.write_all(b"Hello\n").await;
if let Err(e) = r { if let Err(e) = r {
info!("write error: {:?}", e); info!("write error: {:?}", e);
continue; break;
} }
Timer::after(Duration::from_secs(1)).await; Timer::after(Duration::from_secs(1)).await;
} }

View File

@ -40,7 +40,7 @@ async fn main(_spawner: Spawner) {
config.rcc.apb2_pre = APBPrescaler::DIV2; config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.apb3_pre = APBPrescaler::DIV4; config.rcc.apb3_pre = APBPrescaler::DIV4;
config.rcc.sys = Sysclk::Pll1P; config.rcc.sys = Sysclk::Pll1P;
config.rcc.voltage_scale = VoltageScale::SCALE0; config.rcc.voltage_scale = VoltageScale::Scale0;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View File

@ -5,8 +5,6 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::adc::{Adc, SampleTime};
use embassy_stm32::rcc::AdcClockSource;
use embassy_stm32::time::mhz;
use embassy_stm32::Config; use embassy_stm32::Config;
use embassy_time::{Delay, Duration, Timer}; use embassy_time::{Delay, Duration, Timer};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -14,10 +12,34 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.per_ck = Some(mhz(64)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.adc_clock_source = AdcClockSource::PerCk; config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(8), // SPI1 cksel defaults to pll1_q
divr: None,
});
config.rcc.pll2 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(8), // 100mhz
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
config.rcc.adc_clock_source = AdcClockSource::PLL2_P;
}
let mut p = embassy_stm32::init(config); let mut p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View File

@ -6,8 +6,8 @@ use embassy_executor::Spawner;
use embassy_stm32::dcmi::{self, *}; use embassy_stm32::dcmi::{self, *};
use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::i2c::I2c; use embassy_stm32::i2c::I2c;
use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; use embassy_stm32::rcc::{Mco, Mco1Source};
use embassy_stm32::time::{khz, mhz}; use embassy_stm32::time::khz;
use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; use embassy_stm32::{bind_interrupts, i2c, peripherals, Config};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use ov7725::*; use ov7725::*;
@ -26,17 +26,30 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(400)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.pclk1 = Some(mhz(100)); config.rcc.csi = true;
config.rcc.pclk2 = Some(mhz(100)); config.rcc.pll_src = PllSource::Hsi;
config.rcc.pclk3 = Some(mhz(100)); config.rcc.pll1 = Some(Pll {
config.rcc.pclk4 = Some(mhz(100)); prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(8), // 100mhz
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
defmt::info!("Hello World!"); defmt::info!("Hello World!");
let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 3);
let mut led = Output::new(p.PE3, Level::High, Speed::Low); let mut led = Output::new(p.PE3, Level::High, Speed::Low);
let cam_i2c = I2c::new( let cam_i2c = I2c::new(

View File

@ -6,7 +6,6 @@ use cortex_m_rt::entry;
use defmt::*; use defmt::*;
use embassy_stm32::dac::{DacCh1, DacChannel, Value}; use embassy_stm32::dac::{DacCh1, DacChannel, Value};
use embassy_stm32::dma::NoDma; use embassy_stm32::dma::NoDma;
use embassy_stm32::time::mhz;
use embassy_stm32::Config; use embassy_stm32::Config;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -15,9 +14,34 @@ fn main() -> ! {
info!("Hello World, dude!"); info!("Hello World, dude!");
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(8), // SPI1 cksel defaults to pll1_q
divr: None,
});
config.rcc.pll2 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(8), // 100mhz
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
config.rcc.adc_clock_source = AdcClockSource::PLL2_P;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);

View File

@ -8,7 +8,7 @@ use embassy_stm32::dac::{DacChannel, ValueArray};
use embassy_stm32::pac::timer::vals::{Mms, Opm}; use embassy_stm32::pac::timer::vals::{Mms, Opm};
use embassy_stm32::peripherals::{TIM6, TIM7}; use embassy_stm32::peripherals::{TIM6, TIM7};
use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::rcc::low_level::RccPeripheral;
use embassy_stm32::time::{mhz, Hertz}; use embassy_stm32::time::Hertz;
use embassy_stm32::timer::low_level::Basic16bitInstance; use embassy_stm32::timer::low_level::Basic16bitInstance;
use micromath::F32Ext; use micromath::F32Ext;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -22,9 +22,34 @@ pub type Dac2Type =
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
let mut config = embassy_stm32::Config::default(); let mut config = embassy_stm32::Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(100)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(8), // SPI1 cksel defaults to pll1_q
divr: None,
});
config.rcc.pll2 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(8), // 100mhz
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
config.rcc.adc_clock_source = AdcClockSource::PLL2_P;
}
// Initialize the board and obtain a Peripherals instance // Initialize the board and obtain a Peripherals instance
let p: embassy_stm32::Peripherals = embassy_stm32::init(config); let p: embassy_stm32::Peripherals = embassy_stm32::init(config);

View File

@ -10,7 +10,6 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::eth::{Ethernet, PacketQueue};
use embassy_stm32::peripherals::ETH; use embassy_stm32::peripherals::ETH;
use embassy_stm32::rng::Rng; use embassy_stm32::rng::Rng;
use embassy_stm32::time::mhz;
use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use embedded_io_async::Write; use embedded_io_async::Write;
@ -33,9 +32,27 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) -> ! { async fn main(spawner: Spawner) -> ! {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for RNG
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");
@ -102,6 +119,7 @@ async fn main(spawner: Spawner) -> ! {
let r = socket.connect(remote_endpoint).await; let r = socket.connect(remote_endpoint).await;
if let Err(e) = r { if let Err(e) = r {
info!("connect error: {:?}", e); info!("connect error: {:?}", e);
Timer::after(Duration::from_secs(1)).await;
continue; continue;
} }
info!("connected!"); info!("connected!");
@ -109,7 +127,7 @@ async fn main(spawner: Spawner) -> ! {
let r = socket.write_all(b"Hello\n").await; let r = socket.write_all(b"Hello\n").await;
if let Err(e) = r { if let Err(e) = r {
info!("write error: {:?}", e); info!("write error: {:?}", e);
continue; break;
} }
Timer::after(Duration::from_secs(1)).await; Timer::after(Duration::from_secs(1)).await;
} }

View File

@ -10,7 +10,6 @@ use embassy_stm32::eth::generic_smi::GenericSMI;
use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::eth::{Ethernet, PacketQueue};
use embassy_stm32::peripherals::ETH; use embassy_stm32::peripherals::ETH;
use embassy_stm32::rng::Rng; use embassy_stm32::rng::Rng;
use embassy_stm32::time::mhz;
use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use embedded_io_async::Write; use embedded_io_async::Write;
@ -34,9 +33,27 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) -> ! { async fn main(spawner: Spawner) -> ! {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for RNG
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");
@ -108,7 +125,7 @@ async fn main(spawner: Spawner) -> ! {
let r = connection.write_all(b"Hello\n").await; let r = connection.write_all(b"Hello\n").await;
if let Err(e) = r { if let Err(e) = r {
info!("write error: {:?}", e); info!("write error: {:?}", e);
continue; break;
} }
Timer::after(Duration::from_secs(1)).await; Timer::after(Duration::from_secs(1)).await;
} }

View File

@ -5,7 +5,6 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::fmc::Fmc; use embassy_stm32::fmc::Fmc;
use embassy_stm32::time::mhz;
use embassy_stm32::Config; use embassy_stm32::Config;
use embassy_time::{Delay, Duration, Timer}; use embassy_time::{Delay, Duration, Timer};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -13,9 +12,26 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(8), // 100mhz
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View File

@ -6,7 +6,7 @@ use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::low_level::AFType;
use embassy_stm32::gpio::Speed; use embassy_stm32::gpio::Speed;
use embassy_stm32::time::{khz, mhz, Hertz}; use embassy_stm32::time::{khz, Hertz};
use embassy_stm32::timer::*; use embassy_stm32::timer::*;
use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
@ -15,13 +15,27 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(400)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.pclk1 = Some(mhz(100)); config.rcc.csi = true;
config.rcc.pclk2 = Some(mhz(100)); config.rcc.hsi48 = true; // needed for RNG
config.rcc.pclk3 = Some(mhz(100)); config.rcc.pll_src = PllSource::Hsi;
config.rcc.pclk4 = Some(mhz(100)); config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(8), // 100 Mhz
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View File

@ -5,7 +5,7 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; use embassy_stm32::rcc::{Mco, Mco1Source};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) {
let mut led = Output::new(p.PB14, Level::High, Speed::Low); let mut led = Output::new(p.PB14, Level::High, Speed::Low);
let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(8)); let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, 8);
loop { loop {
info!("high"); info!("high");

View File

@ -5,7 +5,7 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::gpio::OutputType; use embassy_stm32::gpio::OutputType;
use embassy_stm32::time::{khz, mhz}; use embassy_stm32::time::khz;
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
use embassy_stm32::timer::Channel; use embassy_stm32::timer::Channel;
use embassy_stm32::Config; use embassy_stm32::Config;
@ -15,13 +15,26 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(400)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.pclk1 = Some(mhz(100)); config.rcc.csi = true;
config.rcc.pclk2 = Some(mhz(100)); config.rcc.pll_src = PllSource::Hsi;
config.rcc.pclk3 = Some(mhz(100)); config.rcc.pll1 = Some(Pll {
config.rcc.pclk4 = Some(mhz(100)); prediv: 4,
mul: 50,
divp: Some(2),
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View File

@ -5,7 +5,7 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::rng::Rng; use embassy_stm32::rng::Rng;
use embassy_stm32::{bind_interrupts, peripherals, rng}; use embassy_stm32::{bind_interrupts, peripherals, rng, Config};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs { bind_interrupts!(struct Irqs {
@ -14,7 +14,9 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default()); let mut config = Config::default();
config.rcc.hsi48 = true; // needed for RNG.
let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");
let mut rng = Rng::new(p.RNG, Irqs); let mut rng = Rng::new(p.RNG, Irqs);

View File

@ -16,7 +16,26 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(200)); {
use embassy_stm32::rcc::*;
config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(4), // default clock chosen by SDMMCSEL. 200 Mhz
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View File

@ -38,9 +38,26 @@ fn main() -> ! {
info!("Hello World!"); info!("Hello World!");
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(4), // used by SPI3. 100Mhz.
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let mut spi_config = spi::Config::default(); let mut spi_config = spi::Config::default();

View File

@ -34,9 +34,26 @@ fn main() -> ! {
info!("Hello World!"); info!("Hello World!");
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(4), // used by SPI3. 100Mhz.
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let mut spi_config = spi::Config::default(); let mut spi_config = spi::Config::default();

View File

@ -4,7 +4,6 @@
use defmt::{panic, *}; use defmt::{panic, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::time::mhz;
use embassy_stm32::usb_otg::{Driver, Instance}; use embassy_stm32::usb_otg::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
@ -22,9 +21,27 @@ async fn main(_spawner: Spawner) {
info!("Hello World!"); info!("Hello World!");
let mut config = Config::default(); let mut config = Config::default();
config.rcc.sys_ck = Some(mhz(400)); {
config.rcc.hclk = Some(mhz(200)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(mhz(100)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for USB
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
// Create the driver, from the HAL. // Create the driver, from the HAL.

View File

@ -31,9 +31,32 @@ pub fn config() -> Config {
#[cfg(feature = "stm32h755zi")] #[cfg(feature = "stm32h755zi")]
{ {
config.rcc.sys_ck = Some(Hertz(400_000_000)); use embassy_stm32::rcc::*;
config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); config.rcc.hsi = Some(Hsi::Mhz64);
config.rcc.adc_clock_source = embassy_stm32::rcc::AdcClockSource::PerCk; config.rcc.csi = true;
config.rcc.pll_src = PllSource::Hsi;
config.rcc.pll1 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(2),
divq: Some(8), // SPI1 cksel defaults to pll1_q
divr: None,
});
config.rcc.pll2 = Some(Pll {
prediv: 4,
mul: 50,
divp: Some(8), // 100mhz
divq: None,
divr: None,
});
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz
config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz
config.rcc.voltage_scale = VoltageScale::Scale1;
config.rcc.adc_clock_source = AdcClockSource::PLL2_P;
} }
#[cfg(feature = "stm32u585ai")] #[cfg(feature = "stm32u585ai")]