aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-07-24 21:23:02 +0000
committerGitHub <[email protected]>2025-07-24 21:23:02 +0000
commitff29d61b318efb54912096ec7771b0a906380440 (patch)
tree2e763aae46a2969ef7a6e69e5fe868bccf4742aa
parent3bb94469f4efebee04f2d8e5f380c1cfbc967ad6 (diff)
parent0d1e34d0fcc1fb995f0da46eede063cfbd9c962e (diff)
Merge pull request #4313 from snakehand/main
U5: Enable MSI auto calibration and compute frequencies
-rw-r--r--embassy-stm32/src/rcc/u5.rs165
1 files changed, 160 insertions, 5 deletions
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs
index 97eb2eb6d..06895a99a 100644
--- a/embassy-stm32/src/rcc/u5.rs
+++ b/embassy-stm32/src/rcc/u5.rs
@@ -5,7 +5,7 @@ pub use crate::pac::rcc::vals::{
5 Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, 5 Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
6 Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, 6 Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,
7}; 7};
8use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; 8use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge};
9#[cfg(all(peri_usb_otg_hs))] 9#[cfg(all(peri_usb_otg_hs))]
10pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; 10pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG};
11use crate::pac::{FLASH, PWR, RCC}; 11use crate::pac::{FLASH, PWR, RCC};
@@ -64,6 +64,46 @@ pub struct Pll {
64 pub divr: Option<PllDiv>, 64 pub divr: Option<PllDiv>,
65} 65}
66 66
67#[derive(Clone, Copy, PartialEq)]
68pub enum MsiAutoCalibration {
69 /// MSI auto-calibration is disabled
70 Disabled,
71 /// MSIS is given priority for auto-calibration
72 MSIS,
73 /// MSIK is given priority for auto-calibration
74 MSIK,
75 /// MSIS with fast mode (always on)
76 MsisFast,
77 /// MSIK with fast mode (always on)
78 MsikFast,
79}
80
81impl MsiAutoCalibration {
82 const fn default() -> Self {
83 MsiAutoCalibration::Disabled
84 }
85
86 fn base_mode(&self) -> Self {
87 match self {
88 MsiAutoCalibration::Disabled => MsiAutoCalibration::Disabled,
89 MsiAutoCalibration::MSIS => MsiAutoCalibration::MSIS,
90 MsiAutoCalibration::MSIK => MsiAutoCalibration::MSIK,
91 MsiAutoCalibration::MsisFast => MsiAutoCalibration::MSIS,
92 MsiAutoCalibration::MsikFast => MsiAutoCalibration::MSIK,
93 }
94 }
95
96 fn is_fast(&self) -> bool {
97 matches!(self, MsiAutoCalibration::MsisFast | MsiAutoCalibration::MsikFast)
98 }
99}
100
101impl Default for MsiAutoCalibration {
102 fn default() -> Self {
103 Self::default()
104 }
105}
106
67#[derive(Clone, Copy)] 107#[derive(Clone, Copy)]
68pub struct Config { 108pub struct Config {
69 // base clock sources 109 // base clock sources
@@ -95,6 +135,7 @@ pub struct Config {
95 135
96 /// Per-peripheral kernel clock selection muxes 136 /// Per-peripheral kernel clock selection muxes
97 pub mux: super::mux::ClockMux, 137 pub mux: super::mux::ClockMux,
138 pub auto_calibration: MsiAutoCalibration,
98} 139}
99 140
100impl Config { 141impl Config {
@@ -116,6 +157,7 @@ impl Config {
116 voltage_range: VoltageScale::RANGE1, 157 voltage_range: VoltageScale::RANGE1,
117 ls: crate::rcc::LsConfig::new(), 158 ls: crate::rcc::LsConfig::new(),
118 mux: super::mux::ClockMux::default(), 159 mux: super::mux::ClockMux::default(),
160 auto_calibration: MsiAutoCalibration::default(),
119 } 161 }
120 } 162 }
121} 163}
@@ -131,7 +173,42 @@ pub(crate) unsafe fn init(config: Config) {
131 PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); 173 PWR.vosr().modify(|w| w.set_vos(config.voltage_range));
132 while !PWR.vosr().read().vosrdy() {} 174 while !PWR.vosr().read().vosrdy() {}
133 175
134 let msis = config.msis.map(|range| { 176 let lse_calibration_freq = if config.auto_calibration != MsiAutoCalibration::Disabled {
177 // LSE must be configured and peripherals clocked for MSI auto-calibration
178 let lse_config = config
179 .ls
180 .lse
181 .clone()
182 .expect("LSE must be configured for MSI auto-calibration");
183 assert!(lse_config.peripherals_clocked);
184
185 // Expect less than +/- 5% deviation for LSE frequency
186 if (31_100..=34_400).contains(&lse_config.frequency.0) {
187 // Check that the calibration is applied to an active clock
188 match (
189 config.auto_calibration.base_mode(),
190 config.msis.is_some(),
191 config.msik.is_some(),
192 ) {
193 (MsiAutoCalibration::MSIS, true, _) => {
194 // MSIS is active and using LSE for auto-calibration
195 Some(lse_config.frequency)
196 }
197 (MsiAutoCalibration::MSIK, _, true) => {
198 // MSIK is active and using LSE for auto-calibration
199 Some(lse_config.frequency)
200 }
201 // improper configuration
202 _ => panic!("MSIx auto-calibration is enabled for a source that has not been configured."),
203 }
204 } else {
205 panic!("LSE frequency more than 5% off from 32.768 kHz, cannot use for MSI auto-calibration");
206 }
207 } else {
208 None
209 };
210
211 let mut msis = config.msis.map(|range| {
135 // Check MSI output per RM0456 § 11.4.10 212 // Check MSI output per RM0456 § 11.4.10
136 match config.voltage_range { 213 match config.voltage_range {
137 VoltageScale::RANGE4 => { 214 VoltageScale::RANGE4 => {
@@ -156,11 +233,21 @@ pub(crate) unsafe fn init(config: Config) {
156 w.set_msipllen(false); 233 w.set_msipllen(false);
157 w.set_msison(true); 234 w.set_msison(true);
158 }); 235 });
236 let msis = if let (Some(freq), MsiAutoCalibration::MSIS) =
237 (lse_calibration_freq, config.auto_calibration.base_mode())
238 {
239 // Enable the MSIS auto-calibration feature
240 RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS));
241 RCC.cr().modify(|w| w.set_msipllen(true));
242 calculate_calibrated_msi_frequency(range, freq)
243 } else {
244 msirange_to_hertz(range)
245 };
159 while !RCC.cr().read().msisrdy() {} 246 while !RCC.cr().read().msisrdy() {}
160 msirange_to_hertz(range) 247 msis
161 }); 248 });
162 249
163 let msik = config.msik.map(|range| { 250 let mut msik = config.msik.map(|range| {
164 // Check MSI output per RM0456 § 11.4.10 251 // Check MSI output per RM0456 § 11.4.10
165 match config.voltage_range { 252 match config.voltage_range {
166 VoltageScale::RANGE4 => { 253 VoltageScale::RANGE4 => {
@@ -184,10 +271,44 @@ pub(crate) unsafe fn init(config: Config) {
184 RCC.cr().modify(|w| { 271 RCC.cr().modify(|w| {
185 w.set_msikon(true); 272 w.set_msikon(true);
186 }); 273 });
274 let msik = if let (Some(freq), MsiAutoCalibration::MSIK) =
275 (lse_calibration_freq, config.auto_calibration.base_mode())
276 {
277 // Enable the MSIK auto-calibration feature
278 RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK));
279 RCC.cr().modify(|w| w.set_msipllen(true));
280 calculate_calibrated_msi_frequency(range, freq)
281 } else {
282 msirange_to_hertz(range)
283 };
187 while !RCC.cr().read().msikrdy() {} 284 while !RCC.cr().read().msikrdy() {}
188 msirange_to_hertz(range) 285 msik
189 }); 286 });
190 287
288 if let Some(lse_freq) = lse_calibration_freq {
289 // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source.
290 if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) {
291 if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) {
292 // Clock source is shared, both will be auto calibrated, recalculate other frequency
293 match config.auto_calibration.base_mode() {
294 MsiAutoCalibration::MSIS => {
295 msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq));
296 }
297 MsiAutoCalibration::MSIK => {
298 msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq));
299 }
300 _ => {}
301 }
302 }
303 }
304 // Check if Fast mode should be used
305 if config.auto_calibration.is_fast() {
306 RCC.cr().modify(|w| {
307 w.set_msipllfast(Msipllfast::FAST);
308 });
309 }
310 }
311
191 let hsi = config.hsi.then(|| { 312 let hsi = config.hsi.then(|| {
192 RCC.cr().modify(|w| w.set_hsion(true)); 313 RCC.cr().modify(|w| w.set_hsion(true));
193 while !RCC.cr().read().hsirdy() {} 314 while !RCC.cr().read().hsirdy() {}
@@ -514,3 +635,37 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltag
514 635
515 PllOutput { p, q, r } 636 PllOutput { p, q, r }
516} 637}
638
639/// Fraction structure for MSI auto-calibration
640/// Represents the multiplier as numerator/denominator that LSE frequency is multiplied by
641#[derive(Debug, Clone, Copy)]
642struct MsiFraction {
643 numerator: u32,
644 denominator: u32,
645}
646
647impl MsiFraction {
648 const fn new(numerator: u32, denominator: u32) -> Self {
649 Self { numerator, denominator }
650 }
651
652 /// Calculate the calibrated frequency given an LSE frequency
653 fn calculate_frequency(&self, lse_freq: Hertz) -> Hertz {
654 Hertz(lse_freq.0 * self.numerator / self.denominator)
655 }
656}
657
658fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction {
659 // Exploiting the MSIx internals to make calculations compact
660 let denominator = (range as u32 & 0x03) + 1;
661 // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet
662 let numerator = [1465, 122, 94, 12][range as usize >> 2];
663
664 MsiFraction::new(numerator, denominator)
665}
666
667/// Calculate the calibrated MSI frequency for a given range and LSE frequency
668fn calculate_calibrated_msi_frequency(range: Msirange, lse_freq: Hertz) -> Hertz {
669 let fraction = get_msi_calibration_fraction(range);
670 fraction.calculate_frequency(lse_freq)
671}