Skip to content

Commit 08f590e

Browse files
committed
Improve f247 PLL output frequency calculation precision
This will produce the correct result for all integer output frequencies and a truncated result for all non-integer output frequencies instead of accumulating an error. This is done by introducing a time::Scale type which is just a rational representing the numerator <-> denominator relationship of a scaling factor register.
1 parent 25e0ebf commit 08f590e

File tree

4 files changed

+70
-19
lines changed

4 files changed

+70
-19
lines changed

embassy-stm32/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS
1616
- 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))
1717
- feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581))
18+
- Improve the PLL output frequency calculation precision for the F2, F4, and F7
19+
series.
1820

1921
## 0.4.0 - 2025-08-26
2022

embassy-stm32/build.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,8 +1682,7 @@ fn main() {
16821682

16831683
if is_rcc_name(e.name) {
16841684
let enum_name = format_ident!("{}", e.name);
1685-
let mut muls = Vec::new();
1686-
let mut divs = Vec::new();
1685+
let mut nds = Vec::new();
16871686
for v in e.variants {
16881687
let Ok(val) = parse_num(v.name) else {
16891688
panic!("could not parse mul/div. enum={} variant={}", e.name, v.name)
@@ -1692,29 +1691,29 @@ fn main() {
16921691
let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name);
16931692
let num = val.num;
16941693
let denom = val.denom;
1695-
muls.push(quote!(#variant => self * #num / #denom,));
1696-
divs.push(quote!(#variant => self * #denom / #num,));
1694+
nds.push(quote!(#variant => Self { num: #num, denom: #denom },));
16971695
}
16981696

16991697
g.extend(quote! {
1700-
impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
1701-
type Output = crate::time::Hertz;
1702-
fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
1703-
match rhs {
1704-
#(#divs)*
1698+
impl core::convert::From<crate::pac::rcc::vals::#enum_name> for crate::time::Scale {
1699+
fn from(value: crate::pac::rcc::vals::#enum_name) -> Self {
1700+
match value {
1701+
#(#nds)*
17051702
#[allow(unreachable_patterns)]
17061703
_ => unreachable!(),
17071704
}
17081705
}
17091706
}
1707+
impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
1708+
type Output = crate::time::Hertz;
1709+
fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
1710+
self / crate::time::Scale::from(rhs)
1711+
}
1712+
}
17101713
impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
17111714
type Output = crate::time::Hertz;
17121715
fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
1713-
match rhs {
1714-
#(#muls)*
1715-
#[allow(unreachable_patterns)]
1716-
_ => unreachable!(),
1717-
}
1716+
self * crate::time::Scale::from(rhs)
17181717
}
17191718
}
17201719
});

embassy-stm32/src/rcc/f247.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ use stm32_metapac::flash::vals::Latency;
22

33
#[cfg(any(stm32f413, stm32f423, stm32f412))]
44
pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource;
5+
use crate::pac::rcc::vals::Pllm;
56
pub use crate::pac::rcc::vals::{
67
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,
78
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
89
};
910
#[cfg(any(stm32f4, stm32f7))]
1011
use crate::pac::PWR;
1112
use crate::pac::{FLASH, RCC};
12-
use crate::time::Hertz;
13+
use crate::time::{Hertz, Scale};
1314

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

394395
let in_freq = pll_src / pll.prediv;
395396
assert!(max::PLL_IN.contains(&in_freq));
396-
let vco_freq = in_freq * pll.mul;
397+
let pll_mulsrc = pll_src.0 as u64 * Scale::from(pll.mul);
398+
let vco_freq = Hertz((pll_mulsrc / Scale::from(pll.prediv)) as u32);
397399
assert!(max::PLL_VCO.contains(&vco_freq));
398400

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

411-
let p = pll.divp.map(|div| vco_freq / div);
412-
let q = pll.divq.map(|div| vco_freq / div);
413-
let r = pll.divr.map(|div| vco_freq / div);
413+
fn div_pll<T: Into<Scale>>(pll_mulsrc: u64, prediv: Pllm, div: T) -> Hertz {
414+
Hertz((pll_mulsrc / (Scale::from(prediv) * div.into())) as u32)
415+
}
416+
417+
let p = pll.divp.map(|div| div_pll(pll_mulsrc, pll.prediv, div));
418+
let q = pll.divq.map(|div| div_pll(pll_mulsrc, pll.prediv, div));
419+
let r = pll.divr.map(|div| div_pll(pll_mulsrc, pll.prediv, div));
414420

415421
macro_rules! write_fields {
416422
($w:ident) => {

embassy-stm32/src/time.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,47 @@ impl From<MaybeHertz> for Option<Hertz> {
136136
}
137137
}
138138
}
139+
140+
#[derive(Clone, Copy, Debug, Default)]
141+
/// Time scaling factor (multiplier/divider), may be an unreduced fraction
142+
pub(crate) struct Scale {
143+
pub num: u32,
144+
pub denom: u32,
145+
}
146+
147+
impl Mul for Scale {
148+
type Output = Scale;
149+
fn mul(self, rhs: Self) -> Self::Output {
150+
let num = self.num * rhs.num;
151+
let denom = self.denom * rhs.denom;
152+
Self { num, denom }
153+
}
154+
}
155+
156+
impl Mul<Scale> for Hertz {
157+
type Output = Hertz;
158+
fn mul(self, rhs: Scale) -> Self::Output {
159+
self * rhs.num / rhs.denom
160+
}
161+
}
162+
163+
impl Div<Scale> for Hertz {
164+
type Output = Hertz;
165+
fn div(self, rhs: Scale) -> Self::Output {
166+
self * rhs.denom / rhs.num
167+
}
168+
}
169+
170+
impl Mul<Scale> for u64 {
171+
type Output = u64;
172+
fn mul(self, rhs: Scale) -> Self::Output {
173+
self * rhs.num as u64 / rhs.denom as u64
174+
}
175+
}
176+
177+
impl Div<Scale> for u64 {
178+
type Output = u64;
179+
fn div(self, rhs: Scale) -> Self::Output {
180+
self * rhs.denom as u64 / rhs.num as u64
181+
}
182+
}

0 commit comments

Comments
 (0)