mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-24 05:01:31 +11:00
Refactor clocks (#54)
* Remove unneeded lines * Reduce macro boilerplate * Refactor clocks
This commit is contained in:
parent
d2aa2b238d
commit
f310d92b64
253
rp2040-hal/src/clocks/available_clocks.rs
Normal file
253
rp2040-hal/src/clocks/available_clocks.rs
Normal file
|
@ -0,0 +1,253 @@
|
|||
//! Available clocks
|
||||
|
||||
use super::{ClocksManager, ShareableClocks};
|
||||
use crate::{
|
||||
gpio::{
|
||||
bank0::{Gpio20, Gpio22},
|
||||
FunctionClock, Pin,
|
||||
},
|
||||
pll::{Locked, PhaseLockedLoop},
|
||||
rosc::{Enabled, RingOscillator},
|
||||
typelevel::Sealed,
|
||||
xosc::{CrystalOscillator, Stable},
|
||||
};
|
||||
use core::{convert::TryInto, marker::PhantomData};
|
||||
use embedded_time::rate::*;
|
||||
use pac::{PLL_SYS, PLL_USB};
|
||||
|
||||
fn make_div<S: TryInto<Hertz<u64>>, F: TryInto<Hertz<u64>>>(
|
||||
src_freq: S,
|
||||
freq: F,
|
||||
) -> Result<u32, ()> {
|
||||
let src_freq = *src_freq.try_into().map_err(|_| ())?.integer();
|
||||
let freq = *freq.try_into().map_err(|_| ())?.integer();
|
||||
let div: u64 = (src_freq << 8).wrapping_div(freq);
|
||||
Ok(div as u32)
|
||||
}
|
||||
|
||||
/// For clocks with a divider
|
||||
trait ClockDivision {
|
||||
/// Set integer divider value.
|
||||
fn set_div(&mut self, div: u32);
|
||||
/// Get integer diveder value.
|
||||
fn get_div(&self) -> u32;
|
||||
}
|
||||
|
||||
/// Clock with glitchless source
|
||||
pub trait GlitchlessClock {
|
||||
/// Self type to hand to ChangingClockToken
|
||||
type Clock: GlitchlessClock;
|
||||
|
||||
/// Await switching clock sources without glitches. Needs a token that is returned when setting
|
||||
fn await_select(&self, clock_token: &ChangingClockToken<Self::Clock>) -> nb::Result<(), ()>;
|
||||
}
|
||||
|
||||
/// Token which can be used to await the glitchless switch
|
||||
pub struct ChangingClockToken<G: GlitchlessClock> {
|
||||
clock_nr: u8,
|
||||
clock: PhantomData<G>,
|
||||
}
|
||||
|
||||
/// For clocks that can be disabled
|
||||
pub trait StoppableClock {
|
||||
/// Enables the clock.
|
||||
fn enable(&mut self);
|
||||
|
||||
/// Disables the clock.
|
||||
fn disable(&mut self);
|
||||
|
||||
/// Kills the clock.
|
||||
fn kill(&mut self);
|
||||
}
|
||||
|
||||
/// Trait for things that can be used as clock source
|
||||
pub trait ClockSource: Sealed {
|
||||
/// Get the operating frequency for this source
|
||||
///
|
||||
/// Used to determine the divisor
|
||||
fn get_freq(&self) -> Hertz;
|
||||
}
|
||||
|
||||
type PllSys = PhaseLockedLoop<Locked, PLL_SYS>;
|
||||
impl Sealed for PllSys {}
|
||||
impl ClockSource for PllSys {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
type PllUsb = PhaseLockedLoop<Locked, PLL_USB>;
|
||||
impl Sealed for PllUsb {}
|
||||
impl ClockSource for PllUsb {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for UsbClock {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for AdcClock {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for RtcClock {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for SystemClock {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockSource for ReferenceClock {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.frequency
|
||||
}
|
||||
}
|
||||
|
||||
type Xosc = CrystalOscillator<Stable>;
|
||||
impl Sealed for Xosc {}
|
||||
impl ClockSource for Xosc {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
type Rosc = RingOscillator<Enabled>;
|
||||
impl Sealed for Rosc {}
|
||||
// We are assuming the second output is never phase shifted (see 2.17.4)
|
||||
impl ClockSource for RingOscillator<Enabled> {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
self.operating_frequency()
|
||||
}
|
||||
}
|
||||
|
||||
// GPIN0
|
||||
type GPin0 = Pin<Gpio20, FunctionClock>;
|
||||
impl ClockSource for GPin0 {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// GPIN1
|
||||
type GPin1 = Pin<Gpio22, FunctionClock>;
|
||||
impl ClockSource for Pin<Gpio22, FunctionClock> {
|
||||
fn get_freq(&self) -> Hertz {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to contrain which ClockSource is valid for which Clock
|
||||
pub trait ValidSrc<Clock>: Sealed {
|
||||
/// Which register values are acceptable
|
||||
type Variant;
|
||||
|
||||
/// Is this a ClockSource for src or aux?
|
||||
fn is_aux(&self) -> bool;
|
||||
/// Get register value for this ClockSource
|
||||
fn variant(&self) -> Self::Variant;
|
||||
}
|
||||
|
||||
clock!(
|
||||
/// GPIO Output 0 Clock
|
||||
struct GpioOutput0Clock {
|
||||
reg: clk_gpout0,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// GPIO Output 1 Clock
|
||||
struct GpioOutput1Clock {
|
||||
reg: clk_gpout1,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// GPIO Output 2 Clock
|
||||
struct GpioOutput2Clock {
|
||||
reg: clk_gpout2,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// GPIO Output 3 Clock
|
||||
struct GpioOutput3Clock {
|
||||
reg: clk_gpout3,
|
||||
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// Reference Clock
|
||||
struct ReferenceClock {
|
||||
reg: clk_ref,
|
||||
src: {Rosc: ROSC_CLKSRC_PH, Xosc:XOSC_CLKSRC},
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// System Clock
|
||||
struct SystemClock {
|
||||
reg: clk_sys,
|
||||
src: {ReferenceClock: CLK_REF},
|
||||
auxsrc: {PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// Peripheral Clock
|
||||
struct PeripheralClock {
|
||||
reg: clk_peri,
|
||||
auxsrc: {SystemClock: CLK_SYS, PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1 },
|
||||
div: false
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// USB Clock
|
||||
struct UsbClock {
|
||||
reg: clk_usb,
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// Adc Clock
|
||||
struct AdcClock {
|
||||
reg: clk_adc,
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
);
|
||||
clock!(
|
||||
/// RTC Clock
|
||||
struct RtcClock {
|
||||
reg: clk_rtc,
|
||||
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
|
||||
}
|
||||
);
|
||||
|
||||
impl SystemClock {
|
||||
fn get_default_clock_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
|
||||
pac::clocks::clk_sys_ctrl::SRC_A::CLK_REF
|
||||
}
|
||||
|
||||
fn get_aux_source(&self) -> pac::clocks::clk_sys_ctrl::SRC_A {
|
||||
pac::clocks::clk_sys_ctrl::SRC_A::CLKSRC_CLK_SYS_AUX
|
||||
}
|
||||
}
|
||||
|
||||
impl ReferenceClock {
|
||||
fn get_default_clock_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
|
||||
pac::clocks::clk_ref_ctrl::SRC_A::ROSC_CLKSRC_PH
|
||||
}
|
||||
|
||||
fn get_aux_source(&self) -> pac::clocks::clk_ref_ctrl::SRC_A {
|
||||
pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX
|
||||
}
|
||||
}
|
|
@ -1,289 +1,366 @@
|
|||
macro_rules! int_division {
|
||||
($name:ident, $div:ident, $u:ty) => {
|
||||
impl IntegerDivision for $name {
|
||||
fn set_int_div(&mut self, div: usize) {
|
||||
unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe {
|
||||
w.int().bits(div as $u);
|
||||
w
|
||||
});
|
||||
macro_rules! clock {
|
||||
{
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident {
|
||||
reg: $reg:ident,
|
||||
src: {$($src:ident: $src_variant:ident),*},
|
||||
auxsrc: {$($auxsrc:ident: $aux_variant:ident),*}
|
||||
}
|
||||
} => {
|
||||
base_clock!{
|
||||
$(#[$attr])*
|
||||
($name, $reg, auxsrc={$($auxsrc: $aux_variant),*})
|
||||
}
|
||||
|
||||
divisable_clock!($name, $reg);
|
||||
|
||||
$crate::paste::paste!{
|
||||
$(impl ValidSrc<$name> for $src {
|
||||
type Variant = [<$reg:camel SrcType>];
|
||||
|
||||
fn is_aux(&self) -> bool{
|
||||
false
|
||||
}
|
||||
fn variant(&self) -> [<$reg:camel SrcType>] {
|
||||
[<$reg:camel SrcType>]::Src(pac::clocks::[<$reg _ctrl>]::SRC_A::$src_variant)
|
||||
}
|
||||
})*
|
||||
|
||||
impl GlitchlessClock for $name {
|
||||
type Clock = Self;
|
||||
|
||||
fn await_select(&self, clock_token: &ChangingClockToken<Self>) -> nb::Result<(),()> {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
let selected = shared_dev.[<$reg _selected>].read().bits();
|
||||
if selected != 1 << clock_token.clock_nr {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_int_div(&self) -> usize {
|
||||
unsafe { self.shared_dev.get() }.$div.read().int().bits() as usize
|
||||
/// Holds register value for ClockSource for this clock
|
||||
pub enum [<$reg:camel SrcType>] {
|
||||
/// Its an clock source that is to be used as source
|
||||
Src(pac::clocks::[<$reg _ctrl>]::SRC_A),
|
||||
/// Its an clock source that is to be used as aux source
|
||||
Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A)
|
||||
}
|
||||
|
||||
impl [<$reg:camel SrcType>] {
|
||||
fn get_clock_id(&self) -> u8 {
|
||||
match self {
|
||||
Self::Src(v) => *v as u8,
|
||||
Self::Aux(v) => *v as u8,
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_src(&self) -> pac::clocks::[<$reg _ctrl>]::SRC_A{
|
||||
match self {
|
||||
Self::Src(v) => *v,
|
||||
Self::Aux(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_aux(&self) -> pac::clocks::[<$reg _ctrl>]::AUXSRC_A {
|
||||
match self {
|
||||
Self::Src(_) => panic!(),
|
||||
Self::Aux(v) => *v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// WIP - Helper function to reset source (blocking)
|
||||
pub fn reset_source_await(&mut self) -> nb::Result<(), ()> {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
shared_dev.[<$reg _ctrl>].modify(|_,w| {
|
||||
w.src().variant(self.get_default_clock_source())
|
||||
});
|
||||
|
||||
self.await_select(&ChangingClockToken{clock_nr:0, clock: PhantomData::<Self>})
|
||||
}
|
||||
|
||||
fn set_src<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&mut self, src: &S)-> ChangingClockToken<$name> {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
shared_dev.[<$reg _ctrl>].modify(|_,w| {
|
||||
w.src().variant(src.variant().unwrap_src())
|
||||
});
|
||||
|
||||
ChangingClockToken {
|
||||
clock: PhantomData::<$name>,
|
||||
clock_nr: src.variant().get_clock_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_self_aux_src(&mut self) -> ChangingClockToken<$name> {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.src().variant(self.get_aux_source())
|
||||
});
|
||||
|
||||
ChangingClockToken{
|
||||
clock: PhantomData::<$name>,
|
||||
clock_nr: pac::clocks::clk_ref_ctrl::SRC_A::CLKSRC_CLK_REF_AUX as u8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure this clock based on a clock source and desired frequency
|
||||
pub fn configure_clock<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&mut self, src: &S, freq: Hertz) -> bool{
|
||||
let src_freq: Hertz = src.get_freq();
|
||||
|
||||
if freq .gt(& src_freq){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Div register is 24.8) int.frac divider so multiply by 2^8 (left shift by 8)
|
||||
let div = make_div(src_freq, freq).unwrap();
|
||||
|
||||
// If increasing divisor, set divisor before source. Otherwise set source
|
||||
// before divisor. This avoids a momentary overspeed when e.g. switching
|
||||
// to a faster source and increasing divisor to compensate.
|
||||
if div > self.get_div() {
|
||||
self.set_div(div);
|
||||
}
|
||||
|
||||
// If switching a glitchless slice (ref or sys) to an aux source, switch
|
||||
// away from aux *first* to avoid passing glitches when changing aux mux.
|
||||
// Assume (!!!) glitchless source 0 is no faster than the aux source.
|
||||
nb::block!(self.reset_source_await()).unwrap();
|
||||
|
||||
|
||||
// Set aux mux first, and then glitchless mux if this self has one
|
||||
let token = if src.is_aux() {
|
||||
self.set_aux(src);
|
||||
self.set_self_aux_src()
|
||||
} else {
|
||||
self.set_src(src)
|
||||
};
|
||||
|
||||
nb::block!(self.await_select(&token)).unwrap();
|
||||
|
||||
|
||||
// Now that the source is configured, we can trust that the user-supplied
|
||||
// divisor is a safe value.
|
||||
self.set_div(div);
|
||||
|
||||
// Store the configured frequency
|
||||
self.frequency = src_freq / div;
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! frac_division {
|
||||
($name:ident, $div:ident, $u:ty) => {
|
||||
impl FractionDivision for $name {
|
||||
fn set_frac_div(&mut self, div: usize) {
|
||||
unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe {
|
||||
w.frac().bits(div as $u);
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn get_frac_div(&self) -> usize {
|
||||
unsafe { self.shared_dev.get() }
|
||||
.$div
|
||||
.read()
|
||||
.frac()
|
||||
.bits()
|
||||
.into()
|
||||
}
|
||||
{
|
||||
$( #[$attr:meta])*
|
||||
struct $name:ident {
|
||||
reg: $reg:ident,
|
||||
auxsrc: {$($auxsrc:ident: $variant:ident),*},
|
||||
div: false
|
||||
}
|
||||
} => {
|
||||
base_clock!{
|
||||
$(#[$attr])*
|
||||
($name, $reg, auxsrc={$($auxsrc: $variant),*})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! division {
|
||||
($name:ident, $div:ident) => {
|
||||
// Just to match proper divisable clocks so we don't have to do something special in configure function
|
||||
impl ClockDivision for $name {
|
||||
fn set_div(&mut self, div: u32) {
|
||||
unsafe { self.shared_dev.get() }.$div.modify(|_, w| unsafe {
|
||||
w.bits(div);
|
||||
w
|
||||
});
|
||||
}
|
||||
fn set_div(&mut self, _: u32) {}
|
||||
fn get_div(&self) -> u32 {1}
|
||||
}
|
||||
|
||||
fn get_div(&self) -> u32 {
|
||||
unsafe { self.shared_dev.get() }.$div.read().bits()
|
||||
stoppable_clock!($name, $reg);
|
||||
};
|
||||
{
|
||||
$( #[$attr:meta])*
|
||||
struct $name:ident {
|
||||
reg: $reg:ident,
|
||||
auxsrc: {$($auxsrc:ident: $variant:ident),*}
|
||||
}
|
||||
} => {
|
||||
base_clock!{
|
||||
$(#[$attr])*
|
||||
($name, $reg, auxsrc={$($auxsrc: $variant),*})
|
||||
}
|
||||
|
||||
divisable_clock!($name, $reg);
|
||||
stoppable_clock!($name, $reg);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! divisable_clock {
|
||||
($name:ident, $reg:ident) => {
|
||||
$crate::paste::paste! {
|
||||
impl ClockDivision for $name {
|
||||
fn set_div(&mut self, div: u32) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _div>].modify(|_, w| unsafe {
|
||||
w.bits(div);
|
||||
w
|
||||
});
|
||||
}
|
||||
fn get_div(&self) -> u32 {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _div>].read().bits()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! clock_generator {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ClockGenerator for $name {
|
||||
fn enable(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.enable().set_bit();
|
||||
w
|
||||
});
|
||||
macro_rules! stoppable_clock {
|
||||
($name:ident, $reg:ident) => {
|
||||
$crate::paste::paste!{
|
||||
/// Holds register value for ClockSource for this clock
|
||||
pub enum [<$reg:camel SrcType>] {
|
||||
/// Its an clock source that is to be used as aux source
|
||||
Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A)
|
||||
}
|
||||
|
||||
fn disable(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.enable().clear_bit();
|
||||
w
|
||||
});
|
||||
impl [<$reg:camel SrcType>] {
|
||||
fn unwrap_aux(&self) -> pac::clocks::[<$reg _ctrl>]::AUXSRC_A {
|
||||
match self {
|
||||
Self::Aux(v) => *v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn kill(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.kill().set_bit();
|
||||
w
|
||||
});
|
||||
impl StoppableClock for $name {
|
||||
fn enable(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.enable().set_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn disable(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.enable().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn kill(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.[<$reg _ctrl>].modify(|_, w| {
|
||||
w.kill().set_bit()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// Configure this clock based on a clock source and desired frequency
|
||||
pub fn configure_clock<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&mut self, src: &S, freq: Hertz) -> bool{
|
||||
let src_freq: Hertz = src.get_freq();
|
||||
|
||||
if freq .gt(& src_freq){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Div register is 24.8) int.frac divider so multiply by 2^8 (left shift by 8)
|
||||
let div = make_div(src_freq, freq).unwrap();
|
||||
|
||||
// If increasing divisor, set divisor before source. Otherwise set source
|
||||
// before divisor. This avoids a momentary overspeed when e.g. switching
|
||||
// to a faster source and increasing divisor to compensate.
|
||||
if div > self.get_div() {
|
||||
self.set_div(div);
|
||||
}
|
||||
|
||||
// If no glitchless mux, cleanly stop the clock to avoid glitches
|
||||
// propagating when changing aux mux. Note it would be a really bad idea
|
||||
// to do this on one of the glitchless clocks (clk_sys, clk_ref).
|
||||
|
||||
// Disable clock. On clk_ref and clk_sys this does nothing,
|
||||
// all other clocks have the ENABLE bit in the same position.
|
||||
self.disable();
|
||||
if (self.frequency > 0u32.Hz()) {
|
||||
// Delay for 3 cycles of the target clock, for ENABLE propagation.
|
||||
// Note XOSC_COUNT is not helpful here because XOSC is not
|
||||
// necessarily running, nor is timer... so, 3 cycles per loop:
|
||||
let sys_freq = 125_000_000.Hz(); // TODO
|
||||
let delay_cyc = sys_freq.div( *self.frequency.integer() ) + 1u32.Hz();
|
||||
cortex_m::asm::delay(*delay_cyc.integer());
|
||||
}
|
||||
|
||||
// Set aux mux first, and then glitchless mux if this self has one
|
||||
self.set_aux(src);
|
||||
|
||||
// Enable clock. On clk_ref and clk_sys this does nothing,
|
||||
// all other clocks have the ENABLE bit in the same posi
|
||||
self.enable();
|
||||
|
||||
// Now that the source is configured, we can trust that the user-supplied
|
||||
// divisor is a safe value.
|
||||
self.set_div(div);
|
||||
|
||||
// Store the configured frequency
|
||||
self.frequency = src_freq / div;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! xosc_source {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl XOSCClockSource for $name {
|
||||
fn set_xosc_src(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.src().xosc_clksrc();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! base_clock {
|
||||
{
|
||||
$(#[$attr:meta])*
|
||||
($name:ident, $reg:ident, auxsrc={$($auxsrc:ident: $variant:ident),*})
|
||||
} => {
|
||||
$crate::paste::paste!{
|
||||
|
||||
macro_rules! rosc_source {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ROSCClockSource for $name {
|
||||
fn set_rosc_src(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.src().rosc_clksrc_ph();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
$(impl ValidSrc<$name> for $auxsrc {
|
||||
type Variant = [<$reg:camel SrcType>];
|
||||
|
||||
macro_rules! selfaux_source {
|
||||
($name:ident, $ctrl:ident, $self:ident) => {
|
||||
impl SelfAuxClockSource for $name {
|
||||
fn set_self_aux_src(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.src().$self();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
fn is_aux(&self) -> bool{
|
||||
true
|
||||
}
|
||||
fn variant(&self) -> [<$reg:camel SrcType>] {
|
||||
[<$reg:camel SrcType>]::Aux(pac::clocks::[<$reg _ctrl>]::AUXSRC_A::$variant)
|
||||
}
|
||||
})*
|
||||
|
||||
macro_rules! clockref_source {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ClockREFClockSource for $name {
|
||||
fn set_clkref_src(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.src().clk_ref();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl ClocksManager {
|
||||
#[ doc = "Getter for the" $name ]
|
||||
pub fn [<$name:snake>](&self) -> $name {
|
||||
|
||||
macro_rules! clocksys_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ClockSYSClockAuxSource for $name {
|
||||
fn set_clksys_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clk_sys();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
//TODO: Init clock here
|
||||
$name {
|
||||
shared_dev: self.shared_clocks,
|
||||
frequency: 0.Hz(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! clockusb_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ClockUSBClockAuxSource for $name {
|
||||
fn set_clkusb_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clk_usb();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
$(#[$attr])*
|
||||
pub struct $name {
|
||||
shared_dev: ShareableClocks,
|
||||
frequency: Hertz,
|
||||
}
|
||||
|
||||
macro_rules! clockadc_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ClockADCClockAuxSource for $name {
|
||||
fn set_clkadc_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clk_adc();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl $name {
|
||||
/// Returns the frequency of the configured clock
|
||||
pub fn freq(&self) -> Hertz {
|
||||
self.frequency
|
||||
}
|
||||
|
||||
macro_rules! clockrtc_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ClockRTCClockAuxSource for $name {
|
||||
fn set_clkrtc_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clk_rtc();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
fn set_aux<S:ClockSource + ValidSrc<$name, Variant=[<$reg:camel SrcType>]>>(&mut self, src: &S) {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
macro_rules! clockref_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ClockRefClockAuxSource for $name {
|
||||
fn set_clkref_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clk_ref();
|
||||
w
|
||||
});
|
||||
shared_dev.[<$reg _ctrl>].modify(|_,w| {
|
||||
w.auxsrc().variant(src.variant().unwrap_aux())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! xosc_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl XOSCClockAuxSource for $name {
|
||||
fn set_xosc_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().xosc_clksrc();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl Sealed for $name {}
|
||||
|
||||
macro_rules! rosc_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ROSCClockAuxSource for $name {
|
||||
fn set_rosc_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().rosc_clksrc();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! rosc_ph_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl ROSCPHClockAuxSource for $name {
|
||||
fn set_rosc_ph_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().rosc_clksrc_ph();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! gpin0_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl Gpin0ClockAuxSource for $name {
|
||||
fn set_gpin0_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clksrc_gpin0();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! gpin1_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl Gpin1ClockAuxSource for $name {
|
||||
fn set_gpin1_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clksrc_gpin1();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! pll_usb_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl PLLUSBClockAuxSource for $name {
|
||||
fn set_pll_usb_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clksrc_pll_usb();
|
||||
w
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! pll_sys_auxsource {
|
||||
($name:ident, $ctrl:ident) => {
|
||||
impl PLLSYSClockAuxSource for $name {
|
||||
fn set_pll_sys_auxsrc(&mut self) {
|
||||
unsafe { self.shared_dev.get() }.$ctrl.modify(|_, w| {
|
||||
w.auxsrc().clksrc_pll_sys();
|
||||
w
|
||||
});
|
||||
impl From<$name> for Hertz
|
||||
{
|
||||
fn from(value: $name) -> Hertz {
|
||||
value.frequency
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,34 +10,58 @@
|
|||
//! // 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
|
||||
//! 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();
|
||||
//! clocks.init(&xosc, &pll_sys, &pll_usb);
|
||||
//! 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());
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! See [Chapter 2 Section 15](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
|
||||
use crate::{
|
||||
clocks::available_clocks::ClockSource,
|
||||
pll::{Locked, PhaseLockedLoop},
|
||||
watchdog::Watchdog,
|
||||
xosc::{CrystalOscillator, Stable},
|
||||
};
|
||||
use core::convert::TryInto;
|
||||
use embedded_time::rate::*;
|
||||
use pac::{clocks, CLOCKS, PLL_SYS, PLL_USB};
|
||||
use pac::{CLOCKS, PLL_SYS, PLL_USB};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod available_clocks;
|
||||
#[derive(Copy, Clone)]
|
||||
/// Provides refs to the CLOCKS block.
|
||||
pub struct ShareableClocks {
|
||||
struct ShareableClocks {
|
||||
_internal: (),
|
||||
}
|
||||
|
||||
|
@ -46,22 +70,13 @@ impl ShareableClocks {
|
|||
ShareableClocks { _internal: () }
|
||||
}
|
||||
|
||||
unsafe fn get(&self) -> &clocks::RegisterBlock {
|
||||
unsafe fn get(&self) -> &pac::clocks::RegisterBlock {
|
||||
&*CLOCKS::ptr()
|
||||
}
|
||||
}
|
||||
|
||||
const XOSC_MHZ: u32 = 12_000_000_u32;
|
||||
|
||||
fn make_div<S: TryInto<Hertz<u64>>, F: TryInto<Hertz<u64>>>(
|
||||
src_freq: S,
|
||||
freq: F,
|
||||
) -> Result<u32, ()> {
|
||||
let src_freq = *src_freq.try_into().map_err(|_| ())?.integer();
|
||||
let freq = *freq.try_into().map_err(|_| ())?.integer();
|
||||
let div: u64 = (src_freq << 8).wrapping_div(freq);
|
||||
Ok(div as u32)
|
||||
}
|
||||
/// Abstraction layer providing Clock Management.
|
||||
pub struct ClocksManager {
|
||||
clocks: CLOCKS,
|
||||
|
@ -83,516 +98,42 @@ impl ClocksManager {
|
|||
}
|
||||
}
|
||||
|
||||
/// Initialize the clocks
|
||||
pub fn init(
|
||||
/// Initialize the clocks to a sane default
|
||||
pub fn init_default(
|
||||
&self,
|
||||
_: &CrystalOscillator<Stable>,
|
||||
_: &PhaseLockedLoop<Locked, PLL_SYS>,
|
||||
_: &PhaseLockedLoop<Locked, PLL_USB>,
|
||||
xosc: &CrystalOscillator<Stable>,
|
||||
pll_sys: &PhaseLockedLoop<Locked, PLL_SYS>,
|
||||
pll_usb: &PhaseLockedLoop<Locked, PLL_USB>,
|
||||
) {
|
||||
// Configure clocks
|
||||
// CLK_REF = XOSC (12MHz) / 1 = 12MHz
|
||||
let mut ref_clock = self.ref_clock();
|
||||
let div = make_div(12u32.MHz(), 12u32.MHz()).unwrap();
|
||||
// If increasing divisor, set divisor before source.
|
||||
if div > ref_clock.get_div() {
|
||||
ref_clock.set_div(div);
|
||||
}
|
||||
ref_clock.set_xosc_src();
|
||||
ref_clock.await_select(2);
|
||||
ref_clock.set_div(div);
|
||||
let mut ref_clock = self.reference_clock();
|
||||
ref_clock.configure_clock(xosc, xosc.get_freq());
|
||||
|
||||
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
|
||||
let mut sys_clock = self.sys_clock();
|
||||
let div = make_div(125u32.MHz(), 125u32.MHz()).unwrap();
|
||||
// If increasing divisor, set divisor before source.
|
||||
if div > sys_clock.get_div() {
|
||||
sys_clock.set_div(div);
|
||||
}
|
||||
sys_clock.set_pll_sys_auxsrc();
|
||||
sys_clock.set_self_aux_src();
|
||||
sys_clock.await_select(1);
|
||||
sys_clock.set_div(div);
|
||||
let mut sys_clock = self.system_clock();
|
||||
sys_clock.configure_clock(pll_sys, pll_sys.get_freq());
|
||||
|
||||
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
|
||||
let mut usb_clock = self.usb_clock();
|
||||
let div = make_div(48u32.MHz(), 48u32.MHz()).unwrap();
|
||||
// If increasing divisor, set divisor before source.
|
||||
if div > usb_clock.get_div() {
|
||||
usb_clock.set_div(div);
|
||||
}
|
||||
usb_clock.disable();
|
||||
usb_clock.set_pll_usb_auxsrc();
|
||||
usb_clock.enable();
|
||||
usb_clock.set_div(div);
|
||||
usb_clock.configure_clock(pll_usb, pll_usb.get_freq());
|
||||
|
||||
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
|
||||
let mut adc_clock = self.adc_clock();
|
||||
let div = make_div(48u32.MHz(), 48u32.MHz()).unwrap();
|
||||
// If increasing divisor, set divisor before source.
|
||||
if div > adc_clock.get_div() {
|
||||
adc_clock.set_div(div);
|
||||
}
|
||||
adc_clock.disable();
|
||||
adc_clock.set_pll_usb_auxsrc();
|
||||
adc_clock.enable();
|
||||
adc_clock.set_div(div);
|
||||
adc_clock.configure_clock(pll_usb, pll_usb.get_freq());
|
||||
|
||||
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
|
||||
let mut rtc_clock = self.rtc_clock();
|
||||
let div = make_div(48u32.MHz(), 46875u32.Hz()).unwrap();
|
||||
// If increasing divisor, set divisor before source.
|
||||
if div > rtc_clock.get_div() {
|
||||
rtc_clock.set_div(div);
|
||||
}
|
||||
rtc_clock.disable();
|
||||
rtc_clock.set_pll_usb_auxsrc();
|
||||
rtc_clock.enable();
|
||||
rtc_clock.set_div(div);
|
||||
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 = self.peripheral_clock();
|
||||
peripheral_clock.disable();
|
||||
peripheral_clock.set_clksys_auxsrc();
|
||||
peripheral_clock.enable();
|
||||
peripheral_clock.configure_clock(&sys_clock, sys_clock.freq());
|
||||
}
|
||||
|
||||
/// Releases the CLOCKS block
|
||||
pub fn free(self) -> CLOCKS {
|
||||
self.clocks
|
||||
}
|
||||
|
||||
/// Getter for the GPIO Output 0 Clock.
|
||||
pub fn gpio_output0_clock(&self) -> GpioOutput0Clock {
|
||||
GpioOutput0Clock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the GPIO Output 1 Clock.
|
||||
pub fn gpio_output1_clock(&self) -> GpioOutput1Clock {
|
||||
GpioOutput1Clock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the GPIO Output 2 Clock.
|
||||
pub fn gpio_output2_clock(&self) -> GpioOutput2Clock {
|
||||
GpioOutput2Clock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the GPIO Output 3 Clock.
|
||||
pub fn gpio_output3_clock(&self) -> GpioOutput3Clock {
|
||||
GpioOutput3Clock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the Reference Clock.
|
||||
pub fn ref_clock(&self) -> ReferenceClock {
|
||||
ReferenceClock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the System Clock
|
||||
pub fn sys_clock(&self) -> SystemClock {
|
||||
SystemClock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the PeripheralClock
|
||||
pub fn peripheral_clock(&self) -> PeripheralClock {
|
||||
PeripheralClock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the Usb Clock
|
||||
pub fn usb_clock(&self) -> UsbClock {
|
||||
UsbClock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the Adc Clock
|
||||
pub fn adc_clock(&self) -> AdcClock {
|
||||
AdcClock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for the Rtc Clock
|
||||
pub fn rtc_clock(&self) -> RtcClock {
|
||||
RtcClock {
|
||||
shared_dev: self.shared_clocks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For clocks with an integer divider.
|
||||
pub trait IntegerDivision {
|
||||
/// Set integer divider value.
|
||||
fn set_int_div(&mut self, div: usize);
|
||||
/// Get integer diveder value.
|
||||
fn get_int_div(&self) -> usize;
|
||||
}
|
||||
|
||||
/// For clocks with a fraction divider.
|
||||
pub trait FractionDivision {
|
||||
/// Set fraction divider value.
|
||||
fn set_frac_div(&mut self, div: usize);
|
||||
/// Get fraction divider value.
|
||||
fn get_frac_div(&self) -> usize;
|
||||
}
|
||||
|
||||
/// For clocks with a divider
|
||||
pub trait ClockDivision {
|
||||
/// Set integer divider value.
|
||||
fn set_div(&mut self, div: u32);
|
||||
/// Get integer diveder value.
|
||||
fn get_div(&self) -> u32;
|
||||
}
|
||||
|
||||
/// For clocks that can have XOSC as source.
|
||||
pub trait XOSCClockSource {
|
||||
/// Set XOSC as a source.
|
||||
fn set_xosc_src(&mut self);
|
||||
}
|
||||
/// For clocks that can have ROSC as source.
|
||||
pub trait ROSCClockSource {
|
||||
/// set ROSC as a source.
|
||||
fn set_rosc_src(&mut self);
|
||||
}
|
||||
/// For clocks that can have ... itself (?) as a source (is that the "glitchless mux" ?)
|
||||
pub trait SelfAuxClockSource {
|
||||
/// Set ...
|
||||
fn set_self_aux_src(&mut self);
|
||||
}
|
||||
/// For clocks that can have the Reference Clock as source.
|
||||
pub trait ClockREFClockSource {
|
||||
/// Set Reference Clock as
|
||||
fn set_clkref_src(&mut self);
|
||||
}
|
||||
/// For clocks that can have the System Clock as an auxilliary source.
|
||||
pub trait ClockSYSClockAuxSource {
|
||||
/// Set System Clock as source.
|
||||
fn set_clksys_auxsrc(&mut self);
|
||||
}
|
||||
|
||||
/// For clocks that can have the USB Clock as an auxilliary source.
|
||||
pub trait ClockUSBClockAuxSource {
|
||||
/// Set USB Clock as source.
|
||||
fn set_clkusb_auxsrc(&mut self);
|
||||
}
|
||||
|
||||
/// For clocks that can have the ADC Clock as an auxilliary source.
|
||||
pub trait ClockADCClockAuxSource {
|
||||
/// Set ADC Clock as source.
|
||||
fn set_clkadc_auxsrc(&mut self);
|
||||
}
|
||||
|
||||
/// For clocks that can have the RTC Clock as an auxilliary source.
|
||||
pub trait ClockRTCClockAuxSource {
|
||||
/// Set RTC Clock as source.
|
||||
fn set_clkrtc_auxsrc(&mut self);
|
||||
}
|
||||
|
||||
/// For clocks that can have the Reference Clock as an auxilliary source.
|
||||
pub trait ClockRefClockAuxSource {
|
||||
/// Set Reference Clock as source.
|
||||
fn set_clkref_auxsrc(&mut self);
|
||||
}
|
||||
/// For clocks that can have XOSC as an auxilliary source.
|
||||
pub trait XOSCClockAuxSource {
|
||||
/// Set XOSC as auxilliary source.
|
||||
fn set_xosc_auxsrc(&mut self);
|
||||
}
|
||||
/// For clocks that can have ROSC as an auxilliary source.
|
||||
pub trait ROSCClockAuxSource {
|
||||
/// Set ROSC as auxilliary source.
|
||||
fn set_rosc_auxsrc(&mut self);
|
||||
}
|
||||
/// For clocks that can have ROSC_PH as an auxilliary source.
|
||||
pub trait ROSCPHClockAuxSource {
|
||||
/// Set ROSC_PH as auxilliary source.
|
||||
fn set_rosc_ph_auxsrc(&mut self);
|
||||
}
|
||||
/// For clocks that can have PLL_USB as an auxilliary source.
|
||||
pub trait PLLUSBClockAuxSource {
|
||||
/// Set PLL_USB as auxilliary source.
|
||||
fn set_pll_usb_auxsrc(&mut self);
|
||||
}
|
||||
/// For clocks that can have PLL_SYS as an auxilliary source.
|
||||
pub trait PLLSYSClockAuxSource {
|
||||
/// Set PLL_SYS as auxilliary source.
|
||||
fn set_pll_sys_auxsrc(&mut self);
|
||||
}
|
||||
/// For clocks that can have gpin0 as an auxilliary source.
|
||||
pub trait Gpin0ClockAuxSource {
|
||||
/// Set clock to be received from gpin0 (auxilliary)
|
||||
fn set_gpin0_auxsrc(&mut self);
|
||||
}
|
||||
/// For clocks that can have gpin1 as an auxilliary source.
|
||||
pub trait Gpin1ClockAuxSource {
|
||||
/// Set clock to be received from gpin1
|
||||
fn set_gpin1_auxsrc(&mut self);
|
||||
}
|
||||
|
||||
/// For clocks having a generator.
|
||||
pub trait ClockGenerator {
|
||||
/// Enables the clock.
|
||||
fn enable(&mut self);
|
||||
|
||||
/// Disables the clock.
|
||||
fn disable(&mut self);
|
||||
|
||||
/// Kills the clock.
|
||||
fn kill(&mut self);
|
||||
}
|
||||
|
||||
/// GPIO Output 0 Clock
|
||||
pub struct GpioOutput0Clock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
|
||||
clock_generator!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_sys_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
gpin0_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
gpin1_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
pll_usb_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
rosc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
xosc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
clocksys_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
clockusb_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
clockadc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
clockrtc_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
clockref_auxsource!(GpioOutput0Clock, clk_gpout0_ctrl);
|
||||
|
||||
division!(GpioOutput0Clock, clk_gpout0_div);
|
||||
int_division!(GpioOutput0Clock, clk_gpout0_div, u32);
|
||||
frac_division!(GpioOutput0Clock, clk_gpout0_div, u8);
|
||||
|
||||
/// GPIO Output 1 Clock
|
||||
pub struct GpioOutput1Clock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
|
||||
clock_generator!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_sys_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
gpin0_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
gpin1_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
pll_usb_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
rosc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
xosc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
clocksys_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
clockusb_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
clockadc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
clockrtc_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
clockref_auxsource!(GpioOutput1Clock, clk_gpout1_ctrl);
|
||||
|
||||
division!(GpioOutput1Clock, clk_gpout1_div);
|
||||
int_division!(GpioOutput1Clock, clk_gpout1_div, u32);
|
||||
frac_division!(GpioOutput1Clock, clk_gpout1_div, u8);
|
||||
|
||||
/// GPIO Output 2 Clock
|
||||
pub struct GpioOutput2Clock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
|
||||
clock_generator!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_sys_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
gpin0_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
gpin1_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
pll_usb_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
rosc_ph_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
xosc_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
clocksys_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
clockusb_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
clockadc_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
clockrtc_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
clockref_auxsource!(GpioOutput2Clock, clk_gpout2_ctrl);
|
||||
|
||||
division!(GpioOutput2Clock, clk_gpout2_div);
|
||||
int_division!(GpioOutput2Clock, clk_gpout2_div, u32);
|
||||
frac_division!(GpioOutput2Clock, clk_gpout2_div, u8);
|
||||
|
||||
/// GPIO Output 3 Clock
|
||||
pub struct GpioOutput3Clock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
|
||||
clock_generator!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_sys_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
gpin0_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
gpin1_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
pll_usb_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
rosc_ph_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
xosc_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
clocksys_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
clockusb_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
clockadc_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
clockrtc_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
clockref_auxsource!(GpioOutput3Clock, clk_gpout3_ctrl);
|
||||
|
||||
division!(GpioOutput3Clock, clk_gpout3_div);
|
||||
int_division!(GpioOutput3Clock, clk_gpout3_div, u32);
|
||||
frac_division!(GpioOutput3Clock, clk_gpout3_div, u8);
|
||||
|
||||
/// Reference Clock
|
||||
pub struct ReferenceClock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
|
||||
impl ReferenceClock {
|
||||
/// WIP - Helper function to reset source (blocking)
|
||||
pub fn reset_source_await(&mut self) {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
shared_dev.clk_ref_ctrl.write(|w| {
|
||||
unsafe { w.src().bits(0) };
|
||||
w
|
||||
});
|
||||
|
||||
self.await_select(0x0);
|
||||
}
|
||||
|
||||
/// WIP - Helper function to select new source (blocking)
|
||||
pub fn await_select(&self, clock: u8) {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
while (shared_dev.clk_ref_selected.read().bits() & (1 << clock)) == 0 {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rosc_source!(ReferenceClock, clk_ref_ctrl);
|
||||
selfaux_source!(ReferenceClock, clk_ref_ctrl, clksrc_clk_ref_aux);
|
||||
xosc_source!(ReferenceClock, clk_ref_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_usb_auxsource!(ReferenceClock, clk_ref_ctrl);
|
||||
gpin0_auxsource!(ReferenceClock, clk_ref_ctrl);
|
||||
gpin1_auxsource!(ReferenceClock, clk_ref_ctrl);
|
||||
|
||||
division!(ReferenceClock, clk_ref_div);
|
||||
/// System Clock
|
||||
pub struct SystemClock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
impl SystemClock {
|
||||
/// WIP - Helper function to reset source (blocking)
|
||||
pub fn reset_source_await(&mut self) {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
shared_dev.clk_sys_ctrl.write(|w| {
|
||||
w.src().clear_bit();
|
||||
w
|
||||
});
|
||||
|
||||
self.await_select(0x0);
|
||||
}
|
||||
|
||||
/// WIP - Helper function to select new source (blocking)
|
||||
pub fn await_select(&self, clock: u8) {
|
||||
let shared_dev = unsafe { self.shared_dev.get() };
|
||||
|
||||
while (shared_dev.clk_sys_selected.read().bits() & (1 << clock)) == 0 {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clock glitchless sources
|
||||
clockref_source!(SystemClock, clk_sys_ctrl);
|
||||
selfaux_source!(SystemClock, clk_sys_ctrl, clksrc_clk_sys_aux);
|
||||
|
||||
// Clock aux sources
|
||||
pll_sys_auxsource!(SystemClock, clk_sys_ctrl);
|
||||
pll_usb_auxsource!(SystemClock, clk_sys_ctrl);
|
||||
rosc_auxsource!(SystemClock, clk_sys_ctrl);
|
||||
xosc_auxsource!(SystemClock, clk_sys_ctrl);
|
||||
gpin0_auxsource!(SystemClock, clk_sys_ctrl);
|
||||
gpin1_auxsource!(SystemClock, clk_sys_ctrl);
|
||||
|
||||
division!(SystemClock, clk_sys_div);
|
||||
int_division!(SystemClock, clk_sys_div, u32);
|
||||
frac_division!(SystemClock, clk_sys_div, u8);
|
||||
|
||||
/// Peripheral Clock
|
||||
pub struct PeripheralClock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
clock_generator!(PeripheralClock, clk_peri_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
clocksys_auxsource!(PeripheralClock, clk_peri_ctrl);
|
||||
pll_sys_auxsource!(PeripheralClock, clk_peri_ctrl);
|
||||
pll_usb_auxsource!(PeripheralClock, clk_peri_ctrl);
|
||||
rosc_ph_auxsource!(PeripheralClock, clk_peri_ctrl);
|
||||
xosc_auxsource!(PeripheralClock, clk_peri_ctrl);
|
||||
gpin0_auxsource!(PeripheralClock, clk_peri_ctrl);
|
||||
gpin1_auxsource!(PeripheralClock, clk_peri_ctrl);
|
||||
|
||||
/// USB Clock
|
||||
pub struct UsbClock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
clock_generator!(UsbClock, clk_usb_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_usb_auxsource!(UsbClock, clk_usb_ctrl);
|
||||
pll_sys_auxsource!(UsbClock, clk_usb_ctrl);
|
||||
rosc_ph_auxsource!(UsbClock, clk_usb_ctrl);
|
||||
xosc_auxsource!(UsbClock, clk_usb_ctrl);
|
||||
gpin0_auxsource!(UsbClock, clk_usb_ctrl);
|
||||
gpin1_auxsource!(UsbClock, clk_usb_ctrl);
|
||||
|
||||
division!(UsbClock, clk_usb_div);
|
||||
|
||||
/// Adc Clock
|
||||
pub struct AdcClock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
clock_generator!(AdcClock, clk_adc_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_usb_auxsource!(AdcClock, clk_adc_ctrl);
|
||||
pll_sys_auxsource!(AdcClock, clk_adc_ctrl);
|
||||
rosc_ph_auxsource!(AdcClock, clk_adc_ctrl);
|
||||
xosc_auxsource!(AdcClock, clk_adc_ctrl);
|
||||
gpin0_auxsource!(AdcClock, clk_adc_ctrl);
|
||||
gpin1_auxsource!(AdcClock, clk_adc_ctrl);
|
||||
|
||||
division!(AdcClock, clk_adc_div);
|
||||
|
||||
/// RTC Clock
|
||||
pub struct RtcClock {
|
||||
shared_dev: ShareableClocks,
|
||||
}
|
||||
clock_generator!(RtcClock, clk_rtc_ctrl);
|
||||
|
||||
// Clock aux sources
|
||||
pll_usb_auxsource!(RtcClock, clk_rtc_ctrl);
|
||||
pll_sys_auxsource!(RtcClock, clk_rtc_ctrl);
|
||||
rosc_ph_auxsource!(RtcClock, clk_rtc_ctrl);
|
||||
xosc_auxsource!(RtcClock, clk_rtc_ctrl);
|
||||
gpin0_auxsource!(RtcClock, clk_rtc_ctrl);
|
||||
gpin1_auxsource!(RtcClock, clk_rtc_ctrl);
|
||||
|
||||
division!(RtcClock, clk_rtc_div);
|
||||
int_division!(RtcClock, clk_rtc_div, u32);
|
||||
frac_division!(RtcClock, clk_rtc_div, u8);
|
||||
|
|
|
@ -23,6 +23,7 @@ pub mod prelude;
|
|||
pub mod pwm;
|
||||
pub mod resets;
|
||||
pub mod rom_data;
|
||||
pub mod rosc;
|
||||
pub mod rtc;
|
||||
pub mod sio;
|
||||
pub mod spi;
|
||||
|
|
|
@ -26,16 +26,20 @@ pub struct Disabled {
|
|||
fbdiv: u16,
|
||||
post_div1: u8,
|
||||
post_div2: u8,
|
||||
frequency: Hertz,
|
||||
}
|
||||
|
||||
/// PLL is configured, started and locking into its designated frequency.
|
||||
pub struct Locking {
|
||||
post_div1: u8,
|
||||
post_div2: u8,
|
||||
frequency: Hertz,
|
||||
}
|
||||
|
||||
/// PLL is locked : it delivers a steady frequency.
|
||||
pub struct Locked;
|
||||
pub struct Locked {
|
||||
frequency: Hertz,
|
||||
}
|
||||
|
||||
impl State for Disabled {}
|
||||
impl State for Locked {}
|
||||
|
@ -183,6 +187,8 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
|
|||
let refdiv = config.refdiv;
|
||||
let post_div1 = config.post_div1;
|
||||
let post_div2 = config.post_div2;
|
||||
let frequency: Hertz =
|
||||
(ref_freq_hz / refdiv as u32) * fbdiv as u32 / (post_div1 as u32 * post_div2 as u32);
|
||||
|
||||
Ok(PhaseLockedLoop {
|
||||
state: Disabled {
|
||||
|
@ -190,6 +196,7 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
|
|||
fbdiv,
|
||||
post_div1,
|
||||
post_div2,
|
||||
frequency,
|
||||
},
|
||||
device: dev,
|
||||
})
|
||||
|
@ -222,10 +229,12 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
|
|||
|
||||
let post_div1 = self.state.post_div1;
|
||||
let post_div2 = self.state.post_div2;
|
||||
let frequency = self.state.frequency;
|
||||
|
||||
self.transition(Locking {
|
||||
post_div1,
|
||||
post_div2,
|
||||
frequency,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -262,7 +271,16 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Locking, D> {
|
|||
w
|
||||
});
|
||||
|
||||
self.transition(Locked)
|
||||
let frequency = self.state.frequency;
|
||||
|
||||
self.transition(Locked { frequency })
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Locked, D> {
|
||||
/// Get the operating frequency for the PLL
|
||||
pub fn operating_frequency(&self) -> Hertz {
|
||||
self.state.frequency
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,11 +296,11 @@ where
|
|||
R: Into<Hertz<u64>>,
|
||||
{
|
||||
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
|
||||
let mut sys_clock = clocks.sys_clock();
|
||||
sys_clock.reset_source_await();
|
||||
let mut sys_clock = clocks.system_clock();
|
||||
nb::block!(sys_clock.reset_source_await()).unwrap();
|
||||
|
||||
let mut ref_clock = clocks.ref_clock();
|
||||
ref_clock.reset_source_await();
|
||||
let mut ref_clock = clocks.reference_clock();
|
||||
nb::block!(ref_clock.reset_source_await()).unwrap();
|
||||
|
||||
let initialized_pll = PhaseLockedLoop::new(dev, xosc_frequency, config)?.initialize(resets);
|
||||
|
||||
|
|
99
rp2040-hal/src/rosc.rs
Normal file
99
rp2040-hal/src/rosc.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
//! Ring Oscillator (ROSC)
|
||||
// See [Chapter 2 Section 17](https://datasheets.raspberrypi.org/rp2040/rp2040_datasheet.pdf) for more details
|
||||
|
||||
use embedded_time::rate::Extensions;
|
||||
use embedded_time::rate::Hertz;
|
||||
|
||||
/// State of the Ring Oscillator (typestate trait)
|
||||
pub trait State {}
|
||||
|
||||
/// ROSC is disabled (typestate)
|
||||
pub struct Disabled;
|
||||
|
||||
/// ROSC is initialized, ie we've given parameters (typestate)
|
||||
pub struct Enabled {
|
||||
freq_hz: Hertz,
|
||||
}
|
||||
|
||||
/// ROSC is in dormant mode (see Chapter 2, Section 17, §7)
|
||||
pub struct Dormant;
|
||||
|
||||
impl State for Disabled {}
|
||||
impl State for Enabled {}
|
||||
impl State for Dormant {}
|
||||
|
||||
/// A Ring Oscillator.
|
||||
pub struct RingOscillator<S: State> {
|
||||
device: rp2040_pac::ROSC,
|
||||
state: S,
|
||||
}
|
||||
|
||||
impl<S: State> RingOscillator<S> {
|
||||
/// Transitions the oscillator to another state.
|
||||
fn transition<To: State>(self, state: To) -> RingOscillator<To> {
|
||||
RingOscillator {
|
||||
device: self.device,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases the underlying device.
|
||||
pub fn free(self) -> rp2040_pac::ROSC {
|
||||
self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl RingOscillator<Disabled> {
|
||||
/// Creates a new RingOscillator from the underlying device.
|
||||
pub fn new(dev: rp2040_pac::ROSC) -> Self {
|
||||
RingOscillator {
|
||||
device: dev,
|
||||
state: Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the ROSC : frequency range is set, startup delay is calculated and set.
|
||||
pub fn initialize(self) -> RingOscillator<Enabled> {
|
||||
self.device.ctrl.write(|w| w.enable().enable());
|
||||
|
||||
self.transition(Enabled {
|
||||
freq_hz: 6_500_000u32.Hz(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RingOscillator<Enabled> {
|
||||
/// Approx operating frequency of the ROSC in hertz
|
||||
pub fn operating_frequency(&self) -> Hertz {
|
||||
self.state.freq_hz
|
||||
}
|
||||
|
||||
/// Disables the ROSC
|
||||
pub fn disable(self) -> RingOscillator<Disabled> {
|
||||
self.device.ctrl.modify(|_r, w| w.enable().disable());
|
||||
|
||||
self.transition(Disabled)
|
||||
}
|
||||
|
||||
/// Generate random bit based on the Ring oscillator
|
||||
/// This is not suited for security purposes
|
||||
pub fn get_random_bit(&self) -> bool {
|
||||
self.device.randombit.read().randombit().bit()
|
||||
}
|
||||
|
||||
/// Put the ROSC in DORMANT state.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method is marked unsafe because prior to switch the ROSC into DORMANT state,
|
||||
/// PLLs must be stopped and IRQs have to be properly configured.
|
||||
/// This method does not do any of that, it merely switches the ROSC to DORMANT state.
|
||||
/// See Chapter 2, Section 16, §5) for details.
|
||||
pub unsafe fn dormant(self) -> RingOscillator<Dormant> {
|
||||
//taken from the C SDK
|
||||
const ROSC_DORMANT_VALUE: u32 = 0x636f6d61;
|
||||
|
||||
self.device.dormant.write(|w| w.bits(ROSC_DORMANT_VALUE));
|
||||
|
||||
self.transition(Dormant)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue