Merge pull request #2656 from embassy-rs/rcc-gc0

stm32/rcc: port g0, c0 to new api. Add c0 HSIKER/HSISYS support.
This commit is contained in:
Dario Nieuwenhuis 2024-03-04 00:26:21 +01:00 committed by GitHub
commit 02a0a15976
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 541 additions and 505 deletions

View file

@ -70,7 +70,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0" sdio-host = "0.5.0"
critical-section = "1.1" critical-section = "1.1"
#stm32-metapac = { version = "15" } #stm32-metapac = { version = "15" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e853cf944b150898312984d092d63926970c340d" } stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085" }
vcell = "0.1.3" vcell = "0.1.3"
bxcan = "0.7.0" bxcan = "0.7.0"
nb = "1.0.0" nb = "1.0.0"
@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] }
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e853cf944b150898312984d092d63926970c340d", default-features = false, features = ["metadata"]} stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085", default-features = false, features = ["metadata"]}
[features] [features]

View file

@ -1,25 +1,56 @@
use crate::pac::flash::vals::Latency; use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::Sw; pub use crate::pac::rcc::vals::{
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Ppre as APBPrescaler}; Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Hsikerdiv as HsiKerDiv, Ppre as APBPrescaler, Sw as Sysclk,
};
use crate::pac::{FLASH, RCC}; use crate::pac::{FLASH, RCC};
use crate::time::Hertz; use crate::time::Hertz;
/// HSI speed /// HSI speed
pub const HSI_FREQ: Hertz = Hertz(48_000_000); pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// System clock mux source /// HSE Mode
#[derive(Clone, Copy)] #[derive(Clone, Copy, Eq, PartialEq)]
pub enum Sysclk { pub enum HseMode {
HSE(Hertz), /// crystal/ceramic oscillator (HSEBYP=0)
HSI(HSIPrescaler), Oscillator,
LSI, /// external analog clock (low swing) (HSEBYP=1)
Bypass,
}
/// HSE Configuration
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
/// HSE frequency.
pub freq: Hertz,
/// HSE mode.
pub mode: HseMode,
}
/// HSI Configuration
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hsi {
/// Division factor for HSISYS clock. Default is 4.
pub sys_div: HsiSysDiv,
/// Division factor for HSIKER clock. Default is 3.
pub ker_div: HsiKerDiv,
} }
/// Clocks configutation /// Clocks configutation
#[non_exhaustive]
pub struct Config { pub struct Config {
/// HSI Configuration
pub hsi: Option<Hsi>,
/// HSE Configuration
pub hse: Option<Hse>,
/// System Clock Configuration
pub sys: Sysclk, pub sys: Sysclk,
pub ahb_pre: AHBPrescaler, pub ahb_pre: AHBPrescaler,
pub apb_pre: APBPrescaler, pub apb1_pre: APBPrescaler,
/// Low-Speed Clock Configuration
pub ls: super::LsConfig, pub ls: super::LsConfig,
/// Per-peripheral kernel clock selection muxes /// Per-peripheral kernel clock selection muxes
@ -30,9 +61,14 @@ impl Default for Config {
#[inline] #[inline]
fn default() -> Config { fn default() -> Config {
Config { Config {
sys: Sysclk::HSI(HSIPrescaler::DIV1), hsi: Some(Hsi {
sys_div: HsiSysDiv::DIV4,
ker_div: HsiKerDiv::DIV3,
}),
hse: None,
sys: Sysclk::HSISYS,
ahb_pre: AHBPrescaler::DIV1, ahb_pre: AHBPrescaler::DIV1,
apb_pre: APBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1,
ls: Default::default(), ls: Default::default(),
mux: Default::default(), mux: Default::default(),
} }
@ -40,111 +76,109 @@ impl Default for Config {
} }
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw) = match config.sys { // Configure HSI
Sysclk::HSI(div) => { let (hsi, hsisys, hsiker) = match config.hsi {
// Enable HSI None => {
RCC.cr().write(|w| { RCC.cr().modify(|w| w.set_hsion(false));
w.set_hsidiv(div); (None, None, None)
w.set_hsion(true) }
Some(hsi) => {
RCC.cr().modify(|w| {
w.set_hsidiv(hsi.sys_div);
w.set_hsikerdiv(hsi.ker_div);
w.set_hsion(true);
}); });
while !RCC.cr().read().hsirdy() {} while !RCC.cr().read().hsirdy() {}
(
(HSI_FREQ / div, Sw::HSI) Some(HSI_FREQ),
} Some(HSI_FREQ / hsi.sys_div),
Sysclk::HSE(freq) => { Some(HSI_FREQ / hsi.ker_div),
// Enable HSE )
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
(freq, Sw::HSE)
}
Sysclk::LSI => {
// Enable LSI
RCC.csr2().write(|w| w.set_lsion(true));
while !RCC.csr2().read().lsirdy() {}
(super::LSI_FREQ, Sw::LSI)
} }
}; };
// Configure HSE
let hse = match config.hse {
None => {
RCC.cr().modify(|w| w.set_hseon(false));
None
}
Some(hse) => {
match hse.mode {
HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
}
RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
RCC.cr().modify(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
Some(hse.freq)
}
};
let sys = match config.sys {
Sysclk::HSISYS => unwrap!(hsisys),
Sysclk::HSE => unwrap!(hse),
_ => unreachable!(),
};
assert!(max::SYSCLK.contains(&sys));
// Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.
let hclk = sys / config.ahb_pre;
assert!(max::HCLK.contains(&hclk));
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
assert!(max::PCLK.contains(&pclk1));
let latency = match hclk.0 {
..=24_000_000 => Latency::WS0,
_ => Latency::WS1,
};
// Configure flash read access latency based on voltage scale and frequency
FLASH.acr().modify(|w| {
w.set_latency(latency);
});
// Spin until the effective flash latency is set.
while FLASH.acr().read().latency() != latency {}
// Now that boost mode and flash read access latency are configured, set up SYSCLK
RCC.cfgr().modify(|w| {
w.set_sw(config.sys);
w.set_hpre(config.ahb_pre);
w.set_ppre(config.apb1_pre);
});
let rtc = config.ls.init(); let rtc = config.ls.init();
// Determine the flash latency implied by the target clock speed
// RM0454 § 3.3.4:
let target_flash_latency = if sys_clk <= Hertz(24_000_000) {
Latency::WS0
} else {
Latency::WS1
};
// Increase the number of cycles we wait for flash if the new value is higher
// There's no harm in waiting a little too much before the clock change, but we'll
// crash immediately if we don't wait enough after the clock change
let mut set_flash_latency_after = false;
FLASH.acr().modify(|w| {
// Is the current flash latency less than what we need at the new SYSCLK?
if w.latency().to_bits() <= target_flash_latency.to_bits() {
// We must increase the number of wait states now
w.set_latency(target_flash_latency)
} else {
// We may decrease the number of wait states later
set_flash_latency_after = true;
}
// RM0490 § 3.3.4:
// > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register
// > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the
// > Flash memory.
//
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
w.set_prften(target_flash_latency.to_bits() > 0);
});
if !set_flash_latency_after {
// Spin until the effective flash latency is compatible with the clock change
while FLASH.acr().read().latency() < target_flash_latency {}
}
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
RCC.cfgr().modify(|w| {
w.set_sw(sw);
w.set_hpre(config.ahb_pre);
w.set_ppre(config.apb_pre);
});
// Spin until the SYSCLK changes have taken effect
loop {
let cfgr = RCC.cfgr().read();
if cfgr.sw() == sw && cfgr.hpre() == config.ahb_pre && cfgr.ppre() == config.apb_pre {
break;
}
}
// Set the flash latency to require fewer wait states
if set_flash_latency_after {
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
}
let ahb_freq = sys_clk / config.ahb_pre;
let (apb_freq, apb_tim_freq) = match config.apb_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
config.mux.init(); config.mux.init();
// without this, the ringbuffered uart test fails.
cortex_m::asm::dsb();
set_clocks!( set_clocks!(
hsi: None, sys: Some(sys),
lse: None, hclk1: Some(hclk),
sys: Some(sys_clk), pclk1: Some(pclk1),
hclk1: Some(ahb_freq), pclk1_tim: Some(pclk1_tim),
pclk1: Some(apb_freq), hsi: hsi,
pclk1_tim: Some(apb_tim_freq), hsiker: hsiker,
hse: hse,
rtc: rtc, rtc: rtc,
// TODO
lsi: None,
lse: None,
); );
} }
mod max {
use core::ops::RangeInclusive;
use crate::time::Hertz;
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000);
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(8)..=Hertz(48_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
}

View file

@ -1,7 +1,8 @@
use crate::pac::flash::vals::Latency; use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::{self, Sw}; pub use crate::pac::pwr::vals::Vos as VoltageRange;
pub use crate::pac::rcc::vals::{ pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler, 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}; use crate::pac::{FLASH, PWR, RCC};
use crate::time::Hertz; use crate::time::Hertz;
@ -9,6 +10,7 @@ use crate::time::Hertz;
/// HSI speed /// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000); pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// HSE Mode
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode { pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0) /// crystal/ceramic oscillator (HSEBYP=0)
@ -17,69 +19,71 @@ pub enum HseMode {
Bypass, Bypass,
} }
/// System clock mux source /// HSE Configuration
#[derive(Clone, Copy)]
pub enum Sysclk {
HSE(Hertz, HseMode),
HSI(HSIPrescaler),
PLL(PllConfig),
LSI,
}
/// The PLL configuration.
///
/// * `VCOCLK = source / m * n`
/// * `PLLRCLK = VCOCLK / r`
/// * `PLLQCLK = VCOCLK / q`
/// * `PLLPCLK = VCOCLK / p`
#[derive(Clone, Copy)]
pub struct PllConfig {
/// The source from which the PLL receives a clock signal
pub source: PllSource,
/// The initial divisor of that clock signal
pub m: Pllm,
/// The PLL VCO multiplier, which must be in the range `8..=86`.
pub n: Plln,
/// The final divisor for `PLLRCLK` output which drives the system clock
pub r: Pllr,
/// The divisor for the `PLLQCLK` output, if desired
pub q: Option<Pllq>,
/// The divisor for the `PLLPCLK` output, if desired
pub p: Option<Pllp>,
}
impl Default for PllConfig {
#[inline]
fn default() -> PllConfig {
// HSI / 1 * 8 / 2 = 64 MHz
PllConfig {
source: PllSource::HSI,
m: Pllm::DIV1,
n: Plln::MUL8,
r: Pllr::DIV2,
q: None,
p: None,
}
}
}
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSource { pub struct Hse {
HSI, /// HSE frequency.
HSE(Hertz, HseMode), pub freq: Hertz,
/// HSE mode.
pub mode: HseMode,
}
/// PLL Configuration
///
/// Use this struct to configure the PLL source, input frequency, multiplication factor, and output
/// dividers. Be sure to keep check the datasheet for your specific part for the appropriate
/// frequency ranges for each of these settings.
pub struct Pll {
/// PLL Source clock selection.
pub source: PllSource,
/// PLL pre-divider
pub prediv: PllPreDiv,
/// PLL multiplication factor for VCO
pub mul: PllMul,
/// PLL division factor for P clock (ADC Clock)
pub divp: Option<PllPDiv>,
/// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI)
pub divq: Option<PllQDiv>,
/// PLL division factor for R clock (SYSCLK)
pub divr: Option<PllRDiv>,
} }
/// Clocks configutation /// Clocks configutation
#[non_exhaustive]
pub struct Config { pub struct Config {
/// HSI Enable
pub hsi: bool,
/// HSE Configuration
pub hse: Option<Hse>,
/// System Clock Configuration
pub sys: Sysclk, pub sys: Sysclk,
pub ahb_pre: AHBPrescaler,
pub apb_pre: APBPrescaler, /// HSI48 Configuration
pub low_power_run: bool,
pub ls: super::LsConfig,
#[cfg(crs)] #[cfg(crs)]
pub hsi48: Option<super::Hsi48Config>, pub hsi48: Option<super::Hsi48Config>,
/// PLL Configuration
pub pll: Option<Pll>,
/// If PLL is requested as the main clock source in the `sys` field then the PLL configuration
/// MUST turn on the PLLR output.
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
/// Low-Speed Clock Configuration
pub ls: super::LsConfig,
pub low_power_run: bool,
pub voltage_range: VoltageRange,
/// Per-peripheral kernel clock selection muxes /// Per-peripheral kernel clock selection muxes
pub mux: super::mux::ClockMux, pub mux: super::mux::ClockMux,
} }
@ -88,248 +92,218 @@ impl Default for Config {
#[inline] #[inline]
fn default() -> Config { fn default() -> Config {
Config { Config {
sys: Sysclk::HSI(HSIPrescaler::DIV1), hsi: true,
ahb_pre: AHBPrescaler::DIV1, hse: None,
apb_pre: APBPrescaler::DIV1, sys: Sysclk::HSI,
low_power_run: false,
ls: Default::default(),
#[cfg(crs)] #[cfg(crs)]
hsi48: Some(Default::default()), hsi48: Some(Default::default()),
pll: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
low_power_run: false,
ls: Default::default(),
voltage_range: VoltageRange::RANGE1,
mux: Default::default(), mux: Default::default(),
} }
} }
} }
impl PllConfig { #[derive(Default)]
pub(crate) fn init(self) -> (Hertz, Option<Hertz>, Option<Hertz>) { pub struct PllFreq {
let (src, input_freq) = match self.source { pub pll_p: Option<Hertz>,
PllSource::HSI => (vals::Pllsrc::HSI, HSI_FREQ), pub pll_q: Option<Hertz>,
PllSource::HSE(freq, _) => (vals::Pllsrc::HSE, freq), pub pll_r: Option<Hertz>,
};
let m_freq = input_freq / self.m;
// RM0454 § 5.4.4:
// > Caution: The software must set these bits so that the PLL input frequency after the
// > /M divider is between 2.66 and 16 MHz.
debug_assert!(m_freq.0 >= 2_660_000 && m_freq.0 <= 16_000_000);
let n_freq = m_freq * self.n as u32;
// RM0454 § 5.4.4:
// > Caution: The software must set these bits so that the VCO output frequency is between
// > 64 and 344 MHz.
debug_assert!(n_freq.0 >= 64_000_000 && n_freq.0 <= 344_000_000);
let r_freq = n_freq / self.r;
// RM0454 § 5.4.4:
// > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock.
debug_assert!(r_freq.0 <= 64_000_000);
let q_freq = self.q.map(|q| n_freq / q);
let p_freq = self.p.map(|p| n_freq / p);
// RM0454 § 5.2.3:
// > To modify the PLL configuration, proceed as follows:
// > 1. Disable the PLL by setting PLLON to 0 in Clock control register (RCC_CR).
RCC.cr().modify(|w| w.set_pllon(false));
// > 2. Wait until PLLRDY is cleared. The PLL is now fully stopped.
while RCC.cr().read().pllrdy() {}
// > 3. Change the desired parameter.
// Enable whichever clock source we're using, and wait for it to become ready
match self.source {
PllSource::HSI => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
}
PllSource::HSE(_, mode) => {
RCC.cr().write(|w| {
w.set_hsebyp(mode != HseMode::Oscillator);
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
}
}
// Configure PLLCFGR
RCC.pllcfgr().modify(|w| {
w.set_pllr(self.r);
w.set_pllren(false);
w.set_pllq(self.q.unwrap_or(Pllq::DIV2));
w.set_pllqen(false);
w.set_pllp(self.p.unwrap_or(Pllp::DIV2));
w.set_pllpen(false);
w.set_plln(self.n);
w.set_pllm(self.m);
w.set_pllsrc(src)
});
// > 4. Enable the PLL again by setting PLLON to 1.
RCC.cr().modify(|w| w.set_pllon(true));
// Wait for the PLL to become ready
while !RCC.cr().read().pllrdy() {}
// > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL
// > configuration register (RCC_PLLCFGR).
RCC.pllcfgr().modify(|w| {
// We'll use R for system clock, so enable that unconditionally
w.set_pllren(true);
// We may also use Q or P
w.set_pllqen(self.q.is_some());
w.set_pllpen(self.p.is_some());
});
(r_freq, q_freq, p_freq)
}
} }
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
let mut pll1_q_freq = None; // Configure HSI
let mut pll1_p_freq = None; let hsi = match config.hsi {
false => {
let (sys_clk, sw) = match config.sys { RCC.cr().modify(|w| w.set_hsion(false));
Sysclk::HSI(div) => { None
// Enable HSI }
RCC.cr().write(|w| { true => {
w.set_hsidiv(div); RCC.cr().modify(|w| w.set_hsion(true));
w.set_hsion(true)
});
while !RCC.cr().read().hsirdy() {} while !RCC.cr().read().hsirdy() {}
Some(HSI_FREQ)
(HSI_FREQ / div, Sw::HSI)
} }
Sysclk::HSE(freq, mode) => { };
// Enable HSE
RCC.cr().write(|w| { // Configure HSE
w.set_hseon(true); let hse = match config.hse {
w.set_hsebyp(mode != HseMode::Oscillator); None => {
}); RCC.cr().modify(|w| w.set_hseon(false));
None
}
Some(hse) => {
match hse.mode {
HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
}
RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
RCC.cr().modify(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {} while !RCC.cr().read().hserdy() {}
Some(hse.freq)
(freq, Sw::HSE)
}
Sysclk::PLL(pll) => {
let (r_freq, q_freq, p_freq) = pll.init();
pll1_q_freq = q_freq;
pll1_p_freq = p_freq;
(r_freq, Sw::PLL1_R)
}
Sysclk::LSI => {
// Enable LSI
RCC.csr().write(|w| w.set_lsion(true));
while !RCC.csr().read().lsirdy() {}
(super::LSI_FREQ, Sw::LSI)
} }
}; };
// Determine the flash latency implied by the target clock speed // Configure HSI48 if required
// RM0454 § 3.3.4: #[cfg(crs)]
let target_flash_latency = if sys_clk.0 <= 24_000_000 { let hsi48 = config.hsi48.map(super::init_hsi48);
Latency::WS0
} else if sys_clk.0 <= 48_000_000 { let pll = config
Latency::WS1 .pll
} else { .map(|pll_config| {
Latency::WS2 let src_freq = match pll_config.source {
PllSource::HSI => unwrap!(hsi),
PllSource::HSE => unwrap!(hse),
_ => unreachable!(),
}; };
// Increase the number of cycles we wait for flash if the new value is higher // Disable PLL before configuration
// There's no harm in waiting a little too much before the clock change, but we'll RCC.cr().modify(|w| w.set_pllon(false));
// crash immediately if we don't wait enough after the clock change while RCC.cr().read().pllrdy() {}
let mut set_flash_latency_after = false;
let in_freq = src_freq / pll_config.prediv;
assert!(max::PLL_IN.contains(&in_freq));
let internal_freq = in_freq * pll_config.mul;
assert!(max::PLL_VCO.contains(&internal_freq));
RCC.pllcfgr().write(|w| {
w.set_plln(pll_config.mul);
w.set_pllm(pll_config.prediv);
w.set_pllsrc(pll_config.source.into());
});
let pll_p_freq = pll_config.divp.map(|div_p| {
RCC.pllcfgr().modify(|w| {
w.set_pllp(div_p);
w.set_pllpen(true);
});
let freq = internal_freq / div_p;
assert!(max::PLL_P.contains(&freq));
freq
});
let pll_q_freq = pll_config.divq.map(|div_q| {
RCC.pllcfgr().modify(|w| {
w.set_pllq(div_q);
w.set_pllqen(true);
});
let freq = internal_freq / div_q;
assert!(max::PLL_Q.contains(&freq));
freq
});
let pll_r_freq = pll_config.divr.map(|div_r| {
RCC.pllcfgr().modify(|w| {
w.set_pllr(div_r);
w.set_pllren(true);
});
let freq = internal_freq / div_r;
assert!(max::PLL_R.contains(&freq));
freq
});
// Enable the PLL
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
PllFreq {
pll_p: pll_p_freq,
pll_q: pll_q_freq,
pll_r: pll_r_freq,
}
})
.unwrap_or_default();
let sys = match config.sys {
Sysclk::HSI => unwrap!(hsi),
Sysclk::HSE => unwrap!(hse),
Sysclk::PLL1_R => unwrap!(pll.pll_r),
_ => unreachable!(),
};
assert!(max::SYSCLK.contains(&sys));
// Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.
let hclk = sys / config.ahb_pre;
assert!(max::HCLK.contains(&hclk));
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
assert!(max::PCLK.contains(&pclk1));
let latency = match (config.voltage_range, hclk.0) {
(VoltageRange::RANGE1, ..=24_000_000) => Latency::WS0,
(VoltageRange::RANGE1, ..=48_000_000) => Latency::WS1,
(VoltageRange::RANGE1, _) => Latency::WS2,
(VoltageRange::RANGE2, ..=8_000_000) => Latency::WS0,
(VoltageRange::RANGE2, ..=16_000_000) => Latency::WS1,
(VoltageRange::RANGE2, _) => Latency::WS2,
_ => unreachable!(),
};
// Configure flash read access latency based on voltage scale and frequency (RM0444 3.3.4)
FLASH.acr().modify(|w| { FLASH.acr().modify(|w| {
// Is the current flash latency less than what we need at the new SYSCLK? w.set_latency(latency);
if w.latency().to_bits() <= target_flash_latency.to_bits() {
// We must increase the number of wait states now
w.set_latency(target_flash_latency)
} else {
// We may decrease the number of wait states later
set_flash_latency_after = true;
}
// RM0454 § 3.3.5:
// > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register
// > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the
// > Flash memory.
//
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
w.set_prften(target_flash_latency.to_bits() > 0);
}); });
if !set_flash_latency_after { // Spin until the effective flash latency is set.
// Spin until the effective flash latency is compatible with the clock change while FLASH.acr().read().latency() != latency {}
while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
}
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once // Now that boost mode and flash read access latency are configured, set up SYSCLK
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre, config.apb_pre);
RCC.cfgr().modify(|w| { RCC.cfgr().modify(|w| {
w.set_sw(sw); w.set_sw(config.sys);
w.set_hpre(hpre); w.set_hpre(config.ahb_pre);
w.set_ppre(ppre); w.set_ppre(config.apb1_pre);
}); });
if set_flash_latency_after {
// We can make the flash require fewer wait states
// Spin until the SYSCLK changes have taken effect
loop {
let cfgr = RCC.cfgr().read();
if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre {
break;
}
}
// Set the flash latency to require fewer wait states
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
}
let ahb_freq = sys_clk / config.ahb_pre;
let (apb_freq, apb_tim_freq) = match config.apb_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
if config.low_power_run { if config.low_power_run {
assert!(sys_clk.0 <= 2_000_000); assert!(sys <= Hertz(2_000_000));
PWR.cr1().modify(|w| w.set_lpr(true)); PWR.cr1().modify(|w| w.set_lpr(true));
} }
let rtc = config.ls.init(); let rtc = config.ls.init();
let lse_freq = config.ls.lse.map(|lse| lse.frequency);
let hsi_freq = (sw == Sw::HSI).then_some(HSI_FREQ);
let hsi_div_8_freq = hsi_freq.map(|f| f / 8u32);
let lsi_freq = (sw == Sw::LSI).then_some(super::LSI_FREQ);
let hse_freq = (sw == Sw::HSE).then_some(sys_clk);
#[cfg(crs)]
let hsi48 = config.hsi48.map(super::init_hsi48);
#[cfg(not(crs))]
let hsi48: Option<Hertz> = None;
config.mux.init(); config.mux.init();
set_clocks!( set_clocks!(
sys: Some(sys_clk), sys: Some(sys),
hclk1: Some(ahb_freq), hclk1: Some(hclk),
pclk1: Some(apb_freq), pclk1: Some(pclk1),
pclk1_tim: Some(apb_tim_freq), pclk1_tim: Some(pclk1_tim),
hsi: hsi_freq, pll1_p: pll.pll_p,
pll1_q: pll.pll_q,
pll1_r: pll.pll_r,
hsi: hsi,
hse: hse,
#[cfg(crs)]
hsi48: hsi48, hsi48: hsi48,
hsi_div_8: hsi_div_8_freq,
hse: hse_freq,
lse: lse_freq,
lsi: lsi_freq,
pll1_q: pll1_q_freq,
pll1_p: pll1_p_freq,
rtc: rtc, rtc: rtc,
hsi_div_488: None, hsi_div_8: hsi.map(|h| h / 8u32),
hsi_div_488: hsi.map(|h| h / 488u32),
// TODO
lsi: None,
lse: None,
); );
} }
mod max {
use core::ops::RangeInclusive;
use crate::time::Hertz;
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000);
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(64_000_000);
pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(8)..=Hertz(64_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(64_000_000);
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(2_660_000)..=Hertz(16_000_000);
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000);
pub(crate) const PLL_P: RangeInclusive<Hertz> = Hertz(3_090_000)..=Hertz(122_000_000);
pub(crate) const PLL_Q: RangeInclusive<Hertz> = Hertz(12_000_000)..=Hertz(128_000_000);
pub(crate) const PLL_R: RangeInclusive<Hertz> = Hertz(12_000_000)..=Hertz(64_000_000);
}

