From c501b162fc5a7aa1219689132537471b8826a638 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 25 May 2021 17:09:01 +0200 Subject: [PATCH] Enable clock by default for stm32l0 Modify init function to return a Clock instance defined by a per-chip SystemClock type and use this in macro setup A proof of concept implementation for STM32 L0 chips. This allows using embassy::main macros for STM32 devices that have the clock setup logic. --- embassy-macros/src/chip/stm32.rs | 15 +- embassy-macros/src/lib.rs | 7 + embassy-stm32/src/clock.rs | 9 +- embassy-stm32/src/lib.rs | 13 +- embassy-stm32/src/rcc/h7/mod.rs | 6 +- embassy-stm32/src/rcc/l0/mod.rs | 585 ++++++++++++++++++++++++++++++- embassy-stm32/src/rcc/mod.rs | 4 +- 7 files changed, 614 insertions(+), 25 deletions(-) diff --git a/embassy-macros/src/chip/stm32.rs b/embassy-macros/src/chip/stm32.rs index a1ceadd5..71eb9890 100644 --- a/embassy-macros/src/chip/stm32.rs +++ b/embassy-macros/src/chip/stm32.rs @@ -7,20 +7,17 @@ pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream let embassy_stm32_path = embassy_prefix.append("embassy_stm32").path(); quote!( - use #embassy_stm32_path::{clock::Clock}; + use #embassy_stm32_path::{interrupt, peripherals, clock::Clock, time::Hertz}; - let p = #embassy_stm32_path::init(#config); + let (p, mut c) = #embassy_stm32_path::init(#config); - /* - let mut rtc = #embass::RTC::new(unsafe { ::steal() }, interrupt::take!(TIM2)); - let rtc = unsafe { make_static(&mut rtc) }; - rtc.start(); - let mut alarm = rtc.alarm0(); + let clock = unsafe { make_static(&mut c) }; + clock.start(); - unsafe { #embassy_path::time::set_clock(rtc) }; + let mut alarm = clock.alarm1(); + unsafe { #embassy_path::time::set_clock(clock) }; let alarm = unsafe { make_static(&mut alarm) }; executor.set_alarm(alarm); - */ ) } diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index 3dc295fc..f69c3970 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -350,6 +350,13 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { #[cortex_m_rt::entry] fn main() -> ! { + rtt_init_print!(); + unsafe { + log::set_logger_racy(&LOGGER).unwrap(); + } + + log::set_max_level(log::LevelFilter::Trace); + log::info!("Hello World!"); unsafe fn make_static(t: &mut T) -> &'static mut T { ::core::mem::transmute(t) } diff --git a/embassy-stm32/src/clock.rs b/embassy-stm32/src/clock.rs index 709a2d36..2ae0a3ba 100644 --- a/embassy-stm32/src/clock.rs +++ b/embassy-stm32/src/clock.rs @@ -59,6 +59,8 @@ const ALARM_COUNT: usize = 3; pub struct Clock { _inner: T, irq: T::Interrupt, + /// Clock frequency + frequency: Hertz, /// Number of 2^23 periods elapsed since boot. period: AtomicU32, /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. @@ -66,22 +68,23 @@ pub struct Clock { } impl Clock { - pub fn new(peripheral: T, irq: T::Interrupt) -> Self { + pub fn new(peripheral: T, irq: T::Interrupt, frequency: Hertz) -> Self { Self { _inner: peripheral, irq, + frequency, period: AtomicU32::new(0), alarms: Mutex::new([AlarmState::new(), AlarmState::new(), AlarmState::new()]), } } - pub fn start(&'static self, timer_freq: Hertz) { + pub fn start(&'static self) { let inner = T::inner(); // NOTE(unsafe) Critical section to use the unsafe methods critical_section::with(|_| { unsafe { - inner.prepare(timer_freq); + inner.prepare(self.frequency); } self.irq.set_handler_context(self as *const _ as *mut _); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 250281f8..a1fa2211 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -36,6 +36,7 @@ pub mod time; pub use embassy_macros::interrupt; pub use pac::{interrupt, peripherals, Peripherals}; +pub use rcc::SystemClock; // workaround for svd2rust-generated code using `use crate::generic::*;` pub(crate) use pac::regs::generic; @@ -45,6 +46,12 @@ pub struct Config { rcc: rcc::Config, } +impl Config { + pub fn new(rcc: rcc::Config) -> Self { + Self { rcc } + } +} + impl Default for Config { fn default() -> Self { Self { @@ -54,14 +61,12 @@ impl Default for Config { } /// Initialize embassy. -pub fn init(config: Config) -> Peripherals { +pub fn init(config: Config) -> (Peripherals, SystemClock) { let p = Peripherals::take(); unsafe { dma::init(); pac::init_exti(); - rcc::init(config.rcc); + (p, rcc::init(config.rcc)) } - - p } diff --git a/embassy-stm32/src/rcc/h7/mod.rs b/embassy-stm32/src/rcc/h7/mod.rs index 85e1cd00..9a9c7ef0 100644 --- a/embassy-stm32/src/rcc/h7/mod.rs +++ b/embassy-stm32/src/rcc/h7/mod.rs @@ -528,6 +528,6 @@ impl<'d> Rcc<'d> { } } -pub unsafe fn init(config: Config) { - // TODO -} +// TODO +pub type SystemClock = (); +pub unsafe fn init(_config: Config) -> SystemClock {} diff --git a/embassy-stm32/src/rcc/l0/mod.rs b/embassy-stm32/src/rcc/l0/mod.rs index 000aaa9e..6c89022b 100644 --- a/embassy-stm32/src/rcc/l0/mod.rs +++ b/embassy-stm32/src/rcc/l0/mod.rs @@ -1,11 +1,574 @@ +use crate::clock::Clock; +use crate::interrupt; use crate::pac; -use embassy::util::Steal; -use pac::rcc::{self, vals}; +use crate::pac::peripherals::{self, TIM2}; +use crate::time::Hertz; +use crate::time::U32Ext; +use pac::rcc::vals; +use vals::{Hpre, Lptimen, Msirange, Plldiv, Pllmul, Pllon, Pllsrc, Ppre, Sw}; -#[derive(Default)] -pub struct Config {} +/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, +/// and with the addition of the init function to configure a system clock. -pub unsafe fn init(config: Config) { +/// System clock mux source +#[derive(Clone, Copy)] +pub enum ClockSrc { + MSI(MSIRange), + PLL(PLLSource, PLLMul, PLLDiv), + HSE(Hertz), + HSI16, +} + +/// MSI Clock Range +/// +/// These ranges control the frequency of the MSI. Internally, these ranges map +/// to the `MSIRANGE` bits in the `RCC_ICSCR` register. +#[derive(Clone, Copy)] +pub enum MSIRange { + /// Around 65.536 kHz + Range0, + /// Around 131.072 kHz + Range1, + /// Around 262.144 kHz + Range2, + /// Around 524.288 kHz + Range3, + /// Around 1.048 MHz + Range4, + /// Around 2.097 MHz (reset value) + Range5, + /// Around 4.194 MHz + Range6, +} + +impl Default for MSIRange { + fn default() -> MSIRange { + MSIRange::Range5 + } +} + +/// PLL divider +#[derive(Clone, Copy)] +pub enum PLLDiv { + Div2, + Div3, + Div4, +} + +/// PLL multiplier +#[derive(Clone, Copy)] +pub enum PLLMul { + Mul3, + Mul4, + Mul6, + Mul8, + Mul12, + Mul16, + Mul24, + Mul32, + Mul48, +} + +/// AHB prescaler +#[derive(Clone, Copy)] +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, +} + +/// PLL clock input source +#[derive(Clone, Copy)] +pub enum PLLSource { + HSI16, + HSE(Hertz), +} + +/// HSI speed +pub const HSI_FREQ: u32 = 16_000_000; + +/// Clocks configutation +pub struct Config { + mux: ClockSrc, + ahb_pre: AHBPrescaler, + apb1_pre: APBPrescaler, + apb2_pre: APBPrescaler, +} + +impl Default for Config { + #[inline] + fn default() -> Config { + Config { + mux: ClockSrc::MSI(MSIRange::default()), + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + } + } +} + +impl Config { + #[inline] + pub fn clock_src(mut self, mux: ClockSrc) -> Self { + self.mux = mux; + self + } + + #[inline] + pub fn ahb_pre(mut self, pre: AHBPrescaler) -> Self { + self.ahb_pre = pre; + self + } + + #[inline] + pub fn apb1_pre(mut self, pre: APBPrescaler) -> Self { + self.apb1_pre = pre; + self + } + + #[inline] + pub fn apb2_pre(mut self, pre: APBPrescaler) -> Self { + self.apb2_pre = pre; + self + } + + #[inline] + pub fn hsi16() -> Config { + Config { + mux: ClockSrc::HSI16, + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + } + } + + #[inline] + pub fn msi(range: MSIRange) -> Config { + Config { + mux: ClockSrc::MSI(range), + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + } + } + + #[inline] + pub fn pll(pll_src: PLLSource, pll_mul: PLLMul, pll_div: PLLDiv) -> Config { + Config { + mux: ClockSrc::PLL(pll_src, pll_mul, pll_div), + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + } + } + + #[inline] + pub fn hse(freq: T) -> Config + where + T: Into, + { + Config { + mux: ClockSrc::HSE(freq.into()), + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + } + } +} + +/// RCC peripheral +pub struct Rcc { + clocks: Clocks, + rb: pac::rcc::Rcc, +} + +/* +impl Rcc { + pub fn enable_lse(&mut self, _: &PWR) -> LSE { + self.rb.csr.modify(|_, w| { + // Enable LSE clock + w.lseon().set_bit() + }); + while self.rb.csr.read().lserdy().bit_is_clear() {} + LSE(()) + } +} +impl Rcc { + pub fn enable_hsi48(&mut self, syscfg: &mut SYSCFG, crs: CRS) -> HSI48 { + // Reset CRS peripheral + self.rb.apb1rstr.modify(|_, w| w.crsrst().set_bit()); + self.rb.apb1rstr.modify(|_, w| w.crsrst().clear_bit()); + + // Enable CRS peripheral + self.rb.apb1enr.modify(|_, w| w.crsen().set_bit()); + + // Initialize CRS + crs.cfgr.write(|w| + // Select LSE as synchronization source + unsafe { w.syncsrc().bits(0b01) }); + crs.cr + .modify(|_, w| w.autotrimen().set_bit().cen().set_bit()); + + // Enable VREFINT reference for HSI48 oscillator + syscfg + .syscfg + .cfgr3 + .modify(|_, w| w.enref_hsi48().set_bit().en_vrefint().set_bit()); + + // Select HSI48 as USB clock + self.rb.ccipr.modify(|_, w| w.hsi48msel().set_bit()); + + // Enable dedicated USB clock + self.rb.crrcr.modify(|_, w| w.hsi48on().set_bit()); + while self.rb.crrcr.read().hsi48rdy().bit_is_clear() {} + + HSI48(()) + } +} + +impl Rcc { + /// Configure MCO (Microcontroller Clock Output). + pub fn configure_mco

( + &mut self, + source: MCOSEL_A, + prescaler: MCOPRE_A, + output_pin: P, + ) -> MCOEnabled + where + P: mco::Pin, + { + output_pin.into_mco(); + + self.rb.cfgr.modify(|_, w| { + w.mcosel().variant(source); + w.mcopre().variant(prescaler) + }); + + MCOEnabled(()) + } +} +*/ + +/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration +pub trait RccExt { + unsafe fn freeze(self, config: Config) -> Rcc; +} + +impl RccExt for pac::rcc::Rcc { + // `cfgr` is almost always a constant, so make sure it can be constant-propagated properly by + // marking this function and all `Config` constructors and setters as `#[inline]`. + // This saves ~900 Bytes for the `pwr.rs` example. + #[inline] + unsafe fn freeze(self, cfgr: Config) -> Rcc { + let (sys_clk, sw) = match cfgr.mux { + ClockSrc::MSI(range) => { + // Set MSI range + self.icscr().write(|w| w.set_msirange(range.into())); + + // Enable MSI + self.cr().write(|w| w.set_msion(Pllon::ENABLED)); + while !self.cr().read().msirdy() {} + + let freq = 32_768 * (1 << (range as u8 + 1)); + (freq, Sw::MSI) + } + ClockSrc::HSI16 => { + // Enable HSI16 + self.cr().write(|w| w.set_hsi16on(Pllon::ENABLED)); + while !self.cr().read().hsi16rdyf() {} + + (HSI_FREQ, Sw::HSI16) + } + ClockSrc::HSE(freq) => { + // Enable HSE + self.cr().write(|w| w.set_hseon(Pllon::ENABLED)); + while !self.cr().read().hserdy() {} + + (freq.0, Sw::HSE) + } + ClockSrc::PLL(src, mul, div) => { + let freq = match src { + PLLSource::HSE(freq) => { + // Enable HSE + self.cr().write(|w| w.set_hseon(Pllon::ENABLED)); + while !self.cr().read().hserdy() {} + freq.0 + } + PLLSource::HSI16 => { + // Enable HSI + self.cr().write(|w| w.set_hsi16on(Pllon::ENABLED)); + while !self.cr().read().hsi16rdyf() {} + HSI_FREQ + } + }; + + // Disable PLL + self.cr().modify(|w| w.set_pllon(Pllon::DISABLED)); + while self.cr().read().pllrdy() {} + + let freq = match mul { + PLLMul::Mul3 => freq * 3, + PLLMul::Mul4 => freq * 4, + PLLMul::Mul6 => freq * 6, + PLLMul::Mul8 => freq * 8, + PLLMul::Mul12 => freq * 12, + PLLMul::Mul16 => freq * 16, + PLLMul::Mul24 => freq * 24, + PLLMul::Mul32 => freq * 32, + PLLMul::Mul48 => freq * 48, + }; + + let freq = match div { + PLLDiv::Div2 => freq / 2, + PLLDiv::Div3 => freq / 3, + PLLDiv::Div4 => freq / 4, + }; + assert!(freq <= 32_u32.mhz().0); + + self.cfgr().write(move |w| { + w.set_pllmul(mul.into()); + w.set_plldiv(div.into()); + w.set_pllsrc(src.into()); + }); + + // Enable PLL + self.cr().modify(|w| w.set_pllon(Pllon::ENABLED)); + while !self.cr().read().pllrdy() {} + + (freq, Sw::PLL) + } + }; + + self.cfgr().modify(|w| { + w.set_sw(sw.into()); + w.set_hpre(cfgr.ahb_pre.into()); + w.set_ppre(0, cfgr.apb1_pre.into()); + w.set_ppre(1, cfgr.apb2_pre.into()); + }); + + let ahb_freq: u32 = match cfgr.ahb_pre { + AHBPrescaler::NotDivided => sys_clk, + pre => { + let pre: Hpre = pre.into(); + let pre = 1 << (pre.0 as u32 - 7); + sys_clk / pre + } + }; + + let (apb1_freq, apb1_tim_freq, apb1_pre) = match cfgr.apb1_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq, 1), + pre => { + let pre: Ppre = pre.into(); + let pre: u8 = 1 << (pre.0 - 3); + let freq = ahb_freq / pre as u32; + (freq, freq * 2, pre as u8) + } + }; + + let (apb2_freq, apb2_tim_freq, apb2_pre) = match cfgr.apb2_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq, 1), + pre => { + let pre: Ppre = pre.into(); + let pre: u8 = 1 << (pre.0 - 3); + let freq = ahb_freq / (1 << (pre as u8 - 3)); + (freq, freq * 2, pre as u8) + } + }; + + let clocks = Clocks { + source: cfgr.mux, + sys_clk: sys_clk.hz(), + ahb_clk: ahb_freq.hz(), + apb1_clk: apb1_freq.hz(), + apb2_clk: apb2_freq.hz(), + apb1_tim_clk: apb1_tim_freq.hz(), + apb2_tim_clk: apb2_tim_freq.hz(), + apb1_pre, + apb2_pre, + }; + + Rcc { rb: self, clocks } + } +} + +/// Frozen clock frequencies +/// +/// The existence of this value indicates that the clock configuration can no longer be changed +#[derive(Clone, Copy)] +pub struct Clocks { + source: ClockSrc, + sys_clk: Hertz, + ahb_clk: Hertz, + apb1_clk: Hertz, + apb1_tim_clk: Hertz, + apb2_clk: Hertz, + apb2_tim_clk: Hertz, + apb1_pre: u8, + apb2_pre: u8, +} + +impl Clocks { + /// Returns the clock source + pub fn source(&self) -> &ClockSrc { + &self.source + } + + /// Returns the system (core) frequency + pub fn sys_clk(&self) -> Hertz { + self.sys_clk + } + + /// Returns the frequency of the AHB + pub fn ahb_clk(&self) -> Hertz { + self.ahb_clk + } + + /// Returns the frequency of the APB1 + pub fn apb1_clk(&self) -> Hertz { + self.apb1_clk + } + + /// Returns the frequency of the APB1 timers + pub fn apb1_tim_clk(&self) -> Hertz { + self.apb1_tim_clk + } + + /// Returns the prescaler of the APB1 + pub fn apb1_pre(&self) -> u8 { + self.apb1_pre + } + + /// Returns the frequency of the APB2 + pub fn apb2_clk(&self) -> Hertz { + self.apb2_clk + } + + /// Returns the frequency of the APB2 timers + pub fn apb2_tim_clk(&self) -> Hertz { + self.apb2_tim_clk + } + + /// Returns the prescaler of the APB2 + pub fn apb2_pre(&self) -> u8 { + self.apb2_pre + } +} + +/// Token that exists only, if the HSI48 clock has been enabled +/// +/// You can get an instance of this struct by calling [`Rcc::enable_hsi48`]. +#[derive(Clone, Copy)] +pub struct HSI48(()); + +/// Token that exists only if MCO (Microcontroller Clock Out) has been enabled. +/// +/// You can get an instance of this struct by calling [`Rcc::configure_mco`]. +#[derive(Clone, Copy)] +pub struct MCOEnabled(()); + +/// Token that exists only, if the LSE clock has been enabled +/// +/// You can get an instance of this struct by calling [`Rcc::enable_lse`]. +#[derive(Clone, Copy)] +pub struct LSE(()); + +impl Into for PLLMul { + fn into(self) -> Pllmul { + match self { + PLLMul::Mul3 => Pllmul::MUL3, + PLLMul::Mul4 => Pllmul::MUL4, + PLLMul::Mul6 => Pllmul::MUL6, + PLLMul::Mul8 => Pllmul::MUL8, + PLLMul::Mul12 => Pllmul::MUL12, + PLLMul::Mul16 => Pllmul::MUL16, + PLLMul::Mul24 => Pllmul::MUL24, + PLLMul::Mul32 => Pllmul::MUL32, + PLLMul::Mul48 => Pllmul::MUL48, + } + } +} + +impl Into for PLLDiv { + fn into(self) -> Plldiv { + match self { + PLLDiv::Div2 => Plldiv::DIV2, + PLLDiv::Div3 => Plldiv::DIV3, + PLLDiv::Div4 => Plldiv::DIV4, + } + } +} + +impl Into for PLLSource { + fn into(self) -> Pllsrc { + match self { + PLLSource::HSI16 => Pllsrc::HSI16, + PLLSource::HSE(_) => Pllsrc::HSE, + } + } +} + +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, + } + } +} + +impl Into for MSIRange { + fn into(self) -> Msirange { + match self { + MSIRange::Range0 => Msirange::RANGE0, + MSIRange::Range1 => Msirange::RANGE1, + MSIRange::Range2 => Msirange::RANGE2, + MSIRange::Range3 => Msirange::RANGE3, + MSIRange::Range4 => Msirange::RANGE4, + MSIRange::Range5 => Msirange::RANGE5, + MSIRange::Range6 => Msirange::RANGE6, + } + } +} + +// We use TIM2 as SystemClock +pub type SystemClock = Clock; + +pub unsafe fn init(config: Config) -> SystemClock { let rcc = pac::RCC; let enabled = vals::Iophen::ENABLED; @@ -17,4 +580,16 @@ pub unsafe fn init(config: Config) { w.set_iopeen(enabled); w.set_iophen(enabled); }); + + let r = rcc.freeze(config); + + rcc.apb1enr().modify(|w| w.set_tim2en(Lptimen::ENABLED)); + rcc.apb1rstr().modify(|w| w.set_tim2rst(true)); + rcc.apb1rstr().modify(|w| w.set_tim2rst(false)); + + Clock::new( + ::steal(), + interrupt::take!(TIM2), + r.clocks.apb1_clk(), + ) } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 59938e7b..9b8c6633 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -6,8 +6,10 @@ cfg_if::cfg_if! { mod l0; pub use l0::*; } else { + pub type SystemClock = (); #[derive(Default)] pub struct Config {} - pub fn init(_config: Config) {} + pub unsafe fn init(_config: Config) -> SystemClock { + } } }