dds: add single tone control

This commit is contained in:
occheung 2020-08-26 17:39:33 +08:00
parent 1d3ced0d16
commit 649b5b498b

View File

@ -66,6 +66,7 @@ const READ_MASK :u8 = 0x80;
pub struct DDS<SPI> { pub struct DDS<SPI> {
spi: SPI, spi: SPI,
f_ref_clk: u64, f_ref_clk: u64,
f_sys_clk: u64,
} }
impl<SPI, E> DDS<SPI> impl<SPI, E> DDS<SPI>
@ -76,6 +77,7 @@ where
DDS { DDS {
spi, spi,
f_ref_clk, f_ref_clk,
f_sys_clk: f_ref_clk,
} }
} }
} }
@ -119,7 +121,9 @@ where
(DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0), (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0),
// Ensure divider is not reset // Ensure divider is not reset
(DDSCFRMask::REFCLK_IN_DIV_RESETB, 1), (DDSCFRMask::REFCLK_IN_DIV_RESETB, 1),
]) ])?;
self.f_sys_clk = self.f_ref_clk / 2;
Ok(())
} }
pub fn enable_normal_ref_clk(&mut self) -> Result<(), Error<E>> { pub fn enable_normal_ref_clk(&mut self) -> Result<(), Error<E>> {
@ -130,7 +134,9 @@ where
(DDSCFRMask::REFCLK_IN_DIV_BYPASS, 1), (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 1),
// Reset does not matter // Reset does not matter
(DDSCFRMask::REFCLK_IN_DIV_RESETB, 1), (DDSCFRMask::REFCLK_IN_DIV_RESETB, 1),
]) ])?;
self.f_sys_clk = self.f_ref_clk;
Ok(())
} }
pub fn enable_pll(&mut self, f_sys_clk: u64) -> Result<(), Error<E>> { pub fn enable_pll(&mut self, f_sys_clk: u64) -> Result<(), Error<E>> {
@ -170,16 +176,19 @@ where
])?; ])?;
self.set_configurations(&mut [ self.set_configurations(&mut [
(DDSCFRMask::PFD_RESET, 0), (DDSCFRMask::PFD_RESET, 0),
]) ])?;
self.f_sys_clk = self.f_ref_clk * divider;
Ok(())
} }
// Change external clock source (ref_clk) // Change external clock source (ref_clk)
pub fn set_ref_clk_frequency(&mut self, f_ref_clk: u64) { pub fn set_ref_clk_frequency(&mut self, f_ref_clk: u64) {
self.f_ref_clk = f_ref_clk; self.f_ref_clk = f_ref_clk;
// TODO: Examine clock tree and update f_sys_clk
} }
/* /*
* Impleement configurations registers I/O through bitmasks * Implement configurations registers I/O through bitmasks
* *
* Get all (cfr1, cfr2, cfr3) contents * Get all (cfr1, cfr2, cfr3) contents
*/ */
@ -246,6 +255,41 @@ where
self.set_all_configurations(data_array.clone()) self.set_all_configurations(data_array.clone())
} }
/*
* Set a single tone profile
* Phase: Expressed in positive degree
* Frequency: Must be integer
* Amplitude: In a scale from 0 to 1, taking float
*/
pub fn set_single_tone_profile(&mut self, profile: u8, f_out: u64, phase_offset: f64, amp_scale_factor: f64) -> Result<(), Error<E>> {
assert!(profile < 8);
assert!(phase_offset >= 0.0 && phase_offset < 360.0);
assert!(amp_scale_factor >=0.0 && amp_scale_factor <= 1.0);
let resolutions :[u64; 3] = [1 << 32, 1 << 16, 1 << 14];
let ftw = (resolutions[0] * f_out / self.f_sys_clk) as u32;
let pow = ((resolutions[1] as f64) * phase_offset / 360.0) as u16;
let asf = ((resolutions[2] as f64) * amp_scale_factor) as u16;
// Setup configuration registers before writing single tone register
self.set_configurations(&mut [
(DDSCFRMask::RAM_ENABLE, 0),
(DDSCFRMask::DIGITAL_RAMP_ENABLE, 0),
(DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0),
])?;
// Transfer single tone profile data
self.write_register(0x0E + profile, &mut [
((asf >> 8 ) & 0xFF) as u8,
((asf >> 0 ) & 0xFF) as u8,
((pow >> 8 ) & 0xFF) as u8,
((pow >> 0 ) & 0xFF) as u8,
((ftw >> 24) & 0xFF) as u8,
((ftw >> 16) & 0xFF) as u8,
((ftw >> 8 ) & 0xFF) as u8,
((ftw >> 0 ) & 0xFF) as u8,
])
}
} }
macro_rules! impl_register_io { macro_rules! impl_register_io {