use crate::pac::rcc::vals::Pllsrc;
#[cfg(rcc_f030)]
use crate::pac::rcc::vals::HseFreq;
#[cfg(rcc_f072)]
pub use crate::pac::rcc::vals::Pllmul as PllMul;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, HsiFs, Hsidiv, Ppre as APBPrescaler, Sw as Sysclk,
};
use crate::pac::{CONFIGBYTES, FLASH, RCC};
use crate::time::Hertz;
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
Oscillator,
Bypass,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
pub freq: Hertz,
pub mode: HseMode,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hsi {
pub freq: Hertz,
pub mode: HseMode,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSource {
HSE,
HSI,
}
#[derive(Clone, Copy)]
pub struct Pll {
pub src: PllSource,
#[cfg(rcc_f072)]
pub mul: PllMul,
}
#[non_exhaustive]
#[derive(Clone, Copy)]
pub struct Config {
pub hsi: Option<HsiFs>,
pub hsidiv: Hsidiv,
pub hse: Option<Hse>,
pub sys: Sysclk,
pub pll: Option<Pll>,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub mux: super::mux::ClockMux,
}
impl Default for Config {
fn default() -> Self {
Self {
hsi: Some(HsiFs::HSI_8MHZ),
hse: None,
sys: Sysclk::HSI,
hsidiv: Hsidiv::DIV1,
pll: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
mux: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
RCC.cr().modify(|w| w.set_hsion(true));
let hsi_value = if let Some(value) = config.hsi {
let hsi_trimming_bytes = CONFIGBYTES.hsi_trimming(value as usize).read();
assert_eq!(hsi_trimming_bytes.hsi_fs(), value as u8);
RCC.icscr().modify(|w| {
w.set_hsi_fs(value);
w.set_hsi_trim(hsi_trimming_bytes.hsi_trim());
});
match value {
HsiFs::HSI_4MHZ => Some(Hertz(4_000_000)),
HsiFs::HSI_8MHZ => Some(Hertz(8_000_000)),
HsiFs::HSI_16MHZ => Some(Hertz(16_000_000)),
HsiFs::HSI_22_12MHZ => Some(Hertz(22_120_000)),
HsiFs::HSI_24MHZ => Some(Hertz(24_000_000)),
_ => unreachable!(),
}
} else {
None
};
while !RCC.cr().read().hsirdy() {}
RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSI));
while RCC.cfgr().read().sws() != Sysclk::HSI {}
RCC.cr().modify(|w| w.set_hsidiv(config.hsidiv));
let hsi = config.hsi;
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)),
}
#[cfg(rcc_f030)]
RCC.ecscr().modify(|w| {
w.set_hse_freq(match hse.freq.0 {
..=8_000_000 => HseFreq::RANGE1,
..=16_000_000 => HseFreq::RANGE2,
_ => HseFreq::RANGE3,
})
});
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 pll = match config.pll {
None => None,
Some(pll) => {
let (src_val, src_freq) = match pll.src {
PllSource::HSE => (Pllsrc::HSE, unwrap!(hse)),
PllSource::HSI => (Pllsrc::HSI, unwrap!(hsi_value)),
};
#[cfg(rcc_f030)]
let out_freq = src_freq * 2u8;
#[cfg(rcc_f072)]
let out_freq = src_freq * pll.mul;
assert!(max::PLL_IN.contains(&src_freq));
RCC.cr().modify(|w| w.set_pllon(false));
while RCC.cr().read().pllrdy() {}
RCC.pllcfgr().modify(|w| {
#[cfg(rcc_f072)]
w.set_pllmul(pll.mul);
w.set_pllsrc(src_val);
});
RCC.cr().modify(|w| w.set_pllon(true));
cortex_m::asm::delay(1_000);
while !RCC.cr().read().pllrdy() {}
Some(out_freq)
}
};
let sys = match config.sys {
Sysclk::HSI => unwrap!(hsi_value) / config.hsidiv,
Sysclk::HSE => unwrap!(hse),
Sysclk::PLL => unwrap!(pll),
_ => unreachable!(),
};
let hclk1 = sys / config.ahb_pre;
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
let latency: u8 = match hclk1.0 {
..=24_000_000 => 0,
..=48_000_000 => 1,
_ => 2,
};
FLASH.acr().modify(|w| {
#[cfg(rcc_f072)]
w.set_latency(latency);
#[cfg(rcc_f030)]
w.set_latency(latency != 0);
});
RCC.cfgr().modify(|w| {
w.set_ppre(config.apb1_pre);
w.set_hpre(config.ahb_pre);
});
cortex_m::asm::delay(16);
RCC.cfgr().modify(|w| w.set_sw(config.sys));
while RCC.cfgr().read().sws() != config.sys {}
if hsi == None {
RCC.cr().modify(|w| w.set_hsion(false));
}
config.mux.init();
let clocks = crate::rcc::Clocks {
hclk1: Some(hclk1).into(),
pclk1: Some(pclk1).into(),
pclk1_tim: Some(pclk1_tim).into(),
sys: Some(sys).into(),
hsi: hsi_value.into(),
lse: None.into(),
pll: pll.into(),
};
crate::rcc::set_freqs(clocks);
}
mod max {
use core::ops::RangeInclusive;
use crate::time::Hertz;
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(32_000_000);
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(32_000_000);
#[cfg(any(rcc_f030, rcc_f072))]
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(24_000_000);
}