use crate::pac; use crate::peripherals::{self, RCC}; use crate::rcc::{get_freqs, set_freqs, Clocks}; use crate::time::Hertz; use crate::time::U32Ext; use core::marker::PhantomData; use embassy::util::Unborrow; use embassy_hal_common::unborrow; /// HSI speed pub const HSI_FREQ: u32 = 16_000_000; /// LSI speed pub const LSI_FREQ: u32 = 32_000; /// System clock mux source #[derive(Clone, Copy)] pub enum ClockSrc { HSE(Hertz), HSI16(HSI16Prescaler), LSI, } #[derive(Clone, Copy)] pub enum HSI16Prescaler { NotDivided, Div2, Div4, Div8, Div16, Div32, Div64, Div128, } impl Into for HSI16Prescaler { fn into(self) -> u8 { match self { HSI16Prescaler::NotDivided => 0x00, HSI16Prescaler::Div2 => 0x01, HSI16Prescaler::Div4 => 0x02, HSI16Prescaler::Div8 => 0x03, HSI16Prescaler::Div16 => 0x04, HSI16Prescaler::Div32 => 0x05, HSI16Prescaler::Div64 => 0x06, HSI16Prescaler::Div128 => 0x07, } } } /// AHB prescaler #[derive(Clone, Copy, PartialEq)] pub enum AHBPrescaler { NotDivided, Div2, Div4, Div8, Div16, Div64, Div128, Div256, Div512, } /// APB prescaler #[derive(Clone, Copy)] pub enum APBPrescaler { NotDivided, Div2, Div4, Div8, Div16, } impl Into for APBPrescaler { fn into(self) -> u8 { match self { APBPrescaler::NotDivided => 1, APBPrescaler::Div2 => 0x04, APBPrescaler::Div4 => 0x05, APBPrescaler::Div8 => 0x06, APBPrescaler::Div16 => 0x07, } } } impl Into for AHBPrescaler { fn into(self) -> u8 { match self { AHBPrescaler::NotDivided => 1, AHBPrescaler::Div2 => 0x08, AHBPrescaler::Div4 => 0x09, AHBPrescaler::Div8 => 0x0a, AHBPrescaler::Div16 => 0x0b, AHBPrescaler::Div64 => 0x0c, AHBPrescaler::Div128 => 0x0d, AHBPrescaler::Div256 => 0x0e, AHBPrescaler::Div512 => 0x0f, } } } /// Clocks configutation pub struct Config { pub mux: ClockSrc, pub ahb_pre: AHBPrescaler, pub apb_pre: APBPrescaler, pub low_power_run: bool, } impl Default for Config { #[inline] fn default() -> Config { Config { mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided), ahb_pre: AHBPrescaler::NotDivided, apb_pre: APBPrescaler::NotDivided, low_power_run: false, } } } /// RCC peripheral pub struct Rcc<'d> { _rb: peripherals::RCC, phantom: PhantomData<&'d mut peripherals::RCC>, } impl<'d> Rcc<'d> { pub fn new(rcc: impl Unborrow + 'd) -> Self { unborrow!(rcc); Self { _rb: rcc, phantom: PhantomData, } } // Safety: RCC init must have been called pub fn clocks(&self) -> &'static Clocks { unsafe { get_freqs() } } } /// Extension trait that freezes the `RCC` peripheral with provided clocks configuration pub trait RccExt { fn freeze(self, config: Config) -> Clocks; } impl RccExt for RCC { #[inline] fn freeze(self, cfgr: Config) -> Clocks { let rcc = pac::RCC; let (sys_clk, sw) = match cfgr.mux { ClockSrc::HSI16(div) => { // Enable HSI16 let div: u8 = div.into(); unsafe { rcc.cr().write(|w| { w.set_hsidiv(div); w.set_hsion(true) }); while !rcc.cr().read().hsirdy() {} } (HSI_FREQ >> div, 0x00) } ClockSrc::HSE(freq) => { // Enable HSE unsafe { rcc.cr().write(|w| w.set_hseon(true)); while !rcc.cr().read().hserdy() {} } (freq.0, 0x01) } ClockSrc::LSI => { // Enable LSI unsafe { rcc.csr().write(|w| w.set_lsion(true)); while !rcc.csr().read().lsirdy() {} } (LSI_FREQ, 0x03) } }; unsafe { rcc.cfgr().modify(|w| { w.set_sw(sw.into()); w.set_hpre(cfgr.ahb_pre.into()); w.set_ppre(cfgr.apb_pre.into()); }); } let ahb_freq: u32 = match cfgr.ahb_pre { AHBPrescaler::NotDivided => sys_clk, pre => { let pre: u8 = pre.into(); let pre = 1 << (pre as u32 - 7); sys_clk / pre } }; let (apb_freq, apb_tim_freq) = match cfgr.apb_pre { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; let pwr = pac::PWR; if cfgr.low_power_run { assert!(sys_clk.hz() <= 2_000_000.hz()); unsafe { pwr.cr1().modify(|w| w.set_lpr(true)); } } Clocks { sys: sys_clk.hz(), ahb: ahb_freq.hz(), apb: apb_freq.hz(), apb_tim: apb_tim_freq.hz(), } } } pub(crate) unsafe fn init(config: Config) { let r = ::steal(); let clocks = r.freeze(config); set_freqs(clocks); }