Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions embassy-stm32/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS
- fix: STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577))
- feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581))
- Improve the PLL output frequency calculation precision for the F2, F4, and F7
series.

## 0.4.0 - 2025-08-26

Expand Down
27 changes: 13 additions & 14 deletions embassy-stm32/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1682,8 +1682,7 @@ fn main() {

if is_rcc_name(e.name) {
let enum_name = format_ident!("{}", e.name);
let mut muls = Vec::new();
let mut divs = Vec::new();
let mut nds = Vec::new();
for v in e.variants {
let Ok(val) = parse_num(v.name) else {
panic!("could not parse mul/div. enum={} variant={}", e.name, v.name)
Expand All @@ -1692,29 +1691,29 @@ fn main() {
let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name);
let num = val.num;
let denom = val.denom;
muls.push(quote!(#variant => self * #num / #denom,));
divs.push(quote!(#variant => self * #denom / #num,));
nds.push(quote!(#variant => Self { num: #num, denom: #denom },));
}

g.extend(quote! {
impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
type Output = crate::time::Hertz;
fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
match rhs {
#(#divs)*
impl core::convert::From<crate::pac::rcc::vals::#enum_name> for crate::time::Scale {
fn from(value: crate::pac::rcc::vals::#enum_name) -> Self {
match value {
#(#nds)*
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
}
impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
type Output = crate::time::Hertz;
fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
self / crate::time::Scale::from(rhs)
}
}
impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
type Output = crate::time::Hertz;
fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
match rhs {
#(#muls)*
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
self * crate::time::Scale::from(rhs)
}
}
});
Expand Down
16 changes: 11 additions & 5 deletions embassy-stm32/src/rcc/f247.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use stm32_metapac::flash::vals::Latency;

#[cfg(any(stm32f413, stm32f423, stm32f412))]
pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource;
use crate::pac::rcc::vals::Pllm;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
};
#[cfg(any(stm32f4, stm32f7))]
use crate::pac::PWR;
use crate::pac::{FLASH, RCC};
use crate::time::Hertz;
use crate::time::{Hertz, Scale};

// TODO: on some F4s, PLLM is shared between all PLLs. Enforce that.
// TODO: on some F4s, add support for plli2s_src
Expand Down Expand Up @@ -393,7 +394,8 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll

let in_freq = pll_src / pll.prediv;
assert!(max::PLL_IN.contains(&in_freq));
let vco_freq = in_freq * pll.mul;
let pll_mulsrc = pll_src.0 as u64 * Scale::from(pll.mul);
let vco_freq = Hertz((pll_mulsrc / Scale::from(pll.prediv)) as u32);
assert!(max::PLL_VCO.contains(&vco_freq));

// stm32f2 plls are like swiss cheese
Expand All @@ -408,9 +410,13 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll
}
}

let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div);
fn div_pll<T: Into<Scale>>(pll_mulsrc: u64, prediv: Pllm, div: T) -> Hertz {
Hertz((pll_mulsrc / (Scale::from(prediv) * div.into())) as u32)
}

let p = pll.divp.map(|div| div_pll(pll_mulsrc, pll.prediv, div));
let q = pll.divq.map(|div| div_pll(pll_mulsrc, pll.prediv, div));
let r = pll.divr.map(|div| div_pll(pll_mulsrc, pll.prediv, div));

macro_rules! write_fields {
($w:ident) => {
Expand Down
44 changes: 44 additions & 0 deletions embassy-stm32/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,47 @@ impl From<MaybeHertz> for Option<Hertz> {
}
}
}

#[derive(Clone, Copy, Debug, Default)]
/// Time scaling factor (multiplier/divider), may be an unreduced fraction
pub(crate) struct Scale {
pub num: u32,
pub denom: u32,
}

impl Mul for Scale {
type Output = Scale;
fn mul(self, rhs: Self) -> Self::Output {
let num = self.num * rhs.num;
let denom = self.denom * rhs.denom;
Self { num, denom }
}
}

impl Mul<Scale> for Hertz {
type Output = Hertz;
fn mul(self, rhs: Scale) -> Self::Output {
self * rhs.num / rhs.denom
}
}

impl Div<Scale> for Hertz {
type Output = Hertz;
fn div(self, rhs: Scale) -> Self::Output {
self * rhs.denom / rhs.num
}
}

impl Mul<Scale> for u64 {
type Output = u64;
fn mul(self, rhs: Scale) -> Self::Output {
self * rhs.num as u64 / rhs.denom as u64
}
}

impl Div<Scale> for u64 {
type Output = u64;
fn div(self, rhs: Scale) -> Self::Output {
self * rhs.denom as u64 / rhs.num as u64
}
}