View file

@ -1,12 +1,9 @@
use stm32_metapac::flash::vals::Latency; use crate::pac::flash::vals::Latency;
use stm32_metapac::rcc::vals::Sw;
use stm32_metapac::FLASH;
pub use crate::pac::rcc::vals::{ pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
Ppre as APBPrescaler, Sw as Sysclk, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
}; };
use crate::pac::{PWR, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::time::Hertz; use crate::time::Hertz;
/// HSI speed /// HSI speed
@ -37,7 +34,7 @@ pub struct Hse {
/// frequency ranges for each of these settings. /// frequency ranges for each of these settings.
pub struct Pll { pub struct Pll {
/// PLL Source clock selection. /// PLL Source clock selection.
pub source: Pllsrc, pub source: PllSource,
/// PLL pre-divider /// PLL pre-divider
pub prediv: PllPreDiv, pub prediv: PllPreDiv,
@ -73,7 +70,7 @@ pub struct Config {
/// PLL Configuration /// PLL Configuration
pub pll: Option<Pll>, pub pll: Option<Pll>,
/// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration /// If PLL is requested as the main clock source in the `sys` field then the PLL configuration
/// MUST turn on the PLLR output. /// MUST turn on the PLLR output.
pub ahb_pre: AHBPrescaler, pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler, pub apb1_pre: APBPrescaler,
@ -112,6 +109,7 @@ impl Default for Config {
} }
} }
#[derive(Default)]
pub struct PllFreq { pub struct PllFreq {
pub pll_p: Option<Hertz>, pub pll_p: Option<Hertz>,
pub pll_q: Option<Hertz>, pub pll_q: Option<Hertz>,
@ -154,10 +152,12 @@ pub(crate) unsafe fn init(config: Config) {
// Configure HSI48 if required // Configure HSI48 if required
let hsi48 = config.hsi48.map(super::init_hsi48); let hsi48 = config.hsi48.map(super::init_hsi48);
let pll_freq = config.pll.map(|pll_config| { let pll = config
.pll
.map(|pll_config| {
let src_freq = match pll_config.source { let src_freq = match pll_config.source {
Pllsrc::HSI => unwrap!(hsi), PllSource::HSI => unwrap!(hsi),
Pllsrc::HSE => unwrap!(hse), PllSource::HSE => unwrap!(hse),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -183,7 +183,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_pllpen(true); w.set_pllpen(true);
}); });
let freq = internal_freq / div_p; let freq = internal_freq / div_p;
assert!(max::PCLK.contains(&freq)); assert!(max::PLL_P.contains(&freq));
freq freq
}); });
@ -193,7 +193,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_pllqen(true); w.set_pllqen(true);
}); });
let freq = internal_freq / div_q; let freq = internal_freq / div_q;
assert!(max::PCLK.contains(&freq)); assert!(max::PLL_Q.contains(&freq));
freq freq
}); });
@ -203,7 +203,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_pllren(true); w.set_pllren(true);
}); });
let freq = internal_freq / div_r; let freq = internal_freq / div_r;
assert!(max::PCLK.contains(&freq)); assert!(max::PLL_R.contains(&freq));
freq freq
}); });
@ -216,29 +216,27 @@ pub(crate) unsafe fn init(config: Config) {
pll_q: pll_q_freq, pll_q: pll_q_freq,
pll_r: pll_r_freq, pll_r: pll_r_freq,
} }
}); })
.unwrap_or_default();
let (sys_clk, sw) = match config.sys { let sys = match config.sys {
Sysclk::HSI => (HSI_FREQ, Sw::HSI), Sysclk::HSI => unwrap!(hsi),
Sysclk::HSE => (unwrap!(hse), Sw::HSE), Sysclk::HSE => unwrap!(hse),
Sysclk::PLL1_R => { Sysclk::PLL1_R => unwrap!(pll.pll_r),
assert!(pll_freq.is_some());
assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0;
assert!(max::SYSCLK.contains(&Hertz(freq)));
(Hertz(freq), Sw::PLL1_R)
}
_ => unreachable!(), _ => unreachable!(),
}; };
// Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. assert!(max::SYSCLK.contains(&sys));
let hclk = sys_clk / config.ahb_pre;
// Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.
let hclk = sys / config.ahb_pre;
assert!(max::HCLK.contains(&hclk)); assert!(max::HCLK.contains(&hclk));
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);
assert!(max::PCLK.contains(&pclk2));
assert!(max::PCLK.contains(&pclk2));
// Configure Core Boost mode ([RM0440] p234 inverted because setting r1mode to 0 enables boost mode!) // Configure Core Boost mode ([RM0440] p234 inverted because setting r1mode to 0 enables boost mode!)
if config.boost { if config.boost {
// RM0440 p235 // RM0440 p235
@ -253,9 +251,7 @@ pub(crate) unsafe fn init(config: Config) {
// 4. Configure and switch to new frequency // 4. Configure and switch to new frequency
} }
// Configure flash read access latency based on boost mode and frequency (RM0440 p98) let latency = match (config.boost, hclk.0) {
FLASH.acr().modify(|w| {
w.set_latency(match (config.boost, hclk.0) {
(true, ..=34_000_000) => Latency::WS0, (true, ..=34_000_000) => Latency::WS0,
(true, ..=68_000_000) => Latency::WS1, (true, ..=68_000_000) => Latency::WS1,
(true, ..=102_000_000) => Latency::WS2, (true, ..=102_000_000) => Latency::WS2,
@ -267,9 +263,16 @@ pub(crate) unsafe fn init(config: Config) {
(false, ..=90_000_000) => Latency::WS2, (false, ..=90_000_000) => Latency::WS2,
(false, ..=120_000_000) => Latency::WS3, (false, ..=120_000_000) => Latency::WS3,
(false, _) => Latency::WS4, (false, _) => Latency::WS4,
}) };
// Configure flash read access latency based on boost mode and frequency (RM0440 p98)
FLASH.acr().modify(|w| {
w.set_latency(latency);
}); });
// Spin until the effective flash latency is set.
while FLASH.acr().read().latency() != latency {}
if config.boost { if config.boost {
// 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency. // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency.
cortex_m::asm::delay(16); cortex_m::asm::delay(16);
@ -277,17 +280,14 @@ pub(crate) unsafe fn init(config: Config) {
// Now that boost mode and flash read access latency are configured, set up SYSCLK // Now that boost mode and flash read access latency are configured, set up SYSCLK
RCC.cfgr().modify(|w| { RCC.cfgr().modify(|w| {
w.set_sw(sw); w.set_sw(config.sys);
w.set_hpre(config.ahb_pre); w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre); w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre); w.set_ppre2(config.apb2_pre);
}); });
let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre);
let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre);
if config.low_power_run { if config.low_power_run {
assert!(sys_clk <= Hertz(2_000_000)); assert!(sys <= Hertz(2_000_000));
PWR.cr1().modify(|w| w.set_lpr(true)); PWR.cr1().modify(|w| w.set_lpr(true));
} }
@ -296,17 +296,18 @@ pub(crate) unsafe fn init(config: Config) {
config.mux.init(); config.mux.init();
set_clocks!( set_clocks!(
sys: Some(sys_clk), sys: Some(sys),
hclk1: Some(hclk), hclk1: Some(hclk),
hclk2: Some(hclk), hclk2: Some(hclk),
hclk3: Some(hclk), hclk3: Some(hclk),
pclk1: Some(apb1_freq), pclk1: Some(pclk1),
pclk1_tim: Some(apb1_tim_freq), pclk1_tim: Some(pclk1_tim),
pclk2: Some(apb2_freq), pclk2: Some(pclk2),
pclk2_tim: Some(apb2_tim_freq), pclk2_tim: Some(pclk2_tim),
pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p), pll1_p: pll.pll_p,
pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_q), pll1_q: pll.pll_q,
pll1_r: pll_freq.as_ref().and_then(|pll| pll.pll_r), pll1_r: pll.pll_r,
hsi: hsi,
hse: hse, hse: hse,
hsi48: hsi48, hsi48: hsi48,
rtc: rtc, rtc: rtc,
@ -342,4 +343,7 @@ mod max {
/// PLL VCO (internal) Frequency Range (STM32G474 Datasheet p123, Table 46) /// PLL VCO (internal) Frequency Range (STM32G474 Datasheet p123, Table 46)
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000); pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000);
pub(crate) const PLL_P: RangeInclusive<Hertz> = Hertz(2_064_500)..=Hertz(170_000_000);
pub(crate) const PLL_Q: RangeInclusive<Hertz> = Hertz(8_000_000)..=Hertz(170_000_000);
pub(crate) const PLL_R: RangeInclusive<Hertz> = Hertz(8_000_000)..=Hertz(170_000_000);
} }

