aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------embassy-rp/sdk examples0
-rw-r--r--embassy-rp/src/clocks.rs546
-rw-r--r--examples/rp/src/bin/overclock.rs13
3 files changed, 351 insertions, 208 deletions
diff --git a/embassy-rp/sdk examples b/embassy-rp/sdk examples
new file mode 160000
Subproject ee68c78d0afae2b69c03ae1a72bf5cc267a2d94
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index b74e90f5e..1f5c27df1 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -1,4 +1,78 @@
1//! Clock configuration for the RP2040 1//! # Clock configuration for the RP2040 and RP235x microcontrollers.
2//!
3//! # Clock Configuration API
4//!
5//! This module provides both high-level convenience functions and low-level manual
6//! configuration options for the RP2040 clock system.
7//!
8//! ## High-Level Convenience Functions
9//!
10//! For most users, these functions provide an easy way to configure clocks:
11//!
12//! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock
13//! - `ClockConfig::at_sys_frequency_mhz(200)` - Set system clock to a specific frequency with automatic voltage scaling
14//! - `ClockConfig::with_external_crystal(16_000_000)` - Configure with a non-standard crystal frequency
15//!
16//! ## Manual Configuration
17//!
18//! For advanced users who need precise control:
19//!
20//! ```rust,ignore
21//! // Start with default configuration and customize it
22//! let mut config = ClockConfig::default();
23//!
24//! // Set custom PLL parameters
25//! config.xosc = Some(XoscConfig {
26//! hz: 12_000_000,
27//! sys_pll: Some(PllConfig {
28//! refdiv: 1,
29//! fbdiv: 200,
30//! post_div1: 6,
31//! post_div2: 2,
32//! }),
33//! // ... other fields
34//! });
35//!
36//! // Set voltage for overclocking
37//! config.voltage_scale = Some(VoltageScale::V1_15);
38//! ```
39//!
40//! ## Voltage Scaling for Overclocking (RP2040 only)
41//!
42//! When overclocking beyond 133MHz, higher core voltages are needed:
43//!
44//! - Up to 133MHz: `VoltageScale::V1_10` (default)
45//! - 133-200MHz: `VoltageScale::V1_15`
46//! - Above 200MHz: `VoltageScale::V1_20` or higher
47//!
48//! The `at_sys_frequency_mhz()` function automatically sets appropriate voltages.
49//!
50//! ## Examples
51//!
52//! ### Standard 125MHz configuration
53//! ```rust,ignore
54//! let config = ClockConfig::crystal(12_000_000);
55//! ```
56//!
57//! Or using the default configuration:
58//! ```rust,ignore
59//! let config = ClockConfig::default();
60//! ```
61//!
62//! ### Overclock to 200MHz
63//! ```rust,ignore
64//! let config = ClockConfig::at_sys_frequency_mhz(200);
65//! ```
66//!
67//! ### Manual configuration for advanced scenarios
68//! ```rust,ignore
69//! use embassy_rp::clocks::{ClockConfig, XoscConfig, PllConfig, VoltageScale};
70//!
71//! // Start with defaults and customize
72//! let mut config = ClockConfig::default();
73//! config.voltage_scale = Some(VoltageScale::V1_15);
74//! // Set other parameters as needed...
75//! ```
2 76
3#[cfg(feature = "rp2040")] 77#[cfg(feature = "rp2040")]
4use core::arch::asm; 78use core::arch::asm;
@@ -18,6 +92,7 @@ use crate::{pac, reset, Peri};
18// gpin is not usually safe to use during the boot init() call, so it won't 92// gpin is not usually safe to use during the boot init() call, so it won't
19// be very useful until we have runtime clock reconfiguration. once this 93// be very useful until we have runtime clock reconfiguration. once this
20// happens we can resurrect the commented-out gpin bits. 94// happens we can resurrect the commented-out gpin bits.
95
21struct Clocks { 96struct Clocks {
22 xosc: AtomicU32, 97 xosc: AtomicU32,
23 sys: AtomicU32, 98 sys: AtomicU32,
@@ -70,30 +145,58 @@ pub enum PeriClkSrc {
70} 145}
71 146
72/// Core voltage scaling options for RP2040. 147/// Core voltage scaling options for RP2040.
73/// See RP2040 Datasheet, Table 189, VREG Register. 148///
149/// The RP2040 voltage regulator can be configured for different output voltages.
150/// Higher voltages allow for higher clock frequencies but increase power consumption.
151///
152/// # Typical Use Cases
153///
154/// - `V0_85` to `V1_05`: Power saving for lower frequencies (below 100MHz)
155/// - `V1_10`: Default voltage, safe for standard 125MHz operation
156/// - `V1_15`: Required for frequencies above 133MHz (e.g., 200MHz overclocking)
157/// - `V1_20`: For more extreme overclocking (200MHz+)
158/// - `V1_25` and `V1_30`: Highest voltage settings, use with caution
159///
160/// # Overclocking Notes
161///
162/// When overclocking:
163/// - Frequencies up to 133MHz are typically stable at default voltage (`V1_10`)
164/// - Frequencies from 133MHz to 200MHz generally require `V1_15`
165/// - Frequencies above 200MHz typically require `V1_20` or higher
166///
167/// # Power Consumption
168///
169/// Higher voltages increase power consumption and heat generation. In battery-powered
170/// applications, consider using lower voltages when maximum performance is not required.
171///
172/// # Safety
173///
174/// Increased voltage can reduce the lifespan of the chip if used for extended periods,
175/// especially at `V1_25` and `V1_30`. These higher voltages should be used with
176/// consideration of thermal management.
74#[cfg(feature = "rp2040")] 177#[cfg(feature = "rp2040")]
75#[derive(Clone, Copy, Debug, PartialEq, Eq)] 178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76#[repr(u8)] 179#[repr(u8)]
77pub enum VoltageScale { 180pub enum VoltageScale {
78 /// 0.85V 181 /// 0.85V - Lowest power consumption, suitable for low frequencies
79 V0_85 = 0b0110, 182 V0_85 = 0b0110,
80 /// 0.90V 183 /// 0.90V - Low power consumption
81 V0_90 = 0b0111, 184 V0_90 = 0b0111,
82 /// 0.95V 185 /// 0.95V - Low power consumption
83 V0_95 = 0b1000, 186 V0_95 = 0b1000,
84 /// 1.00V 187 /// 1.00V - Medium power consumption
85 V1_00 = 0b1001, 188 V1_00 = 0b1001,
86 /// 1.05V 189 /// 1.05V - Medium power consumption
87 V1_05 = 0b1010, 190 V1_05 = 0b1010,
88 /// 1.10V (Default) 191 /// 1.10V (Default) - Standard voltage for 125MHz operation
89 V1_10 = 0b1011, 192 V1_10 = 0b1011,
90 /// 1.15V 193 /// 1.15V - Required for frequencies above 133MHz
91 V1_15 = 0b1100, 194 V1_15 = 0b1100,
92 /// 1.20V 195 /// 1.20V - For higher overclocking (200MHz+)
93 V1_20 = 0b1101, 196 V1_20 = 0b1101,
94 /// 1.25V 197 /// 1.25V - High voltage, use with caution
95 V1_25 = 0b1110, 198 V1_25 = 0b1110,
96 /// 1.30V 199 /// 1.30V - Maximum voltage, use with extreme caution
97 V1_30 = 0b1111, 200 V1_30 = 0b1111,
98} 201}
99 202
@@ -143,122 +246,58 @@ pub struct ClockConfig {
143 #[cfg(feature = "rp2040")] 246 #[cfg(feature = "rp2040")]
144 pub voltage_scale: Option<VoltageScale>, 247 pub voltage_scale: Option<VoltageScale>,
145 /// Voltage stabilization delay in microseconds. 248 /// Voltage stabilization delay in microseconds.
249 /// If not set, appropriate defaults will be used based on voltage level.
146 #[cfg(feature = "rp2040")] 250 #[cfg(feature = "rp2040")]
147 pub voltage_stabilization_delay_us: Option<u32>, 251 pub voltage_stabilization_delay_us: Option<u32>,
148 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, 252 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
149 // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, 253 // gpin1: Option<(u32, Gpin<'static, AnyPin>)>,
150} 254}
151 255
152impl ClockConfig { 256impl Default for ClockConfig {
153 /// Clock configuration derived from external crystal. 257 /// Creates a minimal default configuration with safe values.
154 pub fn crystal(crystal_hz: u32) -> Self { 258 ///
259 /// This configuration uses the ring oscillator (ROSC) as the clock source
260 /// and sets minimal defaults that guarantee a working system. It's intended
261 /// as a starting point for manual configuration.
262 ///
263 /// Most users should use one of the more specific configuration functions:
264 /// - `ClockConfig::crystal()` - Standard configuration with external crystal
265 /// - `ClockConfig::rosc()` - Configuration using only the internal oscillator
266 /// - `ClockConfig::at_sys_frequency_mhz()` - Configuration for a specific system frequency
267 fn default() -> Self {
155 Self { 268 Self {
156 rosc: Some(RoscConfig { 269 rosc: None,
157 hz: 6_500_000, 270 xosc: None,
158 range: RoscRange::Medium,
159 drive_strength: [0; 8],
160 div: 16,
161 }),
162 xosc: Some(XoscConfig {
163 hz: crystal_hz,
164 sys_pll: Some(PllConfig {
165 refdiv: 1,
166 fbdiv: 125,
167 #[cfg(feature = "rp2040")]
168 post_div1: 6,
169 #[cfg(feature = "_rp235x")]
170 post_div1: 5,
171 post_div2: 2,
172 }),
173 usb_pll: Some(PllConfig {
174 refdiv: 1,
175 fbdiv: 120,
176 post_div1: 6,
177 post_div2: 5,
178 }),
179 delay_multiplier: 128,
180 }),
181 ref_clk: RefClkConfig { 271 ref_clk: RefClkConfig {
182 src: RefClkSrc::Xosc, 272 src: RefClkSrc::Rosc,
183 div: 1, 273 div: 1,
184 }, 274 },
185 sys_clk: SysClkConfig { 275 sys_clk: SysClkConfig {
186 src: SysClkSrc::PllSys, 276 src: SysClkSrc::Rosc,
187 div_int: 1, 277 div_int: 1,
188 div_frac: 0, 278 div_frac: 0,
189 }, 279 },
190 peri_clk_src: Some(PeriClkSrc::Sys), 280 peri_clk_src: None,
191 // CLK USB = PLL USB (48MHz) / 1 = 48MHz 281 usb_clk: None,
192 usb_clk: Some(UsbClkConfig { 282 adc_clk: None,
193 src: UsbClkSrc::PllUsb,
194 div: 1,
195 phase: 0,
196 }),
197 // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
198 adc_clk: Some(AdcClkConfig {
199 src: AdcClkSrc::PllUsb,
200 div: 1,
201 phase: 0,
202 }),
203 // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
204 #[cfg(feature = "rp2040")] 283 #[cfg(feature = "rp2040")]
205 rtc_clk: Some(RtcClkConfig { 284 rtc_clk: None,
206 src: RtcClkSrc::PllUsb,
207 div_int: 1024,
208 div_frac: 0,
209 phase: 0,
210 }),
211 #[cfg(feature = "rp2040")] 285 #[cfg(feature = "rp2040")]
212 voltage_scale: None, // Use hardware default (1.10V) 286 voltage_scale: None,
213 #[cfg(feature = "rp2040")] 287 #[cfg(feature = "rp2040")]
214 voltage_stabilization_delay_us: None, 288 voltage_stabilization_delay_us: None,
215 // gpin0: None, 289 // gpin0: None,
216 // gpin1: None, 290 // gpin1: None,
217 } 291 }
218 } 292 }
293}
219 294
220 /// Clock configuration derived from external crystal, targeting a specific SYS clock frequency for RP2040. 295impl ClockConfig {
221 /// 296 /// Clock configuration derived from external crystal.
222 /// This function calculates the required PLL settings and core voltage based on the target frequency.
223 ///
224 /// # Arguments
225 ///
226 /// * `crystal_hz`: The frequency of the external crystal (e.g., 12_000_000 for 12MHz).
227 /// * `sys_freq_hz`: The target system clock frequency.
228 ///
229 /// # Panics
230 ///
231 /// Panics if the requested frequency is impossible to achieve with the given crystal,
232 /// or if the required voltage for the frequency is not supported or known.
233 ///
234 /// # Safety Notes (RP2040)
235 /// 297 ///
236 /// * Frequencies > 133MHz require increased core voltage. 298 /// This uses default settings for most parameters, suitable for typical use cases.
237 /// * This function automatically selects `VoltageScale::V1_15` for frequencies > 133MHz and <= 200MHz. 299 /// For manual control of PLL parameters, use `new_manual()` or modify the struct fields directly.
238 /// * Frequencies > 200MHz might require `VoltageScale::V1_20` or higher and are considered overclocking beyond datasheet recommendations. 300 pub fn crystal(crystal_hz: u32) -> Self {
239 /// This function will select `VoltageScale::V1_20` for frequencies > 200MHz, use with caution.
240 /// * Ensure your hardware supports the selected voltage and frequency.
241 #[cfg(feature = "rp2040")]
242 pub fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self {
243 // Determine required voltage based on target frequency
244 let voltage_scale = match sys_freq_hz {
245 0..=133_000_000 => VoltageScale::V1_10, // Default voltage is sufficient
246 133_000_001..=200_000_000 => VoltageScale::V1_15, // Requires 1.15V
247 _ => VoltageScale::V1_20, // Frequencies > 200MHz require at least 1.20V (Overclocking)
248 };
249
250 // Find suitable PLL parameters
251 let pll_params = match find_pll_params(crystal_hz, sys_freq_hz) {
252 Some(params) => params,
253 None => {
254 // If we can't find valid parameters for the requested frequency,
255 // fall back to safe defaults (125 MHz for RP2040)
256 let safe_freq = 125_000_000; // Safe default frequency
257 find_pll_params(crystal_hz, safe_freq)
258 .expect("Could not find valid PLL parameters even for safe default frequency")
259 }
260 };
261
262 Self { 301 Self {
263 rosc: Some(RoscConfig { 302 rosc: Some(RoscConfig {
264 hz: 6_500_000, 303 hz: 6_500_000,
@@ -268,8 +307,15 @@ impl ClockConfig {
268 }), 307 }),
269 xosc: Some(XoscConfig { 308 xosc: Some(XoscConfig {
270 hz: crystal_hz, 309 hz: crystal_hz,
271 sys_pll: Some(pll_params), 310 sys_pll: Some(PllConfig {
272 // Keep USB PLL at 48MHz for compatibility 311 refdiv: 1,
312 fbdiv: 125,
313 #[cfg(feature = "rp2040")]
314 post_div1: 6,
315 #[cfg(feature = "_rp235x")]
316 post_div1: 5,
317 post_div2: 2,
318 }),
273 usb_pll: Some(PllConfig { 319 usb_pll: Some(PllConfig {
274 refdiv: 1, 320 refdiv: 1,
275 fbdiv: 120, 321 fbdiv: 120,
@@ -288,23 +334,28 @@ impl ClockConfig {
288 div_frac: 0, 334 div_frac: 0,
289 }, 335 },
290 peri_clk_src: Some(PeriClkSrc::Sys), 336 peri_clk_src: Some(PeriClkSrc::Sys),
337 // CLK USB = PLL USB (48MHz) / 1 = 48MHz
291 usb_clk: Some(UsbClkConfig { 338 usb_clk: Some(UsbClkConfig {
292 src: UsbClkSrc::PllUsb, 339 src: UsbClkSrc::PllUsb,
293 div: 1, 340 div: 1,
294 phase: 0, 341 phase: 0,
295 }), 342 }),
343 // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
296 adc_clk: Some(AdcClkConfig { 344 adc_clk: Some(AdcClkConfig {
297 src: AdcClkSrc::PllUsb, 345 src: AdcClkSrc::PllUsb,
298 div: 1, 346 div: 1,
299 phase: 0, 347 phase: 0,
300 }), 348 }),
349 // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
350 #[cfg(feature = "rp2040")]
301 rtc_clk: Some(RtcClkConfig { 351 rtc_clk: Some(RtcClkConfig {
302 src: RtcClkSrc::PllUsb, 352 src: RtcClkSrc::PllUsb,
303 div_int: 1024, 353 div_int: 1024,
304 div_frac: 0, 354 div_frac: 0,
305 phase: 0, 355 phase: 0,
306 }), 356 }),
307 voltage_scale: Some(voltage_scale), 357 #[cfg(feature = "rp2040")]
358 voltage_scale: None, // Use hardware default (1.10V)
308 #[cfg(feature = "rp2040")] 359 #[cfg(feature = "rp2040")]
309 voltage_stabilization_delay_us: None, 360 voltage_stabilization_delay_us: None,
310 // gpin0: None, 361 // gpin0: None,
@@ -366,22 +417,17 @@ impl ClockConfig {
366 /// 417 ///
367 /// * `sys_freq_mhz` - The target system clock frequency in MHz 418 /// * `sys_freq_mhz` - The target system clock frequency in MHz
368 /// 419 ///
369 /// # Safety Notes
370 ///
371 /// * Frequencies > 133MHz require increased core voltage and are considered overclocking.
372 /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips.
373 ///
374 /// # Example 420 /// # Example
375 /// 421 ///
376 /// ``` 422 /// ```
377 /// // Configure for standard 125MHz clock 423 /// // Configure for standard 125MHz clock
378 /// let config = ClockConfig::with_speed_mhz(125); 424 /// let config = ClockConfig::at_sys_frequency_mhz(125);
379 /// 425 ///
380 /// // Overclock to 200MHz (requires higher voltage) 426 /// // Overclock to 200MHz
381 /// let config = ClockConfig::with_speed_mhz(200); 427 /// let config = ClockConfig::at_sys_frequency_mhz(200);
382 /// ``` 428 /// ```
383 #[cfg(feature = "rp2040")] 429 #[cfg(feature = "rp2040")]
384 pub fn with_speed_mhz(sys_freq_mhz: u32) -> Self { 430 pub fn at_sys_frequency_mhz(sys_freq_mhz: u32) -> Self {
385 // For 125MHz, use exactly the same config as the default to avoid any differences 431 // For 125MHz, use exactly the same config as the default to avoid any differences
386 if sys_freq_mhz == 125 { 432 if sys_freq_mhz == 125 {
387 return Self::crystal(12_000_000); 433 return Self::crystal(12_000_000);
@@ -392,12 +438,7 @@ impl ClockConfig {
392 const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; 438 const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
393 439
394 let sys_freq_hz = sys_freq_mhz * 1_000_000; 440 let sys_freq_hz = sys_freq_mhz * 1_000_000;
395 let mut config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz); 441 let config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz);
396
397 // For frequencies above 200MHz, ensure we're using the highest voltage
398 if sys_freq_mhz > 200 {
399 config.voltage_scale = Some(VoltageScale::V1_20);
400 }
401 442
402 config 443 config
403 } 444 }
@@ -412,11 +453,6 @@ impl ClockConfig {
412 /// * `crystal_hz` - The frequency of the external crystal in Hz 453 /// * `crystal_hz` - The frequency of the external crystal in Hz
413 /// * `sys_freq_hz` - The target system clock frequency in Hz 454 /// * `sys_freq_hz` - The target system clock frequency in Hz
414 /// 455 ///
415 /// # Safety Notes
416 ///
417 /// * Frequencies > 133MHz require increased core voltage and are considered overclocking.
418 /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips.
419 ///
420 /// # Example 456 /// # Example
421 /// 457 ///
422 /// ``` 458 /// ```
@@ -428,58 +464,93 @@ impl ClockConfig {
428 Self::crystal_freq(crystal_hz, sys_freq_hz) 464 Self::crystal_freq(crystal_hz, sys_freq_hz)
429 } 465 }
430 466
431 #[cfg(feature = "rp2040")] 467 /// Configure clocks derived from an external crystal with specific system frequency.
432 pub fn with_speed_mhz_test_voltage(sys_freq_mhz: u32, voltage: Option<VoltageScale>) -> Self {
433 // Create a config with the requested frequency
434 let sys_freq_hz = sys_freq_mhz * 1_000_000;
435 let mut config = Self::crystal_freq(12_000_000, sys_freq_hz);
436
437 // Override the voltage setting
438 config.voltage_scale = voltage;
439
440 // For debugging
441 // println!("Debug: Setting freq to {}MHz with voltage {:?}", sys_freq_mhz, voltage);
442
443 config
444 }
445
446 /// Similar to `with_speed_mhz_test_voltage` but with an extended voltage stabilization delay.
447 /// 468 ///
448 /// This function is useful for testing voltage stability issues where the default delay 469 /// This function calculates optimal PLL parameters to achieve the requested system
449 /// may not be sufficient for the voltage regulator to fully stabilize. 470 /// frequency from the given crystal frequency. It's used internally by higher-level
471 /// configuration functions.
450 /// 472 ///
451 /// # Arguments 473 /// # Arguments
452 /// 474 ///
453 /// * `sys_freq_mhz` - The target system clock frequency in MHz 475 /// * `crystal_hz` - The frequency of the external crystal in Hz
454 /// * `voltage` - The desired voltage scale setting 476 /// * `sys_freq_hz` - The desired system clock frequency in Hz
455 /// * `stabilization_delay_us` - Voltage stabilization delay in microseconds (default: 500μs) 477 ///
478 /// # Returns
479 ///
480 /// A ClockConfig configured to achieve the requested system frequency using the
481 /// specified crystal, or panic if no valid parameters can be found.
456 #[cfg(feature = "rp2040")] 482 #[cfg(feature = "rp2040")]
457 pub fn with_speed_mhz_test_voltage_extended_delay( 483 fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self {
458 sys_freq_mhz: u32, 484 // Find optimal PLL parameters for the requested frequency
459 voltage: Option<VoltageScale>, 485 let sys_pll_params = find_pll_params(crystal_hz, sys_freq_hz)
460 stabilization_delay_us: Option<u32>, 486 .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock"));
461 ) -> Self {
462 // Create a config with the requested frequency
463 let sys_freq_hz = sys_freq_mhz * 1_000_000;
464 let mut config = Self::crystal_freq(12_000_000, sys_freq_hz);
465 487
466 // Override the voltage setting 488 // Set the voltage scale based on the target frequency
467 config.voltage_scale = voltage; 489 // Higher frequencies require higher voltage
490 let voltage_scale = match sys_freq_hz {
491 freq if freq > 200_000_000 => Some(VoltageScale::V1_20),
492 freq if freq > 133_000_000 => Some(VoltageScale::V1_15),
493 _ => None, // Use default voltage (V1_10)
494 };
468 495
469 // Add a custom voltage stabilization delay 496 // For USB PLL, we always want 48MHz for USB
470 config.voltage_stabilization_delay_us = stabilization_delay_us; 497 let usb_pll_params = if crystal_hz == 12_000_000 {
498 // For standard 12MHz crystal, use the default parameters
499 PllConfig {
500 refdiv: 1,
501 fbdiv: 120,
502 post_div1: 6,
503 post_div2: 5,
504 }
505 } else {
506 // For other crystals, calculate parameters to get 48MHz
507 find_pll_params(crystal_hz, 48_000_000)
508 .unwrap_or_else(|| panic!("Could not find valid PLL parameters for USB clock"))
509 };
471 510
472 config 511 Self {
512 rosc: Some(RoscConfig {
513 hz: 6_500_000,
514 range: RoscRange::Medium,
515 drive_strength: [0; 8],
516 div: 16,
517 }),
518 xosc: Some(XoscConfig {
519 hz: crystal_hz,
520 sys_pll: Some(sys_pll_params),
521 usb_pll: Some(usb_pll_params),
522 delay_multiplier: 128,
523 }),
524 ref_clk: RefClkConfig {
525 src: RefClkSrc::Xosc,
526 div: 1,
527 },
528 sys_clk: SysClkConfig {
529 src: SysClkSrc::PllSys,
530 div_int: 1,
531 div_frac: 0,
532 },
533 peri_clk_src: Some(PeriClkSrc::Sys),
534 usb_clk: Some(UsbClkConfig {
535 src: UsbClkSrc::PllUsb,
536 div: 1,
537 phase: 0,
538 }),
539 adc_clk: Some(AdcClkConfig {
540 src: AdcClkSrc::PllUsb,
541 div: 1,
542 phase: 0,
543 }),
544 rtc_clk: Some(RtcClkConfig {
545 src: RtcClkSrc::PllUsb,
546 div_int: 1024,
547 div_frac: 0,
548 phase: 0,
549 }),
550 voltage_scale,
551 voltage_stabilization_delay_us: None,
552 }
473 } 553 }
474 // pub fn bind_gpin<P: GpinPin>(&mut self, gpin: Gpin<'static, P>, hz: u32) {
475 // match P::NR {
476 // 0 => self.gpin0 = Some((hz, gpin.into())),
477 // 1 => self.gpin1 = Some((hz, gpin.into())),
478 // _ => unreachable!(),
479 // }
480 // // pin is now provisionally bound. if the config is applied it must be forgotten,
481 // // or Gpin::drop will deconfigure the clock input.
482 // }
483} 554}
484 555
485/// ROSC freq range. 556/// ROSC freq range.
@@ -525,6 +596,30 @@ pub struct XoscConfig {
525} 596}
526 597
527/// PLL configuration. 598/// PLL configuration.
599///
600/// This struct defines the parameters used to configure the Phase-Locked Loop (PLL)
601/// in the RP2040. The parameters follow the definitions from the RP2040 datasheet,
602/// section 2.18.3.
603///
604/// # Parameters
605///
606/// * `refdiv` - Reference divider (1-63)
607/// * `fbdiv` - VCO feedback divider (16-320)
608/// * `post_div1` - First post divider (1-7)
609/// * `post_div2` - Second post divider (1-7) - must be less than or equal to post_div1
610///
611/// # Constraints
612///
613/// * VCO frequency (input_hz / refdiv * fbdiv) must be between 750MHz and 1800MHz
614/// * post_div2 must be less than or equal to post_div1
615///
616/// # Calculation
617///
618/// The output frequency of the PLL is calculated as:
619///
620/// `output_hz = (input_hz / refdiv * fbdiv) / (post_div1 * post_div2)`
621///
622/// Where input_hz is typically the crystal frequency (e.g., 12MHz).
528#[derive(Clone, Copy, Debug)] 623#[derive(Clone, Copy, Debug)]
529pub struct PllConfig { 624pub struct PllConfig {
530 /// Reference divisor. 625 /// Reference divisor.
@@ -537,6 +632,50 @@ pub struct PllConfig {
537 pub post_div2: u8, 632 pub post_div2: u8,
538} 633}
539 634
635impl PllConfig {
636 /// Calculate the output frequency for this PLL configuration
637 /// given an input frequency.
638 pub fn output_frequency(&self, input_hz: u32) -> u32 {
639 let ref_freq = input_hz / self.refdiv as u32;
640 let vco_freq = ref_freq * self.fbdiv as u32;
641 vco_freq / ((self.post_div1 * self.post_div2) as u32)
642 }
643
644 /// Check if this PLL configuration is valid for the given input frequency.
645 pub fn is_valid(&self, input_hz: u32) -> bool {
646 // Check divisor constraints
647 if self.refdiv < 1 || self.refdiv > 63 {
648 return false;
649 }
650 if self.fbdiv < 16 || self.fbdiv > 320 {
651 return false;
652 }
653 if self.post_div1 < 1 || self.post_div1 > 7 {
654 return false;
655 }
656 if self.post_div2 < 1 || self.post_div2 > 7 {
657 return false;
658 }
659 if self.post_div2 > self.post_div1 {
660 return false;
661 }
662
663 // Calculate reference frequency
664 let ref_freq = input_hz / self.refdiv as u32;
665
666 // Check reference frequency range
667 if ref_freq < 5_000_000 || ref_freq > 800_000_000 {
668 return false;
669 }
670
671 // Calculate VCO frequency
672 let vco_freq = ref_freq * self.fbdiv as u32;
673
674 // Check VCO frequency range
675 vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000
676 }
677}
678
540/// Reference clock config. 679/// Reference clock config.
541pub struct RefClkConfig { 680pub struct RefClkConfig {
542 /// Reference clock source. 681 /// Reference clock source.
@@ -683,6 +822,35 @@ pub struct RtcClkConfig {
683/// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency 822/// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency
684/// based on the input frequency. 823/// based on the input frequency.
685/// 824///
825/// This function searches for the best PLL configuration to achieve the requested target frequency
826/// while staying within the VCO frequency range of 750MHz to 1800MHz. It prioritizes stability
827/// over exact frequency matching by using larger divisors where possible.
828///
829/// # Parameters
830///
831/// * `input_hz`: The input frequency in Hz (typically the crystal frequency, e.g. 12MHz)
832/// * `target_hz`: The desired output frequency in Hz (e.g. 125MHz for standard RP2040 operation)
833///
834/// # Returns
835///
836/// * `Some(PllConfig)` if valid parameters were found
837/// * `None` if no valid parameters could be found for the requested combination
838///
839/// # Algorithm
840///
841/// 1. Set reference divider to 1 (fixed for simplicity)
842/// 2. Try different feedback divisors (fbdiv) starting from highest to lowest
843/// 3. For each fbdiv value, check if the resulting VCO frequency is valid (750-1800MHz)
844/// 4. Find post-divider combinations that give the exact requested frequency
845/// 5. If no exact match, return the closest approximation
846///
847/// # Example
848///
849/// ```
850/// // Find parameters for 133MHz system clock from 12MHz crystal
851/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap();
852/// ```
853///
686/// Similar to the Pico SDK's parameter selection approach, prioritizing stability. 854/// Similar to the Pico SDK's parameter selection approach, prioritizing stability.
687/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL 855/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL
688#[cfg(feature = "rp2040")] 856#[cfg(feature = "rp2040")]
@@ -766,43 +934,6 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
766 best_config 934 best_config
767} 935}
768 936
769#[cfg(feature = "rp2040")]
770pub fn compare_pll_params() {
771 // Parameters from default configuration
772 let default_params = PllConfig {
773 refdiv: 1,
774 fbdiv: 125,
775 post_div1: 6,
776 post_div2: 2,
777 };
778
779 // Calculate parameters using our find_pll_params function
780 let crystal_hz = 12_000_000;
781 let target_hz = 125_000_000;
782 let calculated_params = find_pll_params(crystal_hz, target_hz).expect("Failed to find PLL parameters");
783
784 // Check if they're identical
785 let params_match = default_params.refdiv == calculated_params.refdiv
786 && default_params.fbdiv == calculated_params.fbdiv
787 && default_params.post_div1 == calculated_params.post_div1
788 && default_params.post_div2 == calculated_params.post_div2;
789
790 // Here we'd normally print results, but without a console we'll just
791 // use this for debugging in our IDE
792 let _default_output_freq = crystal_hz / default_params.refdiv as u32 * default_params.fbdiv as u32
793 / (default_params.post_div1 * default_params.post_div2) as u32;
794
795 let _calculated_output_freq = crystal_hz / calculated_params.refdiv as u32 * calculated_params.fbdiv as u32
796 / (calculated_params.post_div1 * calculated_params.post_div2) as u32;
797
798 // Parameters: default vs calculated
799 // refdiv: 1 vs {calculated_params.refdiv}
800 // fbdiv: 125 vs {calculated_params.fbdiv}
801 // post_div1: 6 vs {calculated_params.post_div1}
802 // post_div2: 2 vs {calculated_params.post_div2}
803 // params_match: {params_match}
804}
805
806/// safety: must be called exactly once at bootup 937/// safety: must be called exactly once at bootup
807pub(crate) unsafe fn init(config: ClockConfig) { 938pub(crate) unsafe fn init(config: ClockConfig) {
808 // Reset everything except: 939 // Reset everything except:
@@ -1603,6 +1734,7 @@ impl rand_core::RngCore for RoscRng {
1603 dest.fill_with(Self::next_u8) 1734 dest.fill_with(Self::next_u8)
1604 } 1735 }
1605} 1736}
1737
1606/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks 1738/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks
1607/// and can only be exited through resets, dormant-wake GPIO interrupts, 1739/// and can only be exited through resets, dormant-wake GPIO interrupts,
1608/// and RTC interrupts. If RTC is clocked from an internal clock source 1740/// and RTC interrupts. If RTC is clocked from an internal clock source
diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs
index db6c8f448..9027f1516 100644
--- a/examples/rp/src/bin/overclock.rs
+++ b/examples/rp/src/bin/overclock.rs
@@ -14,7 +14,18 @@ const COUNT_TO: i64 = 10_000_000;
14#[embassy_executor::main] 14#[embassy_executor::main]
15async fn main(_spawner: Spawner) -> ! { 15async fn main(_spawner: Spawner) -> ! {
16 // Set up for clock frequency of 200 MHz 16 // Set up for clock frequency of 200 MHz
17 let config = Config::new(ClockConfig::with_speed_mhz(200)); 17 // This will set all the necessary defaults including slightly raised voltage
18 // See embassy_rp::clocks::ClockConfig for more options, including full manual control
19 let config = Config::new(ClockConfig::at_sys_frequency_mhz(200));
20
21 // Show the voltage scale and brownout-detection for verification
22 info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale));
23 // info!(
24 // "Brownout detection: {}",
25 // Debug2Format(&config.clocks.brownout_detection)
26 // );
27
28 // Initialize the peripherals
18 let p = embassy_rp::init(config); 29 let p = embassy_rp::init(config);
19 30
20 // Show CPU frequency for verification 31 // Show CPU frequency for verification