From a9727a17b593f7328f721e8905b7fc8dab9ae7ff Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Sun, 5 Oct 2025 15:49:05 -0700 Subject: [PATCH 1/2] stm32/ADC: Fix prescaler calculation to include max frequency. Due to the integer rounding rules one has to subtract 1 from the numerator. For example: Let max clock be 55 and supplied clock be 110 110/55 = 2 which results in the divider being set to 4 and the clock after division ends up being 27 instead of 55 Subtracting 1 to the numerator get around the rounding issue 109/55 = 1 which results in the divider being set to 2 and the clock after division ends up being 55 which is exactly max clock --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/adc/adc4.rs | 3 ++- embassy-stm32/src/adc/c0.rs | 3 ++- embassy-stm32/src/adc/g4.rs | 3 ++- embassy-stm32/src/adc/v2.rs | 3 ++- embassy-stm32/src/adc/v4.rs | 3 ++- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index a6ee5c4b81..716c169e16 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer +- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 255dc7956f..1302dffb81 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -128,7 +128,8 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f2837a8f12..bd9a3e2c6c 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -66,7 +66,8 @@ pub enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 43498966f1..ac0a6196fa 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -72,7 +72,8 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e94a25b249..57f252e133 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -71,7 +71,8 @@ impl Prescaler { // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. #[cfg(not(stm32f2))] const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - let raw_div = freq.0 / MAX_FREQUENCY.0; + // Calculate prescaler divider including MAX_FREQ + let raw_div = freq.0.saturating_sub(1) / MAX_FREQUENCY.0; match raw_div { 0..=1 => Self::Div2, 2..=3 => Self::Div4, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b66437e6e2..c68684cb28 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -93,7 +93,8 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, From 6831fdbfe896e9f848f93c31473703fa1c767198 Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Sun, 5 Oct 2025 19:41:49 -0700 Subject: [PATCH 2/2] stm32: Add raw_prescaler function to make it more reusable. This also puts the explanation why the calculation has to be done that way into one place so it does not need to be copied all over the codebase. --- embassy-stm32/src/adc/adc4.rs | 3 +-- embassy-stm32/src/adc/c0.rs | 3 +-- embassy-stm32/src/adc/g4.rs | 3 +-- embassy-stm32/src/adc/v2.rs | 3 +-- embassy-stm32/src/adc/v4.rs | 3 +-- embassy-stm32/src/rcc/mod.rs | 30 ++++++++++++++++++++++++++++++ 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 1302dffb81..0b442330a8 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -128,8 +128,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index bd9a3e2c6c..5b3438ea1f 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -66,8 +66,7 @@ pub enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index ac0a6196fa..6c7789c0e1 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -72,8 +72,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 57f252e133..09e0f4d117 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -71,8 +71,7 @@ impl Prescaler { // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. #[cfg(not(stm32f2))] const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - // Calculate prescaler divider including MAX_FREQ - let raw_div = freq.0.saturating_sub(1) / MAX_FREQUENCY.0; + let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0); match raw_div { 0..=1 => Self::Div2, 2..=3 => Self::Div4, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c68684cb28..6c8ce7b64c 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -93,8 +93,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c41f818168..8509838ed4 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -409,3 +409,33 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { } } } + +/// Calculate intermediate prescaler number used to calculate peripheral prescalers +/// +/// This function is intended to calculate a number indicating a minimum division +/// necessary to result in a frequency lower than the provided `freq_max`. +/// +/// The returned value indicates the `val + 1` divider is necessary to result in +/// the output frequency that is below the maximum provided. +/// +/// For example: +/// 0 = divider of 1 => no division necessary as the input frequency is below max +/// 1 = divider of 2 => division by 2 necessary +/// ... +/// +/// The provided max frequency is inclusive. So if `freq_in == freq_max` the result +/// will be 0, indicating that no division is necessary. To accomplish that we subtract +/// 1 from the input frequency so that the integer rounding plays in our favor. +/// +/// For example: +/// Let the input frequency be 110 and the max frequency be 55. +/// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3 +/// which in reality will be rounded up to 4 as usually a 3 division is not available. +/// In either case the resulting frequency will be either 36 or 27 which is lower than +/// what we would want. The result should be 1. +/// If we do the following instead `109/55 = 1` indicating that we need a divide by 2 +/// which will result in the correct 55. +#[allow(unused)] +pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 { + freq_in.saturating_sub(1) / freq_max +}