From a608d0deaf7b75fc1c270f74be0891318794074d Mon Sep 17 00:00:00 2001 From: Joonas Javanainen Date: Sun, 27 Mar 2022 18:40:49 +0300 Subject: [PATCH] Add minimal STM32F2 RCC No support for PLL or other clocks than SYSCLK/AHB/APB1/APB2 --- embassy-stm32/src/rcc/f2.rs | 303 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/mod.rs | 5 +- 2 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 embassy-stm32/src/rcc/f2.rs diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs new file mode 100644 index 000000000..bece046f8 --- /dev/null +++ b/embassy-stm32/src/rcc/f2.rs @@ -0,0 +1,303 @@ +use core::ops::Div; + +use crate::pac::flash::vals::Latency; +use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; +use crate::pac::{FLASH, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; + +/// HSI speed +pub const HSI: Hertz = Hertz(16_000_000); + +/// System clock mux source +#[derive(Clone, Copy)] +pub enum ClockSrc { + HSE(Hertz, HSESrc), + HSI, +} + +/// HSE clock source +#[derive(Clone, Copy)] +pub enum HSESrc { + /// Crystal/ceramic resonator + Crystal, + /// External clock source, HSE bypassed + Bypass, +} + +/// AHB prescaler +#[derive(Clone, Copy, PartialEq)] +pub enum AHBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div64, + Div128, + Div256, + Div512, +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: AHBPrescaler) -> Self::Output { + let divisor = match rhs { + AHBPrescaler::NotDivided => 1, + AHBPrescaler::Div2 => 2, + AHBPrescaler::Div4 => 4, + AHBPrescaler::Div8 => 8, + AHBPrescaler::Div16 => 16, + AHBPrescaler::Div64 => 64, + AHBPrescaler::Div128 => 128, + AHBPrescaler::Div256 => 256, + AHBPrescaler::Div512 => 512, + }; + Hertz(self.0 / divisor) + } +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum APBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: APBPrescaler) -> Self::Output { + let divisor = match rhs { + APBPrescaler::NotDivided => 1, + APBPrescaler::Div2 => 2, + APBPrescaler::Div4 => 4, + APBPrescaler::Div8 => 8, + APBPrescaler::Div16 => 16, + }; + Hertz(self.0 / divisor) + } +} + +impl Into for APBPrescaler { + fn into(self) -> Ppre { + match self { + APBPrescaler::NotDivided => Ppre::DIV1, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +impl Into for AHBPrescaler { + fn into(self) -> Hpre { + match self { + AHBPrescaler::NotDivided => Hpre::DIV1, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, + } + } +} + +/// Voltage Range +/// +/// Represents the system supply voltage range +#[derive(Copy, Clone, PartialEq)] +pub enum VoltageRange { + /// 1.8 to 3.6 V + Min1V8, + /// 2.1 to 3.6 V + Min2V1, + /// 2.4 to 3.6 V + Min2V4, + /// 2.7 to 3.6 V + Min2V7, +} + +impl VoltageRange { + const fn wait_states(&self, ahb_freq: Hertz) -> Option { + let ahb_freq = ahb_freq.0; + // Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock + // frequency + match self { + VoltageRange::Min1V8 => { + if ahb_freq <= 16_000_000 { + Some(Latency::WS0) + } else if ahb_freq <= 32_000_000 { + Some(Latency::WS1) + } else if ahb_freq <= 48_000_000 { + Some(Latency::WS2) + } else if ahb_freq <= 64_000_000 { + Some(Latency::WS3) + } else if ahb_freq <= 80_000_000 { + Some(Latency::WS4) + } else if ahb_freq <= 96_000_000 { + Some(Latency::WS5) + } else if ahb_freq <= 112_000_000 { + Some(Latency::WS6) + } else if ahb_freq <= 120_000_000 { + Some(Latency::WS7) + } else { + None + } + } + VoltageRange::Min2V1 => { + if ahb_freq <= 18_000_000 { + Some(Latency::WS0) + } else if ahb_freq <= 36_000_000 { + Some(Latency::WS1) + } else if ahb_freq <= 54_000_000 { + Some(Latency::WS2) + } else if ahb_freq <= 72_000_000 { + Some(Latency::WS3) + } else if ahb_freq <= 90_000_000 { + Some(Latency::WS4) + } else if ahb_freq <= 108_000_000 { + Some(Latency::WS5) + } else if ahb_freq <= 120_000_000 { + Some(Latency::WS6) + } else { + None + } + } + VoltageRange::Min2V4 => { + if ahb_freq <= 24_000_000 { + Some(Latency::WS0) + } else if ahb_freq <= 48_000_000 { + Some(Latency::WS1) + } else if ahb_freq <= 72_000_000 { + Some(Latency::WS2) + } else if ahb_freq <= 96_000_000 { + Some(Latency::WS3) + } else if ahb_freq <= 120_000_000 { + Some(Latency::WS4) + } else { + None + } + } + VoltageRange::Min2V7 => { + if ahb_freq <= 30_000_000 { + Some(Latency::WS0) + } else if ahb_freq <= 60_000_000 { + Some(Latency::WS1) + } else if ahb_freq <= 90_000_000 { + Some(Latency::WS2) + } else if ahb_freq <= 120_000_000 { + Some(Latency::WS3) + } else { + None + } + } + } + } +} + +/// Clocks configuration +pub struct Config { + pub mux: ClockSrc, + pub voltage: VoltageRange, + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, +} + +impl Default for Config { + #[inline] + fn default() -> Config { + Config { + voltage: VoltageRange::Min1V8, + mux: ClockSrc::HSI, + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + } + } +} + +#[inline] +unsafe fn enable_hse(source: HSESrc) { + RCC.cr().write(|w| { + w.set_hsebyp(match source { + HSESrc::Bypass => true, + HSESrc::Crystal => false, + }); + w.set_hseon(true) + }); + while !RCC.cr().read().hserdy() {} +} + +pub(crate) unsafe fn init(config: Config) { + let (sys_clk, sw) = match config.mux { + ClockSrc::HSI => { + // Enable HSI + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + (HSI, Sw::HSI) + } + ClockSrc::HSE(freq, source) => { + enable_hse(source); + (freq, Sw::HSE) + } + }; + // RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL + // max output to be 120 MHz, so there's no way to get higher frequencies + assert!(sys_clk <= Hertz(120_000_000)); + + let ahb_freq = sys_clk / config.ahb_pre; + // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions + assert!(ahb_freq <= Hertz(120_000_000)); + + let flash_ws = config.voltage.wait_states(ahb_freq).expect("Invalid HCLK"); + FLASH.acr().modify(|w| w.set_latency(flash_ws)); + + RCC.cfgr().modify(|w| { + w.set_sw(sw.into()); + w.set_hpre(config.ahb_pre.into()); + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + + let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + pre => { + let freq = ahb_freq / pre; + (freq, Hertz(freq.0 * 2)) + } + }; + // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions + assert!(apb1_freq <= Hertz(30_000_000)); + + let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + pre => { + let freq = ahb_freq / pre; + (freq, Hertz(freq.0 * 2)) + } + }; + // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions + assert!(apb2_freq <= Hertz(60_000_000)); + + set_freqs(Clocks { + sys: sys_clk, + ahb1: ahb_freq, + ahb2: ahb_freq, + ahb3: ahb_freq, + apb1: apb1_freq, + apb1_tim: apb1_tim_freq, + apb2: apb2_freq, + apb2_tim: apb2_tim_freq, + }); +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 01c66f76f..3090b416c 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -5,6 +5,7 @@ use core::mem::MaybeUninit; #[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(rcc_f1, path = "f1.rs")] +#[cfg_attr(rcc_f2, path = "f2.rs")] #[cfg_attr(rcc_f3, path = "f3.rs")] #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] #[cfg_attr(rcc_f7, path = "f7.rs")] @@ -39,11 +40,11 @@ pub struct Clocks { // AHB pub ahb1: Hertz, #[cfg(any( - rcc_l4, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5 + rcc_l4, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5 ))] pub ahb2: Hertz, #[cfg(any( - rcc_l4, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5 + rcc_l4, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5 ))] pub ahb3: Hertz, #[cfg(any(rcc_h7, rcc_h7ab))]