View file

@ -16,15 +16,16 @@ async fn main(_spawner: Spawner) {
let mut config = PeripheralConfig::default(); let mut config = PeripheralConfig::default();
{ {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = true;
config.rcc.sys = Sysclk::PLL(PllConfig { config.rcc.pll = Some(Pll {
source: PllSource::HSI, source: PllSource::HSI,
m: Pllm::DIV1, prediv: PllPreDiv::DIV1,
n: Plln::MUL16, mul: PllMul::MUL16,
r: Pllr::DIV4, // CPU clock comes from PLLR (HSI (16MHz) / 1 * 16 / 4 = 64MHz) divp: None,
q: Some(Pllq::DIV2), // TIM1 or TIM15 can be sourced from PLLQ (HSI (16MHz) / 1 * 16 / 2 = 128MHz) divq: Some(PllQDiv::DIV2), // 16 / 1 * 16 / 2 = 128 Mhz
p: None, divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz
}); });
config.rcc.sys = Sysclk::PLL1_R;
// configure TIM1 mux to select PLLQ as clock source // configure TIM1 mux to select PLLQ as clock source
// https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf // https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

View file

@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
{ {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
source: Pllsrc::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL85, mul: PllMul::MUL85,
divp: None, divp: None,

View file

@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) {
mode: HseMode::Oscillator, mode: HseMode::Oscillator,
}); });
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
source: Pllsrc::HSE, source: PllSource::HSE,
prediv: PllPreDiv::DIV6, prediv: PllPreDiv::DIV6,
mul: PllMul::MUL85, mul: PllMul::MUL85,
divp: None, divp: None,

View file

@ -3,7 +3,6 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk};
use embassy_stm32::Config; use embassy_stm32::Config;
use embassy_time::Timer; use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -11,10 +10,11 @@ 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();
{
use embassy_stm32::rcc::*;
config.rcc.hsi = true; config.rcc.hsi = true;
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
source: Pllsrc::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
mul: PllMul::MUL85, mul: PllMul::MUL85,
divp: None, divp: None,
@ -22,9 +22,8 @@ async fn main(_spawner: Spawner) {
// Main system clock at 170 MHz // Main system clock at 170 MHz
divr: Some(PllRDiv::DIV2), divr: Some(PllRDiv::DIV2),
}); });
config.rcc.sys = Sysclk::PLL1_R; config.rcc.sys = Sysclk::PLL1_R;
}
let _p = embassy_stm32::init(config); let _p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View file

