diff --git a/src/dds.rs b/src/dds.rs index 12cd08b..f05ebd2 100644 --- a/src/dds.rs +++ b/src/dds.rs @@ -64,7 +64,7 @@ construct_bitmask!(DDSCFRMask; u32; const WRITE_MASK :u8 = 0x00; const READ_MASK :u8 = 0x80; -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum RAMDestination { Frequency = 0, Phase = 1, @@ -355,14 +355,9 @@ where 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] as f64) * f_out / self.f_sys_clk) as u32; - let pow = ((resolutions[1] as f64) * phase_offset / 360.0) as u16; - let asf :u16 = if amp_scale_factor == 1.0 { - 0x3FFF - } else { - ((resolutions[2] as f64) * amp_scale_factor) as u16 - }; + let ftw = self.frequency_to_ftw(f_out); + let pow = self.degree_to_pow(phase_offset); + let asf = self.amplitude_to_asf(amp_scale_factor); // Setup configuration registers before writing single tone register self.enable_single_tone_configuration()?; @@ -390,9 +385,7 @@ where // Setup configuration registers before writing single tone register self.enable_single_tone_configuration()?; - // Calculate frequency tuning work (FTW) - let f_res: u64 = 1 << 32; - let ftw = ((f_res as f64) * f_out / self.f_sys_clk) as u32; + let ftw = self.frequency_to_ftw(f_out); // Read existing amplitude/phase data let mut register: [u8; 8] = [0; 8]; @@ -418,9 +411,7 @@ where // Setup configuration registers before writing single tone register self.enable_single_tone_configuration()?; - // Calculate phase offset work (POW) - let phase_res: u64 = 1 << 16; - let pow = ((phase_res as f64) * phase_offset / 360.0) as u16; + let pow = self.degree_to_pow(phase_offset); // Read existing amplitude/frequency data let mut register: [u8; 8] = [0; 8]; @@ -445,12 +436,7 @@ where self.enable_single_tone_configuration()?; // Calculate amplitude_scale_factor (ASF) - let amp_res: u64 = 1 << 14; - let asf :u16 = if amp_scale_factor == 1.0 { - 0x3FFF - } else { - ((amp_res as f64) * amp_scale_factor) as u16 - }; + let asf = self.amplitude_to_asf(amp_scale_factor); // Read existing frequency/phase data let mut register: [u8; 8] = [0; 8]; @@ -496,16 +482,91 @@ where ]) } + /* + * Configure a RAM mode profile, but with RAM data generated by a closure + */ + pub fn set_ram_profile_with_closure(&mut self, profile: u8, start_addr: u16, + ram_dst: RAMDestination, no_dwell_high: bool, zero_crossing: bool, + op_mode: RAMOperationMode, playback_rate: f64, f: F) -> Result<(), Error> + where + F: FnOnce() -> ArrayVec::<[f64; 2048]> + { + // Check the legality of the profile setup + assert!(profile < 7); + assert!(start_addr < 1024); + let mut vec = f(); + if (ram_dst != RAMDestination::Polar && ((vec.len() as u16) + start_addr) < 1024) || + ((((vec.len()/2) as u16) + start_addr) < 1024) { + return Err(Error::DDSRAMError); + } + + // TODO: Convert argument into bytes for RAM + let mut byte_vec: ArrayVec<[u8; 8192]> = ArrayVec::new(); + match ram_dst { + RAMDestination::Frequency => { + for freq in vec.into_iter() { + let ftw = self.frequency_to_ftw(freq); + byte_vec.push(((ftw >> 24) & 0xFF) as u8); + byte_vec.push(((ftw >> 16) & 0xFF) as u8); + byte_vec.push(((ftw >> 8) & 0xFF) as u8); + byte_vec.push(((ftw >> 0) & 0xFF) as u8); + } + } + RAMDestination::Phase => { + for deg in vec.into_iter() { + let pow = self.degree_to_pow(deg); + byte_vec.push(((pow >> 8) & 0xFF) as u8); + byte_vec.push(((pow >> 0) & 0xFF) as u8); + byte_vec.push(0); + byte_vec.push(0); + } + } + RAMDestination::Amplitude => { + for amp in vec.into_iter() { + let asf = self.amplitude_to_asf(amp); + byte_vec.push(((asf >> 8) & 0xFF) as u8); + byte_vec.push(((asf << 2) & 0xFC) as u8); + byte_vec.push(0); + byte_vec.push(0); + } + } + RAMDestination::Polar => { + // Alternate phase and amplitude + let mut phase = true; + for pol in vec.into_iter() { + if phase { + let pow = self.degree_to_pow(pol); + byte_vec.push(((pow >> 8) & 0xFF) as u8); + byte_vec.push(((pow >> 0) & 0xFF) as u8); + phase = false; + } else { + let asf = self.amplitude_to_asf(pol); + byte_vec.push(((asf >> 8) & 0xFF) as u8); + byte_vec.push(((asf << 2) & 0xFC) as u8); + phase = true; + } + } + if phase { + return Err(Error::DDSRAMError); + } + } + } + let data = byte_vec.as_slice(); + self.set_ram_profile(profile, start_addr, start_addr + (((data.len()/4) - 1) as u16), + ram_dst, no_dwell_high, zero_crossing, op_mode, playback_rate, data) + } + /* * Configure a RAM mode profile - * + * TODO: Possibly remove redundant end_addr parameter. + * This can be inferred by start_addr and data size. */ pub fn set_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, ram_dst: RAMDestination, no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, data: &[u8] ) -> Result<(), Error> { - // Check the legality of this setup + // Check the legality of the profile setup assert!(profile < 7); assert!(end_addr >= start_addr); assert!(end_addr < 1024); @@ -513,8 +574,8 @@ where // Calculate address step rate, and check legality let step_rate = (self.f_sys_clk/(4.0 * playback_rate)) as u64; - if (step_rate == 0 || step_rate > 0xFFFF) { - return Err(Error::ParameterError); + if step_rate == 0 || step_rate > 0xFFFF { + return Err(Error::DDSRAMError); } // Before setting up RAM, disable RAM_ENABLE @@ -533,7 +594,7 @@ where ])?; // Temporarily disable RAM mode while accessing into RAM - self.disable_ram_configuration(); + self.disable_ram_configuration()?; self.write_ram(data)?; // Properly configure start_addr and end_addr @@ -541,8 +602,26 @@ where } - // Helper function to write data in RAM - // Need address range for data size check + // Calculate ftw (frequency tuning word) + fn frequency_to_ftw(&mut self, f_out: f64) -> u32 { + let f_res: u64 = 1 << 32; + ((f_res as f64) * f_out / self.f_sys_clk) as u32 + } + + // Calculate pow (Phase Offset Word) + fn degree_to_pow(&mut self, phase_offset: f64) -> u16 { + // Calculate phase offset word (POW) + let phase_res: u64 = 1 << 16; + ((phase_res as f64) * phase_offset / 360.0) as u16 + } + + // Calculate asf (Amplitude Scale Factor) + fn amplitude_to_asf(&mut self, amplitude: f64) -> u16 { + let amp_res: u64 = 0x3FFF; + ((amp_res as f64) * amplitude) as u16 + } + + // Write data in RAM fn write_ram(&mut self, data: &[u8]) -> Result<(), Error> { let mut vec: ArrayVec<[u8; 8192]> = ArrayVec::new(); vec.try_push(0x16)