aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrank Stevenson <[email protected]>2025-07-23 10:08:41 +0200
committerFrank Stevenson <[email protected]>2025-07-23 10:08:41 +0200
commitcf9856255e1c2abf3ad2164ce669645f5da098c6 (patch)
tree1826bdba99f2ca91670534e2e23355c247482be8
parentd750a8b6b99d7012a1aab21ab67ba530e7a7206b (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.rs101
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)]
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}
76
77impl MsiAutoCalibration {
78 const fn default() -> Self {
79 MsiAutoCalibration::Disabled
80 }
81}
82
83impl Default for MsiAutoCalibration {
84 fn default() -> Self {
85 Self::default()
86 }
87}
88
67#[derive(Clone, Copy)] 89#[derive(Clone, Copy)]
68pub struct Config { 90pub 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
100impl Config { 123impl 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
564fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction { 609fn 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