add manual overclock example, finalize API, cleanup

This commit is contained in:
1-rafael-1 2025-05-01 00:11:56 +02:00
parent d44b945235
commit 22b5f73811
3 changed files with 227 additions and 146 deletions

View File

@ -147,56 +147,30 @@ pub enum PeriClkSrc {
/// Core voltage scaling options for RP2040. /// Core voltage scaling options for RP2040.
/// ///
/// The RP2040 voltage regulator can be configured for different output voltages. /// The RP2040 voltage regulator can be configured for different output voltages.
/// Higher voltages allow for higher clock frequencies but increase power consumption. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
///
/// # Typical Use Cases
///
/// - `V0_85` to `V1_05`: Power saving for lower frequencies (below 100MHz)
/// - `V1_10`: Default voltage, safe for standard 125MHz operation
/// - `V1_15`: Required for frequencies above 133MHz (e.g., 200MHz overclocking)
/// - `V1_20`: For more extreme overclocking (200MHz+)
/// - `V1_25` and `V1_30`: Highest voltage settings, use with caution
///
/// # Overclocking Notes
///
/// When overclocking:
/// - Frequencies up to 133MHz are typically stable at default voltage (`V1_10`)
/// - Frequencies from 133MHz to 200MHz generally require `V1_15`
/// - Frequencies above 200MHz typically require `V1_20` or higher
///
/// # Power Consumption
///
/// Higher voltages increase power consumption and heat generation. In battery-powered
/// applications, consider using lower voltages when maximum performance is not required.
///
/// # Safety
///
/// Increased voltage can reduce the lifespan of the chip if used for extended periods,
/// especially at `V1_25` and `V1_30`. These higher voltages should be used with
/// consideration of thermal management.
#[cfg(feature = "rp2040")] #[cfg(feature = "rp2040")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)] #[repr(u8)]
pub enum VoltageScale { pub enum VoltageScale {
/// 0.85V - Lowest power consumption, suitable for low frequencies /// 0.85V
V0_85 = 0b0110, V0_85 = 0b0110,
/// 0.90V - Low power consumption /// 0.90V
V0_90 = 0b0111, V0_90 = 0b0111,
/// 0.95V - Low power consumption /// 0.95V
V0_95 = 0b1000, V0_95 = 0b1000,
/// 1.00V - Medium power consumption /// 1.00V
V1_00 = 0b1001, V1_00 = 0b1001,
/// 1.05V - Medium power consumption /// 1.05V
V1_05 = 0b1010, V1_05 = 0b1010,
/// 1.10V (Default) - Standard voltage for 125MHz operation /// 1.10V
V1_10 = 0b1011, V1_10 = 0b1011,
/// 1.15V - Required for frequencies above 133MHz /// 1.15V
V1_15 = 0b1100, V1_15 = 0b1100,
/// 1.20V - For higher overclocking (200MHz+) /// 1.20V
V1_20 = 0b1101, V1_20 = 0b1101,
/// 1.25V - High voltage, use with caution /// 1.25V
V1_25 = 0b1110, V1_25 = 0b1110,
/// 1.30V - Maximum voltage, use with extreme caution /// 1.30V
V1_30 = 0b1111, V1_30 = 0b1111,
} }
@ -204,10 +178,8 @@ pub enum VoltageScale {
impl VoltageScale { impl VoltageScale {
/// Get the recommended Brown-Out Detection (BOD) setting for this voltage. /// Get the recommended Brown-Out Detection (BOD) setting for this voltage.
/// Sets the BOD threshold to approximately 90% of the core voltage. /// Sets the BOD threshold to approximately 90% of the core voltage.
/// See RP2040 Datasheet, Table 190, BOD Register
fn recommended_bod(self) -> u8 { fn recommended_bod(self) -> u8 {
match self { match self {
// ~90% of voltage setting based on Table 190 values
VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V)
VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V)
VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V)
@ -246,7 +218,7 @@ pub struct ClockConfig {
#[cfg(feature = "rp2040")] #[cfg(feature = "rp2040")]
pub voltage_scale: Option<VoltageScale>, pub voltage_scale: Option<VoltageScale>,
/// Voltage stabilization delay in microseconds. /// Voltage stabilization delay in microseconds.
/// If not set, appropriate defaults will be used based on voltage level. /// If not set, defaults will be used based on voltage level.
#[cfg(feature = "rp2040")] #[cfg(feature = "rp2040")]
pub voltage_stabilization_delay_us: Option<u32>, pub voltage_stabilization_delay_us: Option<u32>,
// gpin0: Option<(u32, Gpin<'static, AnyPin>)>, // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
@ -409,7 +381,7 @@ impl ClockConfig {
/// Configure the system clock to a specific frequency in MHz. /// Configure the system clock to a specific frequency in MHz.
/// ///
/// This is a more user-friendly way to configure the system clock, similar to /// This is a user-friendly way to configure the system clock, similar to
/// the Pico SDK's approach. It automatically handles voltage scaling based on the /// the Pico SDK's approach. It automatically handles voltage scaling based on the
/// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards. /// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards.
/// ///
@ -420,9 +392,6 @@ impl ClockConfig {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// // Configure for standard 125MHz clock
/// let config = ClockConfig::at_sys_frequency_mhz(125);
///
/// // Overclock to 200MHz /// // Overclock to 200MHz
/// let config = ClockConfig::at_sys_frequency_mhz(200); /// let config = ClockConfig::at_sys_frequency_mhz(200);
/// ``` /// ```
@ -551,6 +520,98 @@ impl ClockConfig {
voltage_stabilization_delay_us: None, voltage_stabilization_delay_us: None,
} }
} }
/// Configure with manual PLL settings for full control over system clock
///
/// This method provides a simple way to configure the system with custom PLL parameters
/// without needing to understand the full nested configuration structure.
///
/// # Arguments
///
/// * `xosc_hz` - The frequency of the external crystal in Hz
/// * `pll_config` - The PLL configuration parameters to achieve desired frequency
/// * `voltage_scale` - Optional voltage scaling for overclocking (required for >133MHz)
///
/// # Returns
///
/// A ClockConfig configured with the specified PLL parameters
///
/// # Example
///
/// ```rust,ignore
/// // Configure for 200MHz operation
/// let config = Config::default();
/// config.clocks = ClockConfig::manual_pll(
/// 12_000_000,
/// PllConfig {
/// refdiv: 1, // Reference divider (12 MHz / 1 = 12 MHz)
/// fbdiv: 100, // Feedback divider (12 MHz * 100 = 1200 MHz VCO)
/// post_div1: 3, // First post divider (1200 MHz / 3 = 400 MHz)
/// post_div2: 2, // Second post divider (400 MHz / 2 = 200 MHz)
/// },
/// Some(VoltageScale::V1_15)
/// );
/// ```
#[cfg(feature = "rp2040")]
pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, voltage_scale: Option<VoltageScale>) -> Self {
// Calculate the actual output frequency for documentation
// let ref_freq = xosc_hz / pll_config.refdiv as u32;
// let vco_freq = ref_freq * pll_config.fbdiv as u32;
// let sys_freq = vco_freq / ((pll_config.post_div1 * pll_config.post_div2) as u32);
// Validate PLL parameters
assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters");
let mut config = Self::default();
config.xosc = Some(XoscConfig {
hz: xosc_hz,
sys_pll: Some(pll_config),
usb_pll: Some(PllConfig {
refdiv: 1,
fbdiv: 120,
post_div1: 6,
post_div2: 5,
}),
delay_multiplier: 128,
});
config.ref_clk = RefClkConfig {
src: RefClkSrc::Xosc,
div: 1,
};
config.sys_clk = SysClkConfig {
src: SysClkSrc::PllSys,
div_int: 1,
div_frac: 0,
};
config.voltage_scale = voltage_scale;
config.peri_clk_src = Some(PeriClkSrc::Sys);
// Set reasonable defaults for other clocks
config.usb_clk = Some(UsbClkConfig {
src: UsbClkSrc::PllUsb,
div: 1,
phase: 0,
});
config.adc_clk = Some(AdcClkConfig {
src: AdcClkSrc::PllUsb,
div: 1,
phase: 0,
});
config.rtc_clk = Some(RtcClkConfig {
src: RtcClkSrc::PllUsb,
div_int: 1024,
div_frac: 0,
phase: 0,
});
config
}
} }
/// ROSC freq range. /// ROSC freq range.
@ -596,30 +657,6 @@ pub struct XoscConfig {
} }
/// PLL configuration. /// PLL configuration.
///
/// This struct defines the parameters used to configure the Phase-Locked Loop (PLL)
/// in the RP2040. The parameters follow the definitions from the RP2040 datasheet,
/// section 2.18.3.
///
/// # Parameters
///
/// * `refdiv` - Reference divider (1-63)
/// * `fbdiv` - VCO feedback divider (16-320)
/// * `post_div1` - First post divider (1-7)
/// * `post_div2` - Second post divider (1-7) - must be less than or equal to post_div1
///
/// # Constraints
///
/// * VCO frequency (input_hz / refdiv * fbdiv) must be between 750MHz and 1800MHz
/// * post_div2 must be less than or equal to post_div1
///
/// # Calculation
///
/// The output frequency of the PLL is calculated as:
///
/// `output_hz = (input_hz / refdiv * fbdiv) / (post_div1 * post_div2)`
///
/// Where input_hz is typically the crystal frequency (e.g., 12MHz).
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct PllConfig { pub struct PllConfig {
/// Reference divisor. /// Reference divisor.
@ -836,35 +873,17 @@ pub struct RtcClkConfig {
/// * `Some(PllConfig)` if valid parameters were found /// * `Some(PllConfig)` if valid parameters were found
/// * `None` if no valid parameters could be found for the requested combination /// * `None` if no valid parameters could be found for the requested combination
/// ///
/// # Algorithm
///
/// 1. Set reference divider to 1 (fixed for simplicity)
/// 2. Try different feedback divisors (fbdiv) starting from highest to lowest
/// 3. For each fbdiv value, check if the resulting VCO frequency is valid (750-1800MHz)
/// 4. Find post-divider combinations that give the exact requested frequency
/// 5. If no exact match, return the closest approximation
///
/// # Example /// # Example
/// ///
/// ``` /// ```
/// // Find parameters for 133MHz system clock from 12MHz crystal /// // Find parameters for 133MHz system clock from 12MHz crystal
/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); /// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap();
/// ``` /// ```
///
/// Similar to the Pico SDK's parameter selection approach, prioritizing stability.
/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL
#[cfg(feature = "rp2040")] #[cfg(feature = "rp2040")]
fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
// Fixed reference divider for system PLL // Fixed reference divider for system PLL
const PLL_SYS_REFDIV: u8 = 1; const PLL_SYS_REFDIV: u8 = 1;
// Constraints from datasheet:
// REFDIV: 1..=63
// FBDIV: 16..=320
// POSTDIV1: 1..=7
// POSTDIV2: 1..=7 (must be <= POSTDIV1)
// VCO frequency (input_hz / refdiv * fbdiv): 750MHz ..= 1800MHz
// Calculate reference frequency // Calculate reference frequency
let reference_freq = input_hz / PLL_SYS_REFDIV as u32; let reference_freq = input_hz / PLL_SYS_REFDIV as u32;
@ -969,8 +988,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
#[cfg(feature = "_rp235x")] #[cfg(feature = "_rp235x")]
while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {} while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}
// Set Core Voltage (RP2040 only) BEFORE doing anything with PLLs // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default
// This is critical for overclocking - must be done before PLL setup
#[cfg(feature = "rp2040")] #[cfg(feature = "rp2040")]
if let Some(voltage) = config.voltage_scale { if let Some(voltage) = config.voltage_scale {
let vreg = pac::VREG_AND_CHIP_RESET; let vreg = pac::VREG_AND_CHIP_RESET;
@ -978,17 +996,15 @@ pub(crate) unsafe fn init(config: ClockConfig) {
let target_vsel = voltage as u8; let target_vsel = voltage as u8;
if target_vsel != current_vsel { if target_vsel != current_vsel {
// IMPORTANT: Use modify() instead of write() to preserve the HIZ and EN bits // Use modify() instead of write() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage
// This is critical - otherwise we might disable the regulator when changing voltage
vreg.vreg().modify(|w| w.set_vsel(target_vsel)); vreg.vreg().modify(|w| w.set_vsel(target_vsel));
// For higher voltage settings (overclocking), we need a longer stabilization time // Wait for the voltage to stabilize. Use the provided delay or default based on voltage
// Default to 1000 µs (1ms) like the SDK, but allow user override
let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {
match voltage { match voltage {
VoltageScale::V1_15 => 1000, // 1ms for 1.15V (matches SDK default) VoltageScale::V1_15 => 1000, // 1ms for 1.15V
VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages
_ => 500, // 500 µs for standard voltages _ => 0, // no delay for all others
} }
}); });
@ -1001,7 +1017,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
// Wait for voltage to stabilize // Wait for voltage to stabilize
cortex_m::asm::delay(delay_cycles); cortex_m::asm::delay(delay_cycles);
// Only NOW set the BOD level after voltage has stabilized // Only now set the BOD level after voltage has stabilized
vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod()));
} }
} }
@ -1026,9 +1042,9 @@ pub(crate) unsafe fn init(config: ClockConfig) {
// SETUP TEMPORARY STABLE CLOCKS FIRST // SETUP TEMPORARY STABLE CLOCKS FIRST
// Configure USB PLL for our stable temporary clock // Configure USB PLL for our stable temporary clock
// This follows the SDK's approach of using USB PLL as a stable intermediate clock // This follows the SDK's approach of using USB PLL as a stable intermediate clock
let usb_pll_freq = match &config.xosc { let pll_usb_freq = match &config.xosc {
Some(config) => match &config.usb_pll { Some(config) => match &config.usb_pll {
Some(usb_pll_config) => { Some(pll_usb_config) => {
// Reset USB PLL // Reset USB PLL
let mut peris = reset::Peripherals(0); let mut peris = reset::Peripherals(0);
peris.set_pll_usb(true); peris.set_pll_usb(true);
@ -1036,7 +1052,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
reset::unreset_wait(peris); reset::unreset_wait(peris);
// Configure the USB PLL - this should give us 48MHz // Configure the USB PLL - this should give us 48MHz
let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *usb_pll_config); let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *pll_usb_config);
CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed); CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed);
usb_pll_freq usb_pll_freq
} }
@ -1122,7 +1138,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
let (src, aux, freq) = match config.sys_clk.src { let (src, aux, freq) = match config.sys_clk.src {
SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq),
SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq),
SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, usb_pll_freq), SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq),
SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq),
SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq),
// SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq),
@ -1185,7 +1201,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
let peri_freq = match src { let peri_freq = match src {
PeriClkSrc::Sys => clk_sys_freq, PeriClkSrc::Sys => clk_sys_freq,
PeriClkSrc::PllSys => pll_sys_freq, PeriClkSrc::PllSys => pll_sys_freq,
PeriClkSrc::PllUsb => usb_pll_freq, PeriClkSrc::PllUsb => pll_usb_freq,
PeriClkSrc::Rosc => rosc_freq, PeriClkSrc::Rosc => rosc_freq,
PeriClkSrc::Xosc => xosc_freq, PeriClkSrc::Xosc => xosc_freq,
// PeriClkSrc::Gpin0 => gpin0_freq, // PeriClkSrc::Gpin0 => gpin0_freq,
@ -1210,7 +1226,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _)); w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));
}); });
let usb_freq = match conf.src { let usb_freq = match conf.src {
UsbClkSrc::PllUsb => usb_pll_freq, UsbClkSrc::PllUsb => pll_usb_freq,
UsbClkSrc::PllSys => pll_sys_freq, UsbClkSrc::PllSys => pll_sys_freq,
UsbClkSrc::Rosc => rosc_freq, UsbClkSrc::Rosc => rosc_freq,
UsbClkSrc::Xosc => xosc_freq, UsbClkSrc::Xosc => xosc_freq,
@ -1234,7 +1250,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _)); w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));
}); });
let adc_in_freq = match conf.src { let adc_in_freq = match conf.src {
AdcClkSrc::PllUsb => usb_pll_freq, AdcClkSrc::PllUsb => pll_usb_freq,
AdcClkSrc::PllSys => pll_sys_freq, AdcClkSrc::PllSys => pll_sys_freq,
AdcClkSrc::Rosc => rosc_freq, AdcClkSrc::Rosc => rosc_freq,
AdcClkSrc::Xosc => xosc_freq, AdcClkSrc::Xosc => xosc_freq,
@ -1262,7 +1278,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _)); w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));
}); });
let rtc_in_freq = match conf.src { let rtc_in_freq = match conf.src {
RtcClkSrc::PllUsb => usb_pll_freq, RtcClkSrc::PllUsb => pll_usb_freq,
RtcClkSrc::PllSys => pll_sys_freq, RtcClkSrc::PllSys => pll_sys_freq,
RtcClkSrc::Rosc => rosc_freq, RtcClkSrc::Rosc => rosc_freq,
RtcClkSrc::Xosc => xosc_freq, RtcClkSrc::Xosc => xosc_freq,
@ -1390,40 +1406,36 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
while !pac::XOSC.status().read().stable() {} while !pac::XOSC.status().read().stable() {}
} }
/// PLL (Phase-Locked Loop) configuration
#[inline(always)] #[inline(always)]
fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 {
// Calculate reference frequency // Calculate reference frequency
let ref_freq = input_freq / config.refdiv as u32; let ref_freq = input_freq / config.refdiv as u32;
// Validate PLL parameters // Validate PLL parameters
assert!( // Feedback divider (FBDIV) must be between 16 and 320
config.fbdiv >= 16 && config.fbdiv <= 320, assert!(config.fbdiv >= 16 && config.fbdiv <= 320);
"fbdiv must be between 16 and 320"
); // Post divider 1 (POSTDIV1) must be between 1 and 7
assert!( assert!(config.post_div1 >= 1 && config.post_div1 <= 7);
config.post_div1 >= 1 && config.post_div1 <= 7,
"post_div1 must be between 1 and 7" // Post divider 2 (POSTDIV2) must be between 1 and 7
); assert!(config.post_div2 >= 1 && config.post_div2 <= 7);
assert!(
config.post_div2 >= 1 && config.post_div2 <= 7, // Post divider 2 (POSTDIV2) must be less than or equal to post divider 1 (POSTDIV1)
"post_div2 must be between 1 and 7" assert!(config.post_div2 <= config.post_div1);
);
assert!(config.post_div2 <= config.post_div1, "post_div2 must be <= post_div1"); // Reference divider (REFDIV) must be between 1 and 63
assert!( assert!(config.refdiv >= 1 && config.refdiv <= 63);
config.refdiv >= 1 && config.refdiv <= 63,
"refdiv must be between 1 and 63" // Reference frequency (REF_FREQ) must be between 5MHz and 800MHz
); assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000);
assert!(
ref_freq >= 5_000_000 && ref_freq <= 800_000_000,
"ref_freq must be between 5MHz and 800MHz"
);
// Calculate VCO frequency // Calculate VCO frequency
let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32);
assert!(
vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000, // VCO (Voltage Controlled Oscillator) frequency must be between 750MHz and 1800MHz
"VCO frequency must be between 750MHz and 1800MHz" assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
);
// We follow the SDK's approach to PLL configuration which is: // We follow the SDK's approach to PLL configuration which is:
// 1. Power down PLL // 1. Power down PLL

