diff options
| author | Frank Stevenson <[email protected]> | 2025-07-23 10:08:41 +0200 |
|---|---|---|
| committer | Frank Stevenson <[email protected]> | 2025-07-23 10:08:41 +0200 |
| commit | cf9856255e1c2abf3ad2164ce669645f5da098c6 (patch) | |
| tree | 1826bdba99f2ca91670534e2e23355c247482be8 | |
| parent | d750a8b6b99d7012a1aab21ab67ba530e7a7206b (diff) | |
Make MSI calibration configurabke.
Refine detection and handling of shared clock sources between MSIS and MSIK
| -rw-r--r-- | embassy-stm32/src/rcc/u5.rs | 101 |
1 files changed, 67 insertions, 34 deletions
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index e7fe50f33..0c7dc8ecc 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -64,6 +64,28 @@ pub struct Pll { | |||
| 64 | pub divr: Option<PllDiv>, | 64 | pub divr: Option<PllDiv>, |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | #[derive(Clone, Copy, PartialEq)] | ||
| 68 | pub 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 | } | ||
| 76 | |||
| 77 | impl MsiAutoCalibration { | ||
| 78 | const fn default() -> Self { | ||
| 79 | MsiAutoCalibration::Disabled | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | impl Default for MsiAutoCalibration { | ||
| 84 | fn default() -> Self { | ||
| 85 | Self::default() | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 67 | #[derive(Clone, Copy)] | 89 | #[derive(Clone, Copy)] |
| 68 | pub struct Config { | 90 | pub struct Config { |
| 69 | // base clock sources | 91 | // base clock sources |
| @@ -95,6 +117,7 @@ pub struct Config { | |||
| 95 | 117 | ||
| 96 | /// Per-peripheral kernel clock selection muxes | 118 | /// Per-peripheral kernel clock selection muxes |
| 97 | pub mux: super::mux::ClockMux, | 119 | pub mux: super::mux::ClockMux, |
| 120 | pub auto_calibration: MsiAutoCalibration, | ||
| 98 | } | 121 | } |
| 99 | 122 | ||
| 100 | impl Config { | 123 | impl Config { |
| @@ -116,6 +139,7 @@ impl Config { | |||
| 116 | voltage_range: VoltageScale::RANGE1, | 139 | voltage_range: VoltageScale::RANGE1, |
| 117 | ls: crate::rcc::LsConfig::new(), | 140 | ls: crate::rcc::LsConfig::new(), |
| 118 | mux: super::mux::ClockMux::default(), | 141 | mux: super::mux::ClockMux::default(), |
| 142 | auto_calibration: MsiAutoCalibration::default(), | ||
| 119 | } | 143 | } |
| 120 | } | 144 | } |
| 121 | } | 145 | } |
| @@ -133,7 +157,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 133 | 157 | ||
| 134 | let lse_calibration_freq = match config.ls.lse { | 158 | let lse_calibration_freq = match config.ls.lse { |
| 135 | Some(lse_config) => { | 159 | Some(lse_config) => { |
| 136 | if lse_config.peripherals_clocked && (31_000..=34_000).contains(&lse_config.frequency.0) { | 160 | // Allow +/- 5% tolerance for LSE frequency |
| 161 | if lse_config.peripherals_clocked && (31_100..=34_400).contains(&lse_config.frequency.0) { | ||
| 137 | Some(lse_config.frequency) | 162 | Some(lse_config.frequency) |
| 138 | } else { | 163 | } else { |
| 139 | None | 164 | None |
| @@ -167,11 +192,19 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 167 | w.set_msipllen(false); | 192 | w.set_msipllen(false); |
| 168 | w.set_msison(true); | 193 | w.set_msison(true); |
| 169 | }); | 194 | }); |
| 195 | let msis = if let (Some(freq), MsiAutoCalibration::MSIS) = (lse_calibration_freq, config.auto_calibration) { | ||
| 196 | // Enable the MSIS auto-calibration feature | ||
| 197 | RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS)); | ||
| 198 | RCC.cr().modify(|w| w.set_msipllen(true)); | ||
| 199 | calculate_calibrated_msi_frequency(range, freq) | ||
| 200 | } else { | ||
| 201 | msirange_to_hertz(range) | ||
| 202 | }; | ||
| 170 | while !RCC.cr().read().msisrdy() {} | 203 | while !RCC.cr().read().msisrdy() {} |
| 171 | msirange_to_hertz(range) | 204 | msis |
| 172 | }); | 205 | }); |
| 173 | 206 | ||
| 174 | let msik = config.msik.map(|range| { | 207 | let mut msik = config.msik.map(|range| { |
| 175 | // Check MSI output per RM0456 § 11.4.10 | 208 | // Check MSI output per RM0456 § 11.4.10 |
| 176 | match config.voltage_range { | 209 | match config.voltage_range { |
| 177 | VoltageScale::RANGE4 => { | 210 | VoltageScale::RANGE4 => { |
| @@ -195,24 +228,38 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 195 | RCC.cr().modify(|w| { | 228 | RCC.cr().modify(|w| { |
| 196 | w.set_msikon(true); | 229 | w.set_msikon(true); |
| 197 | }); | 230 | }); |
| 198 | if lse_calibration_freq.is_some() { | 231 | let msik = if let (Some(freq), MsiAutoCalibration::MSIK) = (lse_calibration_freq, config.auto_calibration) { |
| 199 | // Enable the MSIK auto-calibration feature | 232 | // Enable the MSIK auto-calibration feature |
| 200 | RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK)); | 233 | RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK)); |
| 201 | RCC.cr().modify(|w| w.set_msipllen(true)); | 234 | RCC.cr().modify(|w| w.set_msipllen(true)); |
| 202 | } | 235 | calculate_calibrated_msi_frequency(range, freq) |
| 203 | while !RCC.cr().read().msikrdy() {} | ||
| 204 | if let Some(freq) = lse_calibration_freq { | ||
| 205 | let msik_freq = calculate_calibrated_msi_frequency(range, freq); | ||
| 206 | if config.msis == config.msik { | ||
| 207 | // If MSIS and MSIK are the same range both will be auto calibrated to the same frequency | ||
| 208 | msis = Some(msik_freq) | ||
| 209 | } | ||
| 210 | msik_freq | ||
| 211 | } else { | 236 | } else { |
| 212 | msirange_to_hertz(range) | 237 | msirange_to_hertz(range) |
| 213 | } | 238 | }; |
| 239 | while !RCC.cr().read().msikrdy() {} | ||
| 240 | msik | ||
| 214 | }); | 241 | }); |
| 215 | 242 | ||
| 243 | // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source. | ||
| 244 | if let Some(lse_freq) = lse_calibration_freq { | ||
| 245 | if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) { | ||
| 246 | if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) { | ||
| 247 | // Clock source is shared, both will be auto calibrated. | ||
| 248 | match config.auto_calibration { | ||
| 249 | MsiAutoCalibration::MSIS => { | ||
| 250 | // MSIS and MSIK are using the same clock source, recalibrate | ||
| 251 | msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq)); | ||
| 252 | } | ||
| 253 | MsiAutoCalibration::MSIK => { | ||
| 254 | // MSIS and MSIK are using the same clock source, recalibrate | ||
| 255 | msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq)); | ||
| 256 | } | ||
| 257 | _ => {} | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 216 | let hsi = config.hsi.then(|| { | 263 | let hsi = config.hsi.then(|| { |
| 217 | RCC.cr().modify(|w| w.set_hsion(true)); | 264 | RCC.cr().modify(|w| w.set_hsion(true)); |
| 218 | while !RCC.cr().read().hsirdy() {} | 265 | while !RCC.cr().read().hsirdy() {} |
| @@ -559,27 +606,13 @@ impl MsiFraction { | |||
| 559 | } | 606 | } |
| 560 | } | 607 | } |
| 561 | 608 | ||
| 562 | /// Get the calibration fraction for a given MSI range | ||
| 563 | /// Based on STM32U5 datasheet table for LSE = 32.768 kHz | ||
| 564 | fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction { | 609 | fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction { |
| 565 | match range { | 610 | // Exploiting the MSIx internals to make calculations compact |
| 566 | Msirange::RANGE_48MHZ => MsiFraction::new(1465, 1), // Range 0: 48.005 MHz | 611 | let denominator = (range as u32 & 0x03) + 1; |
| 567 | Msirange::RANGE_24MHZ => MsiFraction::new(1465, 2), // Range 1: 24.003 MHz | 612 | // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet |
| 568 | Msirange::RANGE_16MHZ => MsiFraction::new(1465, 3), // Range 2: 16.002 MHz | 613 | let numerator = [1465, 122, 94, 12][(range as u32 >> 2) as usize]; |
| 569 | Msirange::RANGE_12MHZ => MsiFraction::new(1465, 4), // Range 3: 12.001 MHz | 614 | |
| 570 | Msirange::RANGE_4MHZ => MsiFraction::new(122, 1), // Range 4: 3.998 MHz | 615 | MsiFraction::new(numerator, denominator) |
| 571 | Msirange::RANGE_2MHZ => MsiFraction::new(61, 1), // Range 5: 1.999 MHz | ||
| 572 | Msirange::RANGE_1_33MHZ => MsiFraction::new(122, 3), // Range 6: 1.333 MHz | ||
| 573 | Msirange::RANGE_1MHZ => MsiFraction::new(61, 2), // Range 7: 0.999 MHz | ||
| 574 | Msirange::RANGE_3_072MHZ => MsiFraction::new(94, 1), // Range 8: 3.08 MHz | ||
| 575 | Msirange::RANGE_1_536MHZ => MsiFraction::new(47, 1), // Range 9: 1.54 MHz | ||
| 576 | Msirange::RANGE_1_024MHZ => MsiFraction::new(94, 3), // Range 10: 1.027 MHz | ||
| 577 | Msirange::RANGE_768KHZ => MsiFraction::new(47, 2), // Range 11: 0.77 MHz | ||
| 578 | Msirange::RANGE_400KHZ => MsiFraction::new(12, 1), // Range 12: 393 kHz | ||
| 579 | Msirange::RANGE_200KHZ => MsiFraction::new(6, 1), // Range 13: 196.6 kHz | ||
| 580 | Msirange::RANGE_133KHZ => MsiFraction::new(4, 1), // Range 14: 131 kHz | ||
| 581 | Msirange::RANGE_100KHZ => MsiFraction::new(3, 1), // Range 15: 98.3 kHz | ||
| 582 | } | ||
| 583 | } | 616 | } |
| 584 | 617 | ||
| 585 | /// Calculate the calibrated MSI frequency for a given range and LSE frequency | 618 | /// Calculate the calibrated MSI frequency for a given range and LSE frequency |
