From 32112614883c438fcf79e6591c8b8ed67ec8ae7f Mon Sep 17 00:00:00 2001 From: occheung Date: Wed, 23 Sep 2020 17:29:20 +0800 Subject: [PATCH] dds: add ram control --- src/dds.rs | 256 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 164 insertions(+), 92 deletions(-) diff --git a/src/dds.rs b/src/dds.rs index f05ebd2..131c608 100644 --- a/src/dds.rs +++ b/src/dds.rs @@ -2,7 +2,8 @@ use embedded_hal::blocking::spi::Transfer; use crate::Error; use core::mem::size_of; use core::convert::TryInto; -use arrayvec::ArrayVec; +use heapless::Vec; +use heapless::consts::*; /* * Bitmask for all configurations (Order: CFR3, CFR2, CFR1) @@ -64,6 +65,8 @@ construct_bitmask!(DDSCFRMask; u32; const WRITE_MASK :u8 = 0x00; const READ_MASK :u8 = 0x80; +static mut RAM_VEC: Vec = Vec(heapless::i::Vec::new()); + #[derive(Clone, PartialEq)] pub enum RAMDestination { Frequency = 0, @@ -483,94 +486,167 @@ where } /* - * Configure a RAM mode profile, but with RAM data generated by a closure + * Configure a RAM mode profile, wrt supplied frequency data + * This will setup the static RAM_VEC by converting frequency to ftw */ - 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] + pub unsafe fn set_frequency_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + frequency_data: &[f64] ) -> Result<(), Error> { // Check the legality of the profile setup - assert!(profile < 7); + assert!(profile <= 7); assert!(end_addr >= start_addr); assert!(end_addr < 1024); - assert_eq!(data.len() as u16, (end_addr - start_addr + 1) * 4); + assert_eq!(frequency_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert frequency data into bytes recognized by DDS + for freq in frequency_data.iter() { + 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)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Frequency, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, wrt supplied amplitude data + * This will setup the static RAM_VEC by converting amplitude to asf + */ + pub unsafe fn set_amplitude_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + amplitude_data: &[f64] + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(amplitude_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert amplitude data into bytes recognized by DDS + for amp in amplitude_data.iter() { + let asf = self.amplitude_to_asf(*amp); + RAM_VEC.push(((asf >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((asf << 2) & 0xFC) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Amplitude, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, wrt supplied phase data + * This will setup the static RAM_VEC by converting phase to ftw + */ + pub unsafe fn set_phase_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + phase_data: &[f64] + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(phase_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert phase data into bytes recognized by DDS + for deg in phase_data.iter() { + let pow = self.degree_to_pow(*deg); + RAM_VEC.push(((pow >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((pow >> 0) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Phase, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, wrt supplied phase data + * This will setup the static RAM_VEC by converting phase to ftw + */ + pub unsafe fn set_polar_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + polar_data: &[(f64, f64)] + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(polar_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert amplitude data into bytes recognized by DDS + for (deg, amp) in polar_data.iter() { + let pow = self.degree_to_pow(*deg); + let asf = self.amplitude_to_asf(*amp); + RAM_VEC.push(((pow >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((pow >> 0) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((asf >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((asf << 2) & 0xFC) as u8) + .map_err(|_| Error::DDSRAMError)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Phase, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, w.r.t static vector (RAM_VEC) + */ + 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 + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + // assert_eq! RAM_VEC.len() as u16, ((end_addr - start_addr + 1) * 4) + 1); // Calculate address step rate, and check legality let step_rate = (self.f_sys_clk/(4.0 * playback_rate)) as u64; @@ -595,7 +671,9 @@ where // Temporarily disable RAM mode while accessing into RAM self.disable_ram_configuration()?; - self.write_ram(data)?; + unsafe { + self.write_ram()?; + } // Properly configure start_addr and end_addr self.enable_ram_configuration(ram_dst) @@ -622,14 +700,8 @@ where } // 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) - .map_err(|_| Error::DDSRAMError)?; - vec.try_extend_from_slice(data) - .map_err(|_| Error::DDSRAMError)?; - let mut data_slice = vec.as_mut_slice(); - self.spi.transfer(&mut data_slice) + unsafe fn write_ram(&mut self) -> Result<(), Error> { + self.spi.transfer(&mut RAM_VEC) .map(|_| ()) .map_err(Error::SPI) }