use core::cmp; #[cfg(any(feature = "stm32f042", feature = "stm32f030", feature = "stm32f070"))] use crate::stm32::{FLASH, RCC}; use cast::u32; use crate::time::Hertz; /// Extension trait that constrains the `RCC` peripheral pub trait RccExt { /// Constrains the `RCC` peripheral so it plays nicely with the other abstractions fn constrain(self) -> Rcc; } #[cfg(any(feature = "stm32f042", feature = "stm32f030", feature = "stm32f070"))] impl RccExt for RCC { fn constrain(self) -> Rcc { Rcc { cfgr: CFGR { hclk: None, pclk: None, sysclk: None, }, } } } /// Constrained RCC peripheral pub struct Rcc { pub cfgr: CFGR, } const HSI: u32 = 8_000_000; // Hz pub struct CFGR { hclk: Option, pclk: Option, sysclk: Option, } #[cfg(any(feature = "stm32f042", feature = "stm32f030", feature = "stm32f070"))] impl CFGR { pub fn hclk(mut self, freq: F) -> Self where F: Into, { self.hclk = Some(freq.into().0); self } pub fn pclk(mut self, freq: F) -> Self where F: Into, { self.pclk = Some(freq.into().0); self } pub fn sysclk(mut self, freq: F) -> Self where F: Into, { self.sysclk = Some(freq.into().0); self } pub fn freeze(self) -> Clocks { let pllmul = (4 * self.sysclk.unwrap_or(HSI) + HSI) / HSI / 2; let pllmul = cmp::min(cmp::max(pllmul, 2), 16); let sysclk = pllmul * HSI / 2; let pllmul_bits = if pllmul == 2 { None } else { Some(pllmul as u8 - 2) }; let hpre_bits = self .hclk .map(|hclk| match sysclk / hclk { 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 = sysclk / (1 << (hpre_bits - 0b0111)); let ppre_bits = self .pclk .map(|pclk| match hclk / pclk { 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(ppre); // adjust flash wait states unsafe { let flash = &*FLASH::ptr(); flash.acr.write(|w| { w.latency().bits(if sysclk <= 24_000_000 { 0b000 } else if sysclk <= 48_000_000 { 0b001 } else { 0b010 }) }) } let rcc = unsafe { &*RCC::ptr() }; if let Some(pllmul_bits) = pllmul_bits { // use PLL as source rcc.cfgr.write(|w| unsafe { w.pllmul().bits(pllmul_bits) }); rcc.cr.write(|w| w.pllon().set_bit()); while rcc.cr.read().pllrdy().bit_is_clear() {} rcc.cfgr.modify(|_, w| unsafe { w.ppre().bits(ppre_bits).hpre().bits(hpre_bits).sw().bits(2) }); } else { // use HSI as source rcc.cfgr .write(|w| unsafe { w.ppre().bits(ppre_bits).hpre().bits(hpre_bits).sw().bits(0) }); } Clocks { hclk: Hertz(hclk), pclk: Hertz(pclk), sysclk: Hertz(sysclk), } } } /// Frozen clock frequencies /// /// The existence of this value indicates that the clock configuration can no longer be changed #[derive(Clone, Copy)] pub struct Clocks { hclk: Hertz, pclk: Hertz, sysclk: Hertz, } impl Clocks { /// Returns the frequency of the AHB pub fn hclk(&self) -> Hertz { self.hclk } /// Returns the frequency of the APB pub fn pclk(&self) -> Hertz { self.pclk } /// Returns the system (core) frequency pub fn sysclk(&self) -> Hertz { self.sysclk } }