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"
critical-section = "1.1"
#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"
bxcan = "0.7.0"
nb = "1.0.0"
@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] }
proc-macro2 = "1.0.36"
quote = "1.0.15"
#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]

View file

@ -1,25 +1,56 @@
use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::Sw;
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Ppre as APBPrescaler};
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Hsikerdiv as HsiKerDiv, Ppre as APBPrescaler, Sw as Sysclk,
};
use crate::pac::{FLASH, RCC};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(48_000_000);
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// System clock mux source
#[derive(Clone, Copy)]
pub enum Sysclk {
HSE(Hertz),
HSI(HSIPrescaler),
LSI,
/// HSE Mode
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0)
Oscillator,
/// 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
#[non_exhaustive]
pub struct Config {
/// HSI Configuration
pub hsi: Option<Hsi>,
/// HSE Configuration
pub hse: Option<Hse>,
/// System Clock Configuration
pub sys: Sysclk,
pub ahb_pre: AHBPrescaler,
pub apb_pre: APBPrescaler,
pub apb1_pre: APBPrescaler,
/// Low-Speed Clock Configuration
pub ls: super::LsConfig,
/// Per-peripheral kernel clock selection muxes
@ -30,9 +61,14 @@ impl Default for Config {
#[inline]
fn default() -> 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,
apb_pre: APBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
ls: Default::default(),
mux: Default::default(),
}
@ -40,111 +76,109 @@ impl Default for Config {
}
pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw) = match config.sys {
Sysclk::HSI(div) => {
// Enable HSI
RCC.cr().write(|w| {
w.set_hsidiv(div);
w.set_hsion(true)
// Configure HSI
let (hsi, hsisys, hsiker) = match config.hsi {
None => {
RCC.cr().modify(|w| w.set_hsion(false));
(None, None, None)
}
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() {}
(HSI_FREQ / div, Sw::HSI)
}
Sysclk::HSE(freq) => {
// 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)
(
Some(HSI_FREQ),
Some(HSI_FREQ / hsi.sys_div),
Some(HSI_FREQ / hsi.ker_div),
)
}
};
// 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();
// 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();
// without this, the ringbuffered uart test fails.
cortex_m::asm::dsb();
set_clocks!(
hsi: None,
lse: None,
sys: Some(sys_clk),
hclk1: Some(ahb_freq),
pclk1: Some(apb_freq),
pclk1_tim: Some(apb_tim_freq),
sys: Some(sys),
hclk1: Some(hclk),
pclk1: Some(pclk1),
pclk1_tim: Some(pclk1_tim),
hsi: hsi,
hsiker: hsiker,
hse: hse,
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::rcc::vals::{self, Sw};
pub use crate::pac::pwr::vals::Vos as VoltageRange;
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::time::Hertz;
@ -9,6 +10,7 @@ use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// HSE Mode
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0)
@ -17,69 +19,71 @@ pub enum HseMode {
Bypass,
}
/// System clock mux source
#[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,
}
}
}
/// HSE Configuration
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSource {
HSI,
HSE(Hertz, HseMode),
pub struct Hse {
/// HSE frequency.
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
#[non_exhaustive]
pub struct Config {
/// HSI Enable
pub hsi: bool,
/// HSE Configuration
pub hse: Option<Hse>,
/// System Clock Configuration
pub sys: Sysclk,
pub ahb_pre: AHBPrescaler,
pub apb_pre: APBPrescaler,
pub low_power_run: bool,
pub ls: super::LsConfig,
/// HSI48 Configuration
#[cfg(crs)]
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
pub mux: super::mux::ClockMux,
}
@ -88,248 +92,218 @@ impl Default for Config {
#[inline]
fn default() -> Config {
Config {
sys: Sysclk::HSI(HSIPrescaler::DIV1),
ahb_pre: AHBPrescaler::DIV1,
apb_pre: APBPrescaler::DIV1,
low_power_run: false,
ls: Default::default(),
hsi: true,
hse: None,
sys: Sysclk::HSI,
#[cfg(crs)]
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(),
}
}
}
impl PllConfig {
pub(crate) fn init(self) -> (Hertz, Option<Hertz>, Option<Hertz>) {
let (src, input_freq) = match self.source {
PllSource::HSI => (vals::Pllsrc::HSI, HSI_FREQ),
PllSource::HSE(freq, _) => (vals::Pllsrc::HSE, freq),
};
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)
}
#[derive(Default)]
pub struct PllFreq {
pub pll_p: Option<Hertz>,
pub pll_q: Option<Hertz>,
pub pll_r: Option<Hertz>,
}
pub(crate) unsafe fn init(config: Config) {
let mut pll1_q_freq = None;
let mut pll1_p_freq = None;
let (sys_clk, sw) = match config.sys {
Sysclk::HSI(div) => {
// Enable HSI
RCC.cr().write(|w| {
w.set_hsidiv(div);
w.set_hsion(true)
});
// Configure HSI
let hsi = match config.hsi {
false => {
RCC.cr().modify(|w| w.set_hsion(false));
None
}
true => {
RCC.cr().modify(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
(HSI_FREQ / div, Sw::HSI)
Some(HSI_FREQ)
}
Sysclk::HSE(freq, mode) => {
// Enable HSE
RCC.cr().write(|w| {
w.set_hseon(true);
w.set_hsebyp(mode != HseMode::Oscillator);
});
};
// 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() {}
(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)
Some(hse.freq)
}
};
// Determine the flash latency implied by the target clock speed
// RM0454 § 3.3.4:
let target_flash_latency = if sys_clk.0 <= 24_000_000 {
Latency::WS0
} else if sys_clk.0 <= 48_000_000 {
Latency::WS1
} else {
Latency::WS2
// Configure HSI48 if required
#[cfg(crs)]
let hsi48 = config.hsi48.map(super::init_hsi48);
let pll = config
.pll
.map(|pll_config| {
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
// 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;
// Disable PLL before configuration
RCC.cr().modify(|w| w.set_pllon(false));
while RCC.cr().read().pllrdy() {}
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| {
// 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;
}
// 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);
w.set_latency(latency);
});
if !set_flash_latency_after {
// Spin until the effective flash latency is compatible with the clock change
while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
}
// Spin until the effective flash latency is set.
while FLASH.acr().read().latency() != latency {}
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre, config.apb_pre);
// Now that boost mode and flash read access latency are configured, set up SYSCLK
RCC.cfgr().modify(|w| {
w.set_sw(sw);
w.set_hpre(hpre);
w.set_ppre(ppre);
w.set_sw(config.sys);
w.set_hpre(config.ahb_pre);
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 {
assert!(sys_clk.0 <= 2_000_000);
assert!(sys <= Hertz(2_000_000));
PWR.cr1().modify(|w| w.set_lpr(true));
}
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();
set_clocks!(
sys: Some(sys_clk),
hclk1: Some(ahb_freq),
pclk1: Some(apb_freq),
pclk1_tim: Some(apb_tim_freq),
hsi: hsi_freq,
sys: Some(sys),
hclk1: Some(hclk),
pclk1: Some(pclk1),
pclk1_tim: Some(pclk1_tim),
pll1_p: pll.pll_p,
pll1_q: pll.pll_q,
pll1_r: pll.pll_r,
hsi: hsi,
hse: hse,
#[cfg(crs)]
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,
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 stm32_metapac::rcc::vals::Sw;
use stm32_metapac::FLASH;
use crate::pac::flash::vals::Latency;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc,
Ppre as APBPrescaler, Sw as Sysclk,
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::{PWR, RCC};
use crate::pac::{FLASH, PWR, RCC};
use crate::time::Hertz;
/// HSI speed
@ -37,7 +34,7 @@ pub struct Hse {
/// frequency ranges for each of these settings.
pub struct Pll {
/// PLL Source clock selection.
pub source: Pllsrc,
pub source: PllSource,
/// PLL pre-divider
pub prediv: PllPreDiv,
@ -73,7 +70,7 @@ pub struct Config {
/// PLL Configuration
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.
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
@ -112,6 +109,7 @@ impl Default for Config {
}
}
#[derive(Default)]
pub struct PllFreq {
pub pll_p: Option<Hertz>,
pub pll_q: Option<Hertz>,
@ -154,10 +152,12 @@ pub(crate) unsafe fn init(config: Config) {
// Configure HSI48 if required
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 {
Pllsrc::HSI => unwrap!(hsi),
Pllsrc::HSE => unwrap!(hse),
PllSource::HSI => unwrap!(hsi),
PllSource::HSE => unwrap!(hse),
_ => unreachable!(),
};
@ -183,7 +183,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_pllpen(true);
});
let freq = internal_freq / div_p;
assert!(max::PCLK.contains(&freq));
assert!(max::PLL_P.contains(&freq));
freq
});
@ -193,7 +193,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_pllqen(true);
});
let freq = internal_freq / div_q;
assert!(max::PCLK.contains(&freq));
assert!(max::PLL_Q.contains(&freq));
freq
});
@ -203,7 +203,7 @@ pub(crate) unsafe fn init(config: Config) {
w.set_pllren(true);
});
let freq = internal_freq / div_r;
assert!(max::PCLK.contains(&freq));
assert!(max::PLL_R.contains(&freq));
freq
});
@ -216,29 +216,27 @@ pub(crate) unsafe fn init(config: Config) {
pll_q: pll_q_freq,
pll_r: pll_r_freq,
}
});
})
.unwrap_or_default();
let (sys_clk, sw) = match config.sys {
Sysclk::HSI => (HSI_FREQ, Sw::HSI),
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
Sysclk::PLL1_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)
}
let sys = match config.sys {
Sysclk::HSI => unwrap!(hsi),
Sysclk::HSE => unwrap!(hse),
Sysclk::PLL1_R => unwrap!(pll.pll_r),
_ => unreachable!(),
};
// Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.
let hclk = sys_clk / config.ahb_pre;
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);
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!)
if config.boost {
// RM0440 p235
@ -253,9 +251,7 @@ pub(crate) unsafe fn init(config: Config) {
// 4. Configure and switch to new frequency
}
// Configure flash read access latency based on boost mode and frequency (RM0440 p98)
FLASH.acr().modify(|w| {
w.set_latency(match (config.boost, hclk.0) {
let latency = match (config.boost, hclk.0) {
(true, ..=34_000_000) => Latency::WS0,
(true, ..=68_000_000) => Latency::WS1,
(true, ..=102_000_000) => Latency::WS2,
@ -267,9 +263,16 @@ pub(crate) unsafe fn init(config: Config) {
(false, ..=90_000_000) => Latency::WS2,
(false, ..=120_000_000) => Latency::WS3,
(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 {
// 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency.
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
RCC.cfgr().modify(|w| {
w.set_sw(sw);
w.set_sw(config.sys);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_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 {
assert!(sys_clk <= Hertz(2_000_000));
assert!(sys <= Hertz(2_000_000));
PWR.cr1().modify(|w| w.set_lpr(true));
}
@ -296,17 +296,18 @@ pub(crate) unsafe fn init(config: Config) {
config.mux.init();
set_clocks!(
sys: Some(sys_clk),
sys: Some(sys),
hclk1: Some(hclk),
hclk2: Some(hclk),
hclk3: Some(hclk),
pclk1: Some(apb1_freq),
pclk1_tim: Some(apb1_tim_freq),
pclk2: Some(apb2_freq),
pclk2_tim: Some(apb2_tim_freq),
pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p),
pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_q),
pll1_r: pll_freq.as_ref().and_then(|pll| pll.pll_r),
pclk1: Some(pclk1),
pclk1_tim: Some(pclk1_tim),
pclk2: Some(pclk2),
pclk2_tim: Some(pclk2_tim),
pll1_p: pll.pll_p,
pll1_q: pll.pll_q,
pll1_r: pll.pll_r,
hsi: hsi,
hse: hse,
hsi48: hsi48,
rtc: rtc,
@ -342,4 +343,7 @@ mod max {
/// 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_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();
{
use embassy_stm32::rcc::*;
config.rcc.sys = Sysclk::PLL(PllConfig {
config.rcc.hsi = true;
config.rcc.pll = Some(Pll {
source: PllSource::HSI,
m: Pllm::DIV1,
n: Plln::MUL16,
r: Pllr::DIV4, // CPU clock comes from PLLR (HSI (16MHz) / 1 * 16 / 4 = 64MHz)
q: Some(Pllq::DIV2), // TIM1 or TIM15 can be sourced from PLLQ (HSI (16MHz) / 1 * 16 / 2 = 128MHz)
p: None,
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL16,
divp: None,
divq: Some(PllQDiv::DIV2), // 16 / 1 * 16 / 2 = 128 Mhz
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
// 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::*;
config.rcc.pll = Some(Pll {
source: Pllsrc::HSI,
source: PllSource::HSI,
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL85,
divp: None,

View file

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

View file

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

View file

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

View file

@ -260,6 +260,30 @@ pub fn config() -> Config {
#[allow(unused_mut)]
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")]
{
config.rcc = embassy_stm32::rcc::WPAN_DEFAULT;
@ -456,7 +480,7 @@ pub fn config() -> Config {
mode: HseMode::Oscillator,
});
config.rcc.pll = Some(Pll {
source: Pllsrc::HSE,
source: PllSource::HSE,
prediv: PllPreDiv::DIV6,
mul: PllMul::MUL85,
divp: None,