View File

@ -1,9 +1,13 @@
//! # Overclocking the RP2040 to 200 MHz
//!
//! This example demonstrates how to configure the RP2040 to run at 200 MHz using a higher level API.
#![no_std] #![no_std]
#![no_main] #![no_main]
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_rp::clocks::{clk_sys_freq, ClockConfig, VoltageScale}; use embassy_rp::clocks::{clk_sys_freq, ClockConfig};
use embassy_rp::config::Config; use embassy_rp::config::Config;
use embassy_rp::gpio::{Level, Output}; use embassy_rp::gpio::{Level, Output};
use embassy_time::{Duration, Instant, Timer}; use embassy_time::{Duration, Instant, Timer};
@ -15,15 +19,10 @@ const COUNT_TO: i64 = 10_000_000;
async fn main(_spawner: Spawner) -> ! { async fn main(_spawner: Spawner) -> ! {
// Set up for clock frequency of 200 MHz // Set up for clock frequency of 200 MHz
// This will set all the necessary defaults including slightly raised voltage // This will set all the necessary defaults including slightly raised voltage
// See embassy_rp::clocks::ClockConfig for more options, including full manual control
let config = Config::new(ClockConfig::at_sys_frequency_mhz(200)); let config = Config::new(ClockConfig::at_sys_frequency_mhz(200));
// Show the voltage scale and brownout-detection for verification // Show the voltage scale for verification
info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale)); info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale));
// info!(
// "Brownout detection: {}",
// Debug2Format(&config.clocks.brownout_detection)
// );
// Initialize the peripherals // Initialize the peripherals
let p = embassy_rp::init(config); let p = embassy_rp::init(config);
@ -64,14 +63,3 @@ async fn main(_spawner: Spawner) -> ! {
Timer::after(Duration::from_secs(2)).await; Timer::after(Duration::from_secs(2)).await;
} }
} }
// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage(125, Some(VoltageScale::V1_10)));
// let config = Config::default();
// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage_extended_delay(
// 200, // Standard 125MHz clock
// Some(VoltageScale::V1_15), // 1.15V voltage
// Some(1000), // 1000μs (1ms) stabilization delay - significantly longer than default
// ));
// Initialize the peripherals
// let p = embassy_rp::init(Default::default()); //testing the bog standard

View File

@ -0,0 +1,81 @@
//! # Overclocking the RP2040 to 200 MHz manually
//!
//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz.
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::clocks;
use embassy_rp::clocks::{ClockConfig, PllConfig, VoltageScale};
use embassy_rp::config::Config;
use embassy_rp::gpio::{Level, Output};
use embassy_time::{Duration, Instant, Timer};
use {defmt_rtt as _, panic_probe as _};
const COUNT_TO: i64 = 10_000_000;
/// Configure the RP2040 for 200 MHz operation by manually specifying
/// all the required parameters instead of using higher-level APIs.
fn configure_manual_overclock() -> Config {
// Set the PLL configuration manually, starting from default values
let mut config = Config::default();
// Set the system clock to 200 MHz using a PLL with a reference frequency of 12 MHz
config.clocks = ClockConfig::manual_pll(
12_000_000,
PllConfig {
refdiv: 1,
fbdiv: 100,
post_div1: 3,
post_div2: 2,
},
// For 200 MHz, we need a voltage scale of 1.15V
Some(VoltageScale::V1_15),
);
config
}
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
// Initialize with our manual overclock configuration
let p = embassy_rp::init(configure_manual_overclock());
// Verify the actual system clock frequency
let sys_freq = clocks::clk_sys_freq();
info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
// LED to indicate the system is running
let mut led = Output::new(p.PIN_25, Level::Low);
loop {
// Reset the counter at the start of measurement period
let mut counter = 0;
// Turn LED on while counting
led.set_high();
let start = Instant::now();
// This is a busy loop that will take some time to complete
while counter < COUNT_TO {
counter += 1;
}
let elapsed = Instant::now() - start;
// Report the elapsed time
led.set_low();
info!(
"At {}Mhz: Elapsed time to count to {}: {}ms",
sys_freq / 1_000_000,
counter,
elapsed.as_millis()
);
// Wait 2 seconds before starting the next measurement
Timer::after(Duration::from_secs(2)).await;
}
}