use stm32_metapac::flash::vals::Latency; use super::{set_freqs, Clocks}; use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw}; use crate::pac::{FLASH, RCC}; use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(8_000_000); /// Configuration of the clocks /// /// hse takes precedence over hsi48 if both are enabled #[non_exhaustive] #[derive(Default)] pub struct Config { pub hse: Option, pub bypass_hse: bool, pub usb_pll: bool, #[cfg(not(stm32f0x0))] pub hsi48: bool, pub sys_ck: Option, pub hclk: Option, pub pclk: Option, pub ls: super::LsConfig, } pub(crate) unsafe fn init(config: Config) { let sysclk = config.sys_ck.map(|v| v.0).unwrap_or(HSI_FREQ.0); let (src_clk, use_hsi48) = config.hse.map(|v| (v.0, false)).unwrap_or_else(|| { #[cfg(not(stm32f0x0))] if config.hsi48 { return (48_000_000, true); } (HSI_FREQ.0, false) }); let (pllmul_bits, real_sysclk) = if sysclk == src_clk { (None, sysclk) } else { let prediv = if config.hse.is_some() { 1 } else { 2 }; let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2; let pllmul = pllmul.max(2).min(16); let pllmul_bits = pllmul as u8 - 2; let real_sysclk = pllmul * src_clk / prediv; (Some(pllmul_bits), real_sysclk) }; let hpre_bits = config .hclk .map(|hclk| match real_sysclk / hclk.0 { 0 => unreachable!(), 1 => 0b0111, 2 => 0b1000, 3..=5 => 0b1001, 6..=11 => 0b1010, 12..=39 => 0b1011, 40..=95 => 0b1100, 96..=191 => 0b1101, 192..=383 => 0b1110, _ => 0b1111, }) .unwrap_or(0b0111); let hclk = real_sysclk / (1 << (hpre_bits - 0b0111)); let ppre_bits = config .pclk .map(|pclk| match hclk / pclk.0 { 0 => unreachable!(), 1 => 0b011, 2 => 0b100, 3..=5 => 0b101, 6..=11 => 0b110, _ => 0b111, }) .unwrap_or(0b011); let ppre: u8 = 1 << (ppre_bits - 0b011); let pclk = hclk / u32::from(ppre); let timer_mul = if ppre == 1 { 1 } else { 2 }; FLASH.acr().write(|w| { w.set_latency(if real_sysclk <= 24_000_000 { Latency::WS0 } else { Latency::WS1 }); }); match (config.hse.is_some(), use_hsi48) { (true, _) => { RCC.cr().modify(|w| { w.set_csson(true); w.set_hseon(true); w.set_hsebyp(config.bypass_hse); }); while !RCC.cr().read().hserdy() {} if pllmul_bits.is_some() { RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV)) } } // use_hsi48 will always be false for stm32f0x0 #[cfg(not(stm32f0x0))] (false, true) => { RCC.cr2().modify(|w| w.set_hsi48on(true)); while !RCC.cr2().read().hsi48rdy() {} if pllmul_bits.is_some() { RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV)) } } _ => { RCC.cr().modify(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {} if pllmul_bits.is_some() { RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2)) } } } if config.usb_pll { RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLL1_P)); } // TODO: Option to use CRS (Clock Recovery) if let Some(pllmul_bits) = pllmul_bits { RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits))); RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} RCC.cfgr().modify(|w| { w.set_ppre(Ppre::from_bits(ppre_bits)); w.set_hpre(Hpre::from_bits(hpre_bits)); w.set_sw(Sw::PLL1_P) }); } else { RCC.cfgr().modify(|w| { w.set_ppre(Ppre::from_bits(ppre_bits)); w.set_hpre(Hpre::from_bits(hpre_bits)); if config.hse.is_some() { w.set_sw(Sw::HSE); } else if use_hsi48 { #[cfg(not(stm32f0x0))] w.set_sw(Sw::HSI48); } else { w.set_sw(Sw::HSI) } }) } let rtc = config.ls.init(); set_freqs(Clocks { sys: Hertz(real_sysclk), pclk1: Hertz(pclk), pclk2: Hertz(pclk), pclk1_tim: Hertz(pclk * timer_mul), pclk2_tim: Hertz(pclk * timer_mul), hclk1: Hertz(hclk), rtc, }); }