From 50ff63ab888352b6818898617588cc844250c9b7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 8 Apr 2022 02:57:48 +0200 Subject: [PATCH] Add STM32L5 support. --- ci.sh | 1 + embassy-stm32/Cargo.toml | 17 ++ embassy-stm32/src/adc/v3.rs | 5 +- embassy-stm32/src/dac/v2.rs | 2 +- embassy-stm32/src/rcc/l5.rs | 468 ++++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/mod.rs | 6 +- stm32-data | 2 +- stm32-gen-features/src/lib.rs | 1 + 8 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 embassy-stm32/src/rcc/l5.rs diff --git a/ci.sh b/ci.sh index deb64870..af93d0b7 100755 --- a/ci.sh +++ b/ci.sh @@ -54,6 +54,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 991558ac..5579631a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -1276,6 +1276,23 @@ stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ] stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] +stm32l552cc = [ "stm32-metapac/stm32l552cc" ] +stm32l552ce = [ "stm32-metapac/stm32l552ce" ] +stm32l552me = [ "stm32-metapac/stm32l552me" ] +stm32l552qc = [ "stm32-metapac/stm32l552qc" ] +stm32l552qe = [ "stm32-metapac/stm32l552qe" ] +stm32l552rc = [ "stm32-metapac/stm32l552rc" ] +stm32l552re = [ "stm32-metapac/stm32l552re" ] +stm32l552vc = [ "stm32-metapac/stm32l552vc" ] +stm32l552ve = [ "stm32-metapac/stm32l552ve" ] +stm32l552zc = [ "stm32-metapac/stm32l552zc" ] +stm32l552ze = [ "stm32-metapac/stm32l552ze" ] +stm32l562ce = [ "stm32-metapac/stm32l562ce" ] +stm32l562me = [ "stm32-metapac/stm32l562me" ] +stm32l562qe = [ "stm32-metapac/stm32l562qe" ] +stm32l562re = [ "stm32-metapac/stm32l562re" ] +stm32l562ve = [ "stm32-metapac/stm32l562ve" ] +stm32l562ze = [ "stm32-metapac/stm32l562ze" ] stm32u575ag = [ "stm32-metapac/stm32u575ag" ] stm32u575ai = [ "stm32-metapac/stm32u575ai" ] stm32u575cg = [ "stm32-metapac/stm32u575cg" ] diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 3221cf5e..68d94160 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -14,7 +14,7 @@ fn enable() { crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true)); #[cfg(stm32g0)] crate::pac::RCC.apbenr2().modify(|w| w.set_adcen(true)); - #[cfg(any(stm32l4, stm32wb))] + #[cfg(any(stm32l4, stm32l5, stm32wb))] crate::pac::RCC.ahb2enr().modify(|w| w.set_adcen(true)); }); } @@ -286,6 +286,9 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(not(stm32g0))] // TODO is this supposed to be public? #[allow(unused)] // TODO is this supposed to be public? fn calibrate(&mut self, vref: &mut Vref) { + #[cfg(stm32l5)] + let vref_cal: u32 = todo!(); + #[cfg(not(stm32l5))] let vref_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() }; let old_sample_time = self.sample_time; diff --git a/embassy-stm32/src/dac/v2.rs b/embassy-stm32/src/dac/v2.rs index 35facdd2..00660243 100644 --- a/embassy-stm32/src/dac/v2.rs +++ b/embassy-stm32/src/dac/v2.rs @@ -134,7 +134,7 @@ impl<'d, T: Instance> Dac<'d, T> { enable!(apb1lenr, set_dac1en, apb1lrstr, set_dac1rst); #[cfg(stm32g0)] enable!(apbenr1, set_dac1en, apbrstr1, set_dac1rst); - #[cfg(stm32l4)] + #[cfg(any(stm32l4, stm32l5))] enable!(apb1enr1, set_dac1en, apb1rstr1, set_dac1rst); }); diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs new file mode 100644 index 00000000..a502cb4e --- /dev/null +++ b/embassy-stm32/src/rcc/l5.rs @@ -0,0 +1,468 @@ +use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; +use crate::pac::{FLASH, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; +use crate::time::U32Ext; + +/// HSI16 speed +pub const HSI16_FREQ: u32 = 16_000_000; + +/// System clock mux source +#[derive(Clone, Copy)] +pub enum ClockSrc { + MSI(MSIRange), + PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option), + 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 100 kHz + Range0, + /// Around 200 kHz + Range1, + /// Around 400 kHz + Range2, + /// Around 800 kHz + Range3, + /// Around 1 MHz + Range4, + /// Around 2 MHz + Range5, + /// Around 4 MHz (reset value) + Range6, + /// Around 8 MHz + Range7, + /// Around 16 MHz + Range8, + /// Around 24 MHz + Range9, + /// Around 32 MHz + Range10, + /// Around 48 MHz + Range11, +} + +impl Default for MSIRange { + fn default() -> MSIRange { + MSIRange::Range6 + } +} + +pub type PLL48Div = PLLClkDiv; +pub type PLLSAI1RDiv = PLLClkDiv; +pub type PLLSAI1QDiv = PLLClkDiv; +pub type PLLSAI1PDiv = PLLClkDiv; + +/// PLL divider +#[derive(Clone, Copy)] +pub enum PLLDiv { + Div2, + Div3, + Div4, +} + +/// 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, +} + +/// PLL clock input source +#[derive(Clone, Copy)] +pub enum PLLSource { + HSI16, + HSE(Hertz), +} + +seq_macro::seq!(N in 8..=86 { + #[derive(Clone, Copy)] + pub enum PLLMul { + #( + Mul#N, + )* + } + + impl From for u8 { + fn from(val: PLLMul) -> u8 { + match val { + #( + PLLMul::Mul#N => N, + )* + } + } + } + + impl PLLMul { + pub fn to_mul(self) -> u32 { + match self { + #( + PLLMul::Mul#N => N, + )* + } + } + } +}); + +#[derive(Clone, Copy)] +pub enum PLLClkDiv { + Div2, + Div4, + Div6, + Div8, +} + +impl PLLClkDiv { + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + (val as u32 + 1) * 2 + } +} + +impl From for u8 { + fn from(val: PLLClkDiv) -> u8 { + match val { + PLLClkDiv::Div2 => 0b00, + PLLClkDiv::Div4 => 0b01, + PLLClkDiv::Div6 => 0b10, + PLLClkDiv::Div8 => 0b11, + } + } +} + +#[derive(Clone, Copy)] +pub enum PLLSrcDiv { + Div1, + Div2, + Div3, + Div4, + Div5, + Div6, + Div7, + Div8, +} + +impl PLLSrcDiv { + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + val as u32 + 1 + } +} + +impl From for u8 { + fn from(val: PLLSrcDiv) -> u8 { + match val { + PLLSrcDiv::Div1 => 0b000, + PLLSrcDiv::Div2 => 0b001, + PLLSrcDiv::Div3 => 0b010, + PLLSrcDiv::Div4 => 0b011, + PLLSrcDiv::Div5 => 0b100, + PLLSrcDiv::Div6 => 0b101, + PLLSrcDiv::Div7 => 0b110, + PLLSrcDiv::Div8 => 0b111, + } + } +} + +impl From for Pllsrc { + fn from(val: PLLSource) -> Pllsrc { + match val { + PLLSource::HSI16 => Pllsrc::HSI16, + PLLSource::HSE(_) => Pllsrc::HSE, + } + } +} + +impl From for Ppre { + fn from(val: APBPrescaler) -> Ppre { + match val { + APBPrescaler::NotDivided => Ppre::DIV1, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +impl From for Hpre { + fn from(val: AHBPrescaler) -> Hpre { + match val { + 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 From for Msirange { + fn from(val: MSIRange) -> Msirange { + match val { + MSIRange::Range0 => Msirange::RANGE100K, + MSIRange::Range1 => Msirange::RANGE200K, + MSIRange::Range2 => Msirange::RANGE400K, + MSIRange::Range3 => Msirange::RANGE800K, + MSIRange::Range4 => Msirange::RANGE1M, + MSIRange::Range5 => Msirange::RANGE2M, + MSIRange::Range6 => Msirange::RANGE4M, + MSIRange::Range7 => Msirange::RANGE8M, + MSIRange::Range8 => Msirange::RANGE16M, + MSIRange::Range9 => Msirange::RANGE24M, + MSIRange::Range10 => Msirange::RANGE32M, + MSIRange::Range11 => Msirange::RANGE48M, + } + } +} + +impl From for u32 { + fn from(val: MSIRange) -> u32 { + match val { + MSIRange::Range0 => 100_000, + MSIRange::Range1 => 200_000, + MSIRange::Range2 => 400_000, + MSIRange::Range3 => 800_000, + MSIRange::Range4 => 1_000_000, + MSIRange::Range5 => 2_000_000, + MSIRange::Range6 => 4_000_000, + MSIRange::Range7 => 8_000_000, + MSIRange::Range8 => 16_000_000, + MSIRange::Range9 => 24_000_000, + MSIRange::Range10 => 32_000_000, + MSIRange::Range11 => 48_000_000, + } + } +} + +/// Clocks configutation +pub struct Config { + pub mux: ClockSrc, + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, + pub pllsai1: Option<( + PLLMul, + PLLSrcDiv, + Option, + Option, + Option, + )>, +} + +impl Default for Config { + #[inline] + fn default() -> Config { + Config { + mux: ClockSrc::MSI(MSIRange::Range6), + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + pllsai1: None, + } + } +} + +pub(crate) unsafe fn init(config: Config) { + let (sys_clk, sw) = match config.mux { + ClockSrc::MSI(range) => { + // Enable MSI + RCC.cr().write(|w| { + let bits: Msirange = range.into(); + w.set_msirange(bits); + w.set_msipllen(false); + w.set_msirgsel(true); + w.set_msion(true); + }); + while !RCC.cr().read().msirdy() {} + + // Enable as clock source for USB, RNG if running at 48 MHz + if let MSIRange::Range11 = range { + RCC.ccipr1().modify(|w| { + w.set_clk48msel(0b11); + }); + } + (range.into(), Sw::MSI) + } + ClockSrc::HSI16 => { + // Enable HSI16 + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + (HSI16_FREQ, Sw::HSI16) + } + ClockSrc::HSE(freq) => { + // Enable HSE + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + + (freq.0, Sw::HSE) + } + ClockSrc::PLL(src, div, prediv, mul, pll48div) => { + let src_freq = match src { + PLLSource::HSE(freq) => { + // Enable HSE + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + freq.0 + } + PLLSource::HSI16 => { + // Enable HSI + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + HSI16_FREQ + } + }; + + // Disable PLL + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} + + let freq = (src_freq / prediv.to_div() * mul.to_mul()) / div.to_div(); + + #[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))] + assert!(freq <= 120_000_000); + #[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))] + assert!(freq <= 80_000_000); + + RCC.pllcfgr().write(move |w| { + w.set_plln(mul.into()); + w.set_pllm(prediv.into()); + w.set_pllr(div.into()); + if let Some(pll48div) = pll48div { + w.set_pllq(pll48div.into()); + w.set_pllqen(true); + } + w.set_pllsrc(src.into()); + }); + + // Enable as clock source for USB, RNG if PLL48 divisor is provided + if pll48div.is_some() { + RCC.ccipr1().modify(|w| { + w.set_clk48msel(0b10); + }); + } + + if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 { + RCC.pllsai1cfgr().write(move |w| { + w.set_pllsai1n(mul.into()); + w.set_pllsai1m(prediv.into()); + if let Some(r_div) = r_div { + w.set_pllsai1r(r_div.into()); + w.set_pllsai1ren(true); + } + if let Some(q_div) = q_div { + w.set_pllsai1q(q_div.into()); + w.set_pllsai1qen(true); + let freq = (src_freq / prediv.to_div() * mul.to_mul()) / q_div.to_div(); + if freq == 48_000_000 { + RCC.ccipr1().modify(|w| { + w.set_clk48msel(0b1); + }); + } + } + if let Some(p_div) = p_div { + w.set_pllsai1pdiv(p_div.into()); + w.set_pllsai1pen(true); + } + }); + + RCC.cr().modify(|w| w.set_pllsai1on(true)); + } + + // Enable PLL + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + RCC.pllcfgr().modify(|w| w.set_pllren(true)); + + (freq, Sw::PLL) + } + }; + + // Set flash wait states + // VCORE Range 0 (performance), others TODO + FLASH.acr().modify(|w| { + w.set_latency(match sys_clk { + 0..=20_000_000 => 0, + 0..=40_000_000 => 1, + 0..=60_000_000 => 2, + 0..=80_000_000 => 3, + 0..=100_000_000 => 4, + _ => 5, + }) + }); + + RCC.cfgr().modify(|w| { + w.set_sw(sw); + w.set_hpre(config.ahb_pre.into()); + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + + let ahb_freq: u32 = match config.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) = match config.apb1_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + pre => { + let pre: Ppre = pre.into(); + let pre: u8 = 1 << (pre.0 - 3); + let freq = ahb_freq / pre as u32; + (freq, freq * 2) + } + }; + + let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + 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) + } + }; + + set_freqs(Clocks { + sys: sys_clk.hz(), + ahb1: ahb_freq.hz(), + ahb2: ahb_freq.hz(), + ahb3: ahb_freq.hz(), + apb1: apb1_freq.hz(), + apb2: apb2_freq.hz(), + apb1_tim: apb1_tim_freq.hz(), + apb2_tim: apb2_tim_freq.hz(), + }); +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 3090b416..37ecf8fb 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -15,6 +15,7 @@ use core::mem::MaybeUninit; #[cfg_attr(rcc_l0, path = "l0.rs")] #[cfg_attr(rcc_l1, path = "l1.rs")] #[cfg_attr(rcc_l4, path = "l4.rs")] +#[cfg_attr(rcc_l5, path = "l5.rs")] #[cfg_attr(rcc_u5, path = "u5.rs")] #[cfg_attr(rcc_wb, path = "wb.rs")] #[cfg_attr(rcc_wl5, path = "wl5.rs")] @@ -40,11 +41,12 @@ pub struct Clocks { // AHB pub ahb1: Hertz, #[cfg(any( - rcc_l4, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5 + rcc_l4, rcc_l5, 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_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5 + rcc_l4, rcc_l5, 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))] diff --git a/stm32-data b/stm32-data index 246e21c6..bd1c21fd 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 246e21c634e15b5c8337c51cb50e52b9df51ad35 +Subproject commit bd1c21fdc26f8e2213bf3f78eaa935f1bd3785f0 diff --git a/stm32-gen-features/src/lib.rs b/stm32-gen-features/src/lib.rs index d184827c..c9768cdb 100644 --- a/stm32-gen-features/src/lib.rs +++ b/stm32-gen-features/src/lib.rs @@ -14,6 +14,7 @@ const SUPPORTED_FAMILIES: &[&str] = &[ "stm32l0", "stm32l1", "stm32l4", + "stm32l5", "stm32h7", "stm32u5", "stm32wb55",