Move PLL parameters into a struct to help testability and reconfiguration of the PLL.

This commit is contained in:
Nic0w 2021-04-25 17:51:03 +02:00
parent ff418b0453
commit 6157ce552f

View file

@ -4,7 +4,8 @@
use core::{ use core::{
convert::{ convert::{
Infallible, Infallible,
TryFrom TryFrom,
TryInto
}, },
marker::PhantomData, marker::PhantomData,
ops::{ ops::{
@ -18,7 +19,8 @@ use embedded_time::{
fixed_point::FixedPoint, fixed_point::FixedPoint,
rate::{ rate::{
Hertz, Hertz,
Generic Generic,
Rate
} }
}; };
@ -28,10 +30,11 @@ use nb::Error::WouldBlock;
pub trait State {} pub trait State {}
/// PLL is disabled but is configured. /// PLL is disabled.
pub struct Disabled { pub struct Disabled;
refdiv: u8,
vco_freq: Hertz, /// PLL is configured, started and locking into its designated frequency.
pub struct Locking {
post_div1: u8, post_div1: u8,
post_div2: u8 post_div2: u8
} }
@ -39,17 +42,10 @@ pub struct Disabled {
/// PLL is locked : it delivers a steady frequency. /// PLL is locked : it delivers a steady frequency.
pub struct Locked; pub struct Locked;
/// PLL is locking into its designated frequency.
pub struct Locking {
post_div1: u8,
post_div2: u8
}
impl State for Disabled {} impl State for Disabled {}
impl State for Locked {} impl State for Locked {}
impl State for Locking {} impl State for Locking {}
/// Trait to handle both underlying devices from the PAC (PLL_SYS & PLL_USB) /// Trait to handle both underlying devices from the PAC (PLL_SYS & PLL_USB)
pub trait PhaseLockedLoopDevice: Deref<Target = rp2040_pac::pll_sys::RegisterBlock> {} pub trait PhaseLockedLoopDevice: Deref<Target = rp2040_pac::pll_sys::RegisterBlock> {}
@ -97,49 +93,83 @@ pub enum Error {
BadArgument BadArgument
} }
/// Parameters for a PLL.
pub struct PLLConfig<R: Rate> {
/// Voltage Controlled Oscillator frequency.
pub vco_freq: R,
/// Reference divider
pub refdiv: u8,
/// Post Divider 1
pub post_div1: u8,
/// Post Divider 2
pub post_div2: u8
}
/// Common configs for the two PLLs. Both assume the XOSC is cadenced at 12MHz !
/// See Chapter 2, Section 18, §2
pub mod common_configs {
use super::PLLConfig;
use embedded_time::rate::Megahertz;
/// Default, nominal configuration for PLL_SYS
pub const PLL_SYS_125MHZ: PLLConfig<Megahertz> = PLLConfig {
vco_freq: Megahertz(1500),
refdiv: 1,
post_div1: 6,
post_div2: 2
};
/// Default, nominal configuration for PLL_USB.
pub const PLL_USB_48MHZ: PLLConfig<Megahertz> = PLLConfig {
vco_freq: Megahertz(480),
refdiv: 1,
post_div1: 5,
post_div2: 2
};
}
impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> { impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
/// Instantiates a new Phase-Locked-Loop device.
pub fn new(dev: D) -> PhaseLockedLoop<Disabled, D> {
PhaseLockedLoop {
state: Disabled,
device: dev,
}
}
/// Instantiates and configures a new Phase-Locked-Loop device. /// Configures and starts the PLL : it switches to Locking state.
pub fn new(dev: D, refdiv: u8, vco_freq: Generic<u32>, post_div1: u8, post_div2: u8) -> Result<PhaseLockedLoop<Disabled, D>, Error> { pub fn initialize<R: Rate>(self, xosc_frequency: Generic<u32>, config: PLLConfig<R>) -> Result<PhaseLockedLoop<Locking, D>, Error> where R: Into<Hertz> {
const VCO_FREQ_RANGE: RangeInclusive<Hertz<u32>> = Hertz(400_000_000)..=Hertz(1600_000_000); const VCO_FREQ_RANGE: RangeInclusive<Hertz<u32>> = Hertz(400_000_000)..=Hertz(1600_000_000);
const POSTDIV_RANGE: Range<u8> = 1..7; const POSTDIV_RANGE: Range<u8> = 1..7;
const FBDIV_RANGE: Range<u16> = 16..320;
let vco_freq = Hertz::<u32>::try_from(vco_freq).map_err(|_| Error::BadArgument)?; let vco_freq: Hertz = config.vco_freq.try_into().map_err(|_| Error::BadArgument)?;
if !VCO_FREQ_RANGE.contains(&vco_freq) { if !VCO_FREQ_RANGE.contains(&vco_freq) {
return Err(Error::VCOFreqOutOfRange) return Err(Error::VCOFreqOutOfRange)
} }
if !POSTDIV_RANGE.contains(&post_div1) || !POSTDIV_RANGE.contains(&post_div2) { if !POSTDIV_RANGE.contains(&config.post_div2) || !POSTDIV_RANGE.contains(&config.post_div2) {
return Err(Error::PostDivOutOfRage) return Err(Error::PostDivOutOfRage)
} }
Ok(PhaseLockedLoop { let ref_freq_range: Range<Hertz<u32>> = Hertz(5_000_000)..vco_freq.div(16);
state: Disabled {
refdiv, vco_freq, post_div1, post_div2
},
device: dev,
})
}
/// Configures and starts the PLL : it switches to Locking state.
pub fn initialize(self, xosc_frequency: Generic<u32>) -> Result<PhaseLockedLoop<Locking, D>, Error>{
const FBDIV_RANGE: Range<u16> = 16..320;
let ref_freq_range: Range<Hertz<u32>> = Hertz(5_000_000)..self.state.vco_freq.div(16);
// Turn off PLL in case it is already running // Turn off PLL in case it is already running
self.device.pwr.reset(); self.device.pwr.reset();
self.device.fbdiv_int.reset(); self.device.fbdiv_int.reset();
let refdiv = self.state.refdiv;
let ref_freq_hz = Hertz::<u32>::try_from(xosc_frequency). let ref_freq_hz = Hertz::<u32>::try_from(xosc_frequency).
map_err(|_| Error::BadArgument)?. map_err(|_| Error::BadArgument)?.
checked_div(&(refdiv as u32)). checked_div(&(config.refdiv as u32)).
ok_or(Error::BadArgument)?; ok_or(Error::BadArgument)?;
if !ref_freq_range.contains(&ref_freq_hz) { if !ref_freq_range.contains(&ref_freq_hz) {
@ -147,12 +177,14 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
} }
self.device.cs.write(|w| unsafe { self.device.cs.write(|w| unsafe {
w.refdiv().bits(refdiv as u8); w.refdiv().bits(config.refdiv);
w w
}); });
let fbdiv = *self.state.vco_freq.checked_div(ref_freq_hz.integer()). let fbdiv = vco_freq.checked_div(ref_freq_hz.integer()).
ok_or(Error::BadArgument)?.integer() as u16; ok_or(Error::BadArgument)?;
let fbdiv: u16 = (*fbdiv.integer()).try_into().map_err(|_| Error::BadArgument)?;
if !FBDIV_RANGE.contains(&fbdiv) { if !FBDIV_RANGE.contains(&fbdiv) {
return Err(Error::FBDIVOutOfRange) return Err(Error::FBDIVOutOfRange)
@ -163,11 +195,6 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
w w
}); });
self.device.cs.write(|w| unsafe {
w.refdiv().bits(refdiv);
w
});
// Turn on self.device // Turn on self.device
self.device.pwr.write(|w| unsafe { self.device.pwr.write(|w| unsafe {
//w.pd().clear_bit(); //w.pd().clear_bit();
@ -176,8 +203,8 @@ impl<D: PhaseLockedLoopDevice> PhaseLockedLoop<Disabled, D> {
w w
}); });
let post_div1 = self.state.post_div1; let post_div1 = config.post_div1;
let post_div2 = self.state.post_div2; let post_div2 = config.post_div2;
Ok(self.transition(Locking { Ok(self.transition(Locking {
post_div1, post_div2 post_div1, post_div2