2021-05-01 23:18:37 +02:00
|
|
|
//! Clocks (CLOCKS)
|
2021-07-06 17:30:44 +02:00
|
|
|
//!
|
|
|
|
//!
|
|
|
|
//!
|
|
|
|
//! Usage:
|
|
|
|
//! ```rust
|
|
|
|
//! let mut p = rp2040_pac::Peripherals::take().unwrap();
|
|
|
|
//! let mut watchdog = Watchdog::new(p.WATCHDOG);
|
|
|
|
//! let mut clocks = ClocksManager::new(p.CLOCKS, &mut watchdog);
|
|
|
|
//! // Enable the xosc
|
|
|
|
//! let xosc = setup_xosc_blocking(p.XOSC, XOSC_MHZ.Hz()).ok().unwrap();
|
|
|
|
//!
|
|
|
|
//! // Configure PLLs
|
|
|
|
//! // REF FBDIV VCO POSTDIV
|
|
|
|
//! // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
|
|
|
|
//! // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
|
2021-07-08 12:58:48 +02:00
|
|
|
//! let pll_sys = setup_pll_blocking(p.PLL_SYS, 12.MHz().into(), PLL_SYS_125MHZ, &mut clocks, &mut p.RESETS).ok().unwrap();
|
|
|
|
//! let pll_usb = setup_pll_blocking(p.PLL_USB, 12.MHz().into(), PLL_USB_48MHZ, &mut clocks, &mut p.RESETS).ok().unwrap();
|
|
|
|
//!
|
|
|
|
//! // Configure clocks
|
|
|
|
//! // CLK_REF = XOSC (12MHz) / 1 = 12MHz
|
|
|
|
//! let mut ref_clock = clocks.reference_clock();
|
|
|
|
//! ref_clock.configure_clock(&xosc, xosc.get_freq());
|
|
|
|
//!
|
|
|
|
//! // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
|
|
|
|
//! let mut sys_clock = clocks.system_clock();
|
|
|
|
//! sys_clock.configure_clock(&pll_sys, pll_sys.get_freq());
|
|
|
|
//!
|
|
|
|
//! // CLK USB = PLL USB (48MHz) / 1 = 48MHz
|
|
|
|
//! let mut usb_clock = clocks.usb_clock();
|
|
|
|
//! usb_clock.configure_clock(&pll_usb, pll_usb.get_freq());
|
|
|
|
//!
|
|
|
|
//! // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
|
|
|
|
//! let mut adc_clock = clocks.adc_clock();
|
|
|
|
//! adc_clock.configure_clock(&pll_usb, pll_usb.get_freq());
|
|
|
|
//!
|
|
|
|
//! // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
|
|
|
|
//! let mut rtc_clock = clocks.rtc_clock();
|
|
|
|
//! rtc_clock.configure_clock(&pll_usb, 46875u32.Hz());
|
|
|
|
//!
|
|
|
|
//! // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
|
|
|
|
//! // Normally choose clk_sys or clk_usb
|
|
|
|
//! let mut peripheral_clock = clocks.peripheral_clock();
|
|
|
|
//! peripheral_clock.configure_clock(&sys_clock, sys_clock.freq());
|
|
|
|
//!
|
2021-07-06 17:30:44 +02:00
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! See [Chapter 2 Section 15](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
|
|
|
|
|
|
|
use crate::{
|
2021-07-08 12:58:48 +02:00
|
|
|
clocks::available_clocks::ClockSource,
|
2021-07-06 17:30:44 +02:00
|
|
|
pll::{Locked, PhaseLockedLoop},
|
|
|
|
watchdog::Watchdog,
|
|
|
|
xosc::{CrystalOscillator, Stable},
|
|
|
|
};
|
|
|
|
use embedded_time::rate::*;
|
2021-07-08 12:58:48 +02:00
|
|
|
use pac::{CLOCKS, PLL_SYS, PLL_USB};
|
2021-05-01 23:18:37 +02:00
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
mod macros;
|
2021-07-08 12:58:48 +02:00
|
|
|
pub mod available_clocks;
|
2021-05-01 23:18:37 +02:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
/// Provides refs to the CLOCKS block.
|
2021-07-08 12:58:48 +02:00
|
|
|
struct ShareableClocks {
|
2021-05-01 23:19:08 +02:00
|
|
|
_internal: (),
|
2021-05-01 23:18:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ShareableClocks {
|
|
|
|
fn new(_clocks: &mut CLOCKS) -> Self {
|
2021-05-01 23:19:08 +02:00
|
|
|
ShareableClocks { _internal: () }
|
2021-05-01 23:18:37 +02:00
|
|
|
}
|
|
|
|
|
2021-07-08 12:58:48 +02:00
|
|
|
unsafe fn get(&self) -> &pac::clocks::RegisterBlock {
|
2021-05-01 23:18:37 +02:00
|
|
|
&*CLOCKS::ptr()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-06 17:30:44 +02:00
|
|
|
const XOSC_MHZ: u32 = 12_000_000_u32;
|
|
|
|
|
2021-05-01 23:18:37 +02:00
|
|
|
/// Abstraction layer providing Clock Management.
|
|
|
|
pub struct ClocksManager {
|
2021-05-01 23:19:08 +02:00
|
|
|
clocks: CLOCKS,
|
|
|
|
shared_clocks: ShareableClocks,
|
2021-05-01 23:18:37 +02:00
|
|
|
}
|
|
|
|
impl ClocksManager {
|
|
|
|
/// Exchanges CLOCKS block against Self.
|
2021-07-06 17:30:44 +02:00
|
|
|
pub fn new(mut clocks_block: CLOCKS, watchdog: &mut Watchdog) -> Self {
|
|
|
|
// Start tick in watchdog
|
|
|
|
watchdog.enable_tick_generation(XOSC_MHZ as u8);
|
|
|
|
|
|
|
|
// Disable resus that may be enabled from previous software
|
|
|
|
clocks_block.clk_sys_resus_ctrl.write_with_zero(|w| w);
|
|
|
|
|
2021-05-01 23:18:37 +02:00
|
|
|
let shared_clocks = ShareableClocks::new(&mut clocks_block);
|
|
|
|
ClocksManager {
|
|
|
|
clocks: clocks_block,
|
2021-06-26 23:45:28 -05:00
|
|
|
shared_clocks,
|
2021-05-01 23:18:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-08 12:58:48 +02:00
|
|
|
/// Initialize the clocks to a sane default
|
|
|
|
pub fn init_default(
|
2021-07-06 17:30:44 +02:00
|
|
|
&self,
|
2021-07-08 12:58:48 +02:00
|
|
|
xosc: &CrystalOscillator<Stable>,
|
|
|
|
pll_sys: &PhaseLockedLoop<Locked, PLL_SYS>,
|
|
|
|
pll_usb: &PhaseLockedLoop<Locked, PLL_USB>,
|
2021-07-06 17:30:44 +02:00
|
|
|
) {
|
|
|
|
// Configure clocks
|
|
|
|
// CLK_REF = XOSC (12MHz) / 1 = 12MHz
|
2021-07-08 12:58:48 +02:00
|
|
|
let mut ref_clock = self.reference_clock();
|
|
|
|
ref_clock.configure_clock(xosc, xosc.get_freq());
|
2021-07-06 17:30:44 +02:00
|
|
|
|
|
|
|
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
|
2021-07-08 12:58:48 +02:00
|
|
|
let mut sys_clock = self.system_clock();
|
|
|
|
sys_clock.configure_clock(pll_sys, pll_sys.get_freq());
|
2021-07-06 17:30:44 +02:00
|
|
|
|
|
|
|
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
|
|
|
|
let mut usb_clock = self.usb_clock();
|
2021-07-08 12:58:48 +02:00
|
|
|
usb_clock.configure_clock(pll_usb, pll_usb.get_freq());
|
2021-07-06 17:30:44 +02:00
|
|
|
|
|
|
|
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
|
|
|
|
let mut adc_clock = self.adc_clock();
|
2021-07-08 12:58:48 +02:00
|
|
|
adc_clock.configure_clock(pll_usb, pll_usb.get_freq());
|
2021-07-06 17:30:44 +02:00
|
|
|
|
|
|
|
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
|
|
|
|
let mut rtc_clock = self.rtc_clock();
|
2021-07-08 12:58:48 +02:00
|
|
|
rtc_clock.configure_clock(pll_usb, 46875u32.Hz());
|
2021-07-06 17:30:44 +02:00
|
|
|
|
|
|
|
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
|
|
|
|
// Normally choose clk_sys or clk_usb
|
|
|
|
let mut peripheral_clock = self.peripheral_clock();
|
2021-07-08 12:58:48 +02:00
|
|
|
peripheral_clock.configure_clock(&sys_clock, sys_clock.freq());
|
2021-07-06 17:30:44 +02:00
|
|
|
}
|
|
|
|
|
2021-05-01 23:18:37 +02:00
|
|
|
/// Releases the CLOCKS block
|
|
|
|
pub fn free(self) -> CLOCKS {
|
|
|
|
self.clocks
|
|
|
|
}
|
|
|
|
}
|