From 0a3518573a411f0d18abbf08d4fa3eb212d1a540 Mon Sep 17 00:00:00 2001 From: occheung Date: Fri, 25 Sep 2020 17:07:52 +0800 Subject: [PATCH] dds: add frequency sweep --- src/dds.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/urukul.rs | 31 +++++++++++++------- 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/dds.rs b/src/dds.rs index f8d3457..29f2d5c 100644 --- a/src/dds.rs +++ b/src/dds.rs @@ -4,6 +4,7 @@ use core::mem::size_of; use core::convert::TryInto; use heapless::Vec; use heapless::consts::*; +use log::{ trace, debug, warn }; /* * Bitmask for all configurations (Order: CFR3, CFR2, CFR1) @@ -632,6 +633,77 @@ where self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Phase, no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a frequency sweep RAM mode profile + */ + pub unsafe fn set_frequency_sweep_profile(&mut self, profile: u8, start_addr: u16, + lower_boundary: f64, upper_boundary: f64, f_resolution: f64, + no_dwell_high: bool, op_mode: RAMOperationMode, playback_rate: f64 + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(start_addr < 1024); + + // Find out the required RAM size + // Frequencies may have to be repeated if the playback rate is too low + // Reject impossible setups + // E.g. Higher playback rate than f_dds_clk, insufficient RAM allocation + let nominal_step_rate = self.f_sys_clk/(4.0 * playback_rate); + if nominal_step_rate < 1.0 { + return Err(Error::DDSRAMError); + } + + // TODO: Handle unfortunate scenario: step_rate / 0xFFFF gives a round number + // Current implmentation unnecessarily allocates 1 extra RAM space for each data + let duplication = (nominal_step_rate / (0xFFFF as f64)) as u64 + 1; + + // Acquire the RAM size needed by multiplying duplication. + // All data needs to be duplicated such that a desired step_rate can be reached + // Return DDS RAM Error if it does not fix into the RAM + let span = upper_boundary - lower_boundary; + let data_size = if core::intrinsics::roundf64(span/f_resolution) == (span/f_resolution) { + (span/f_resolution) as u64 + 1 + } else { + (span/f_resolution) as u64 + }; + let ram_size = data_size * duplication; + + let end_addr = (start_addr as u64) + ram_size - 1; + trace!("Required RAM size: {}", ram_size); + if end_addr >= 1024 { + warn!("RAM address out of bound"); + return Err(Error::DDSRAMError); + } + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Drop in the data into RAM_VEC + for data_index in 0..data_size { + let freq = lower_boundary + f_resolution * (data_index as f64); + for _ in 0..duplication { + let ftw = self.frequency_to_ftw(freq); + RAM_VEC.push(((ftw >> 24) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((ftw >> 16) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((ftw >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((ftw >> 0) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + } + } + + debug!("start_addr: {}\nend_addr: {}\n, duplication: {}\n, data_size: {}\n", + start_addr, end_addr, duplication, data_size); + + self.set_ram_profile(profile, start_addr, end_addr.try_into().unwrap(), RAMDestination::Frequency, + no_dwell_high, true, op_mode, playback_rate * (duplication as f64)) } /* @@ -650,6 +722,7 @@ where // Calculate address step rate, and check legality let step_rate = (self.f_sys_clk/(4.0 * playback_rate)) as u64; + trace!("Setting up RAM profile, step_rate: {}", step_rate); if step_rate == 0 || step_rate > 0xFFFF { return Err(Error::DDSRAMError); } @@ -729,9 +802,9 @@ where // Setter function for f_sys_clk // Warning: This does not setup the chip to generate this actual f_sys_clk - pub(crate) fn set_f_sys_clk(&mut self, f_sys_clk: f64) { - self.f_sys_clk = f_sys_clk; - } + // pub(crate) fn set_f_sys_clk(&mut self, f_sys_clk: f64) { + // self.f_sys_clk = f_sys_clk; + // } // Getter function for f_sys_clk pub fn get_f_sys_clk(&mut self) -> f64 { diff --git a/src/urukul.rs b/src/urukul.rs index 4464ab8..deb27f9 100644 --- a/src/urukul.rs +++ b/src/urukul.rs @@ -7,7 +7,7 @@ use crate::config_register::ConfigRegister; use crate::config_register::CFGMask; use crate::config_register::StatusMask; use crate::attenuator::Attenuator; -use crate::dds::DDS; +use crate::dds::{ DDS, RAMOperationMode }; /* * Enum for structuring error @@ -90,17 +90,17 @@ where ])?; // Set 0 to all fields on configuration register. self.config_register.set_configurations(&mut [ - (CFGMask::RF_SW, 0), - (CFGMask::LED, 0), - (CFGMask::PROFILE, 0), - (CFGMask::IO_UPDATE, 0), - (CFGMask::MASK_NU, 0), + (CFGMask::RF_SW, 0), + (CFGMask::LED, 0), + (CFGMask::PROFILE, 0), + (CFGMask::IO_UPDATE, 0), + (CFGMask::MASK_NU, 0), (CFGMask::CLK_SEL0, 0), (CFGMask::SYNC_SEL, 0), - (CFGMask::RST, 0), - (CFGMask::IO_RST, 0), + (CFGMask::RST, 0), + (CFGMask::IO_RST, 0), (CFGMask::CLK_SEL1, 0), - (CFGMask::DIV, 0), + (CFGMask::DIV, 0), ])?; // Init all DDS chips. Configure SDIO as input only. for chip_no in 0..4 { @@ -278,6 +278,16 @@ where self.dds[usize::from(channel)].set_single_tone_profile_amplitude(profile, amplitude) } + pub fn set_channel_frequency_sweep_profile(&mut self, channel: u8, profile: u8, start_addr: u16, lower_boundary: f64, + upper_boundary: f64, f_resolution: f64, playback_rate: f64) -> Result<(), Error> + { + unsafe { + self.dds[usize::from(channel)] + .set_frequency_sweep_profile(profile, start_addr, lower_boundary, upper_boundary, + f_resolution, true, RAMOperationMode::ContinuousRecirculate, playback_rate) + } + } + pub fn set_channel_sys_clk(&mut self, channel: u8, f_sys_clk: f64) -> Result<(), Error> { self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk).map(|_| ()) } @@ -320,8 +330,7 @@ where } self.multi_dds.set_sys_clk_frequency(reported_f_sys_clk)?; self.multi_dds.set_single_tone_profile(profile, frequency, phase, amplitude)?; - self.invoke_io_update()?; - Ok(()) + self.invoke_io_update() } // Generate a pulse for io_update bit in configuration register