@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) {
mode: HseMode::Oscillator, mode: HseMode::Oscillator,
}); });
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
source: Pllsrc::HSE, source: PllSource::HSE,
prediv: PllPreDiv::DIV2, prediv: PllPreDiv::DIV2,
mul: PllMul::MUL72, mul: PllMul::MUL72,
divp: None, divp: None,

View file

@ -260,6 +260,30 @@ pub fn config() -> Config {
#[allow(unused_mut)] #[allow(unused_mut)]
let mut config = Config::default(); let mut config = Config::default();
#[cfg(feature = "stm32c031c6")]
{
config.rcc.hsi = Some(Hsi {
sys_div: HsiSysDiv::DIV1, // 48Mhz
ker_div: HsiKerDiv::DIV3, // 16Mhz
});
config.rcc.sys = Sysclk::HSISYS;
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV1;
}
#[cfg(feature = "stm32g071rb")]
{
config.rcc.hsi = true;
config.rcc.pll = Some(Pll {
source: PllSource::HSI,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL16,
divp: None,
divq: None,
divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz
});
config.rcc.sys = Sysclk::PLL1_R;
}
#[cfg(feature = "stm32wb55rg")] #[cfg(feature = "stm32wb55rg")]
{ {
config.rcc = embassy_stm32::rcc::WPAN_DEFAULT; config.rcc = embassy_stm32::rcc::WPAN_DEFAULT;
@ -456,7 +480,7 @@ pub fn config() -> Config {
mode: HseMode::Oscillator, mode: HseMode::Oscillator,
}); });
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
source: Pllsrc::HSE, source: PllSource::HSE,
prediv: PllPreDiv::DIV6, prediv: PllPreDiv::DIV6,
mul: PllMul::MUL85, mul: PllMul::MUL85,
divp: None, divp: None,