From 6c2bc22b7add42f5372081589b7a83d5b65c1f40 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 17 Nov 2020 14:23:56 +0100 Subject: [PATCH] Adding updates for QSPI streaming --- ad9959/src/lib.rs | 55 +++----------- src/main.rs | 148 ++++++++++++++++++-------------------- src/pounder/dds_output.rs | 32 ++------- src/pounder/mod.rs | 87 +++------------------- 4 files changed, 90 insertions(+), 232 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index b1a4001..c9935d8 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -501,52 +501,8 @@ impl Ad9959 { / (1u64 << 32) as f32) } - pub fn serialize_profile( - &self, - channel: Channel, - freq: f32, - turns: f32, - amplitude: f32, - ) -> Result<[u32; 4], Error> { - let csr: u8 = *0x00_u8 - .set_bits(1..=2, self.communication_mode as u8) - .set_bit(4 + channel as usize, true); - - // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the - // frequency tuning word and f_s is the system clock rate. - let tuning_word: u32 = ((freq * (1u64 << 32) as f32) - / self.system_clock_frequency()) - as u32; - - let phase_offset: u16 = (turns * (1 << 14) as f32) as u16 & 0x3FFFu16; - let pow: u32 = *0u32 - .set_bits(24..32, Register::CPOW0 as u32) - .set_bits(8..24, phase_offset as u32) - .set_bits(0..8, Register::CFTW0 as u32); - - // Enable the amplitude multiplier for the channel if required. The amplitude control has - // full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever - // full-scale is used. - let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16; - - let acr: u32 = *0u32 - .set_bits(24..32, Register::ACR as u32) - .set_bits(0..10, amplitude_control as u32 & 0x3FF) - .set_bit(12, amplitude_control < (1 << 10)); - - let serialized: [u32; 4] = [ - u32::from_le_bytes([ - Register::CSR as u8, - csr, - Register::CSR as u8, - csr, - ]), - acr.to_be(), - pow.to_be(), - tuning_word.to_be(), - ]; - - Ok(serialized) + pub fn free(self) -> I { + self.interface } } @@ -578,7 +534,12 @@ impl ProfileSerializer { } } -pub fn serialize_profile(channel: Channel, ftw: u32, pow: u16, acr: Option) -> [u32; 4] { +pub fn serialize_profile( + channel: Channel, + ftw: u32, + pow: u16, + acr: Option, +) -> [u32; 4] { let mut serializer = ProfileSerializer::new(); let csr: u8 = *0x00_u8 diff --git a/src/main.rs b/src/main.rs index b0666fd..bdede85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,7 +55,7 @@ use heapless::{consts::*, String}; const SAMPLE_FREQUENCY_KHZ: u32 = 500; // The desired ADC sample processing buffer size. -const SAMPLE_BUFFER_SIZE: usize = 8; +const SAMPLE_BUFFER_SIZE: usize = 1; #[link_section = ".sram3.eth"] static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); @@ -194,7 +194,7 @@ const APP: () = { eeprom_i2c: hal::i2c::I2c, - dds_output: DdsOutput, + dds_output: Option, // Note: It appears that rustfmt generates a format that GDB cannot recognize, which // results in GDB breakpoints being set improperly. @@ -460,8 +460,9 @@ const APP: () = { // Measure the Pounder PGOOD output to detect if pounder is present on Stabilizer. let pounder_pgood = gpiob.pb13.into_pull_down_input(); delay.delay_ms(2u8); - let pounder_devices = if pounder_pgood.is_high().unwrap() { - let ad9959 = { + let (pounder_devices, dds_output) = if pounder_pgood.is_high().unwrap() + { + let mut ad9959 = { let qspi_interface = { // Instantiate the QUADSPI pins and peripheral interface. let qspi_pins = { @@ -592,20 +593,60 @@ const APP: () = { let adc1_in_p = gpiof.pf11.into_analog(); let adc2_in_p = gpiof.pf14.into_analog(); - Some( - pounder::PounderDevices::new( - io_expander, - ad9959, - spi, - adc1, - adc2, - adc1_in_p, - adc2_in_p, - ) - .unwrap(), + let pounder_devices = pounder::PounderDevices::new( + io_expander, + &mut ad9959, + spi, + adc1, + adc2, + adc1_in_p, + adc2_in_p, ) + .unwrap(); + + let dds_output = { + let io_update_trigger = { + let _io_update = gpiog + .pg7 + .into_alternate_af2() + .set_speed(hal::gpio::Speed::VeryHigh); + + // Configure the IO_Update signal for the DDS. + let mut hrtimer = hrtimer::HighResTimerE::new( + dp.HRTIM_TIME, + dp.HRTIM_MASTER, + dp.HRTIM_COMMON, + ccdr.clocks, + ccdr.peripheral.HRTIM, + ); + + // IO_Update should be latched for 50ns after the QSPI profile write. Profile writes + // are always 16 bytes, with 2 cycles required per byte, coming out to a total of 32 + // QSPI clock cycles. The QSPI is configured for 40MHz, so this comes out to an + // offset of 800nS. We use 900ns to be safe - note that the timer is triggered after + // the QSPI write, which can take approximately 120nS, so there is additional + // margin. + hrtimer.configure_single_shot( + hrtimer::Channel::Two, + 50_e-9, + 900_e-9, + ); + + // Ensure that we have enough time for an IO-update every sample. + assert!( + 1.0 / (1000 * SAMPLE_FREQUENCY_KHZ) as f32 > 900_e-9 + ); + + hrtimer + }; + + let qspi = ad9959.free(); + DdsOutput::new(qspi, io_update_trigger) + }; + + (Some(pounder_devices), Some(dds_output)) } else { - None + (None, None) }; let mut eeprom_i2c = { @@ -728,49 +769,6 @@ const APP: () = { // Utilize the cycle counter for RTIC scheduling. cp.DWT.enable_cycle_counter(); - let dds_output = { - let io_update_trigger = { - let _io_update = gpiog - .pg7 - .into_alternate_af2() - .set_speed(hal::gpio::Speed::VeryHigh); - - // Configure the IO_Update signal for the DDS. - let mut hrtimer = hrtimer::HighResTimerE::new( - dp.HRTIM_TIME, - dp.HRTIM_MASTER, - dp.HRTIM_COMMON, - ccdr.clocks, - ccdr.peripheral.HRTIM, - ); - - // IO_Update should be latched for 50ns after the QSPI profile write. Profile writes - // are always 16 bytes, with 2 cycles required per byte, coming out to a total of 32 - // QSPI clock cycles. The QSPI is configured for 40MHz, so this comes out to an - // offset of 800nS. We use 900ns to be safe - note that the timer is triggered after - // the QSPI write, which can take approximately 120nS, so there is additional - // margin. - hrtimer.configure_single_shot( - hrtimer::Channel::Two, - 50_e-9, - 900_e-9, - ); - - // Ensure that we have enough time for an IO-update every sample. - assert!(1.0 / (1000 * SAMPLE_FREQUENCY_KHZ) as f32 > 900_e-9); - - hrtimer - }; - - let timer3 = dp.TIM3.timer( - SAMPLE_FREQUENCY_KHZ.khz(), - ccdr.peripheral.TIM3, - &ccdr.clocks, - ); - - DdsOutput::new(timer3, io_update_trigger) - }; - // Start sampling ADCs. sampling_timer.start(); @@ -781,7 +779,6 @@ const APP: () = { adcs, dacs, dds_output, - pounder: pounder_devices, eeprom_i2c, @@ -791,13 +788,8 @@ const APP: () = { } } - #[task(binds = TIM3, resources=[dds_output], priority = 3)] - fn dds_update(c: dds_update::Context) { - c.resources.dds_output.update_handler(); - } - - #[task(binds=DMA1_STR3, resources=[adcs, dacs, pounder, dds_output, iir_state, iir_ch], priority=2)] - fn adc_update(mut c: adc_update::Context) { + #[task(binds=DMA1_STR3, resources=[adcs, dacs, dds_output, iir_state, iir_ch], priority=2)] + fn adc_update(c: adc_update::Context) { let (adc0_samples, adc1_samples) = c.resources.adcs.transfer_complete_handler(); @@ -820,25 +812,23 @@ const APP: () = { .update(&mut c.resources.iir_state[1], x1); y1 as i16 as u16 ^ 0x8000 }; + } - if c.resources.pounder.is_some() { - let profile = ad9959::serialize_profile( - pounder::Channel::Out0.into(), - u32::MAX / 4, - 0, - None, - ); + if let Some(dds_output) = c.resources.dds_output { + let profile = ad9959::serialize_profile( + pounder::Channel::Out0.into(), + u32::MAX / 4, + 0, + None, + ); - c.resources.dds_output.lock(|dds_output| { - dds_output.push(profile); - }); - } + dds_output.write_profile(profile); } c.resources.dacs.next_data(&dac0, &dac1); } - #[idle(resources=[net_interface, pounder, mac_addr, eth_mac, iir_state, iir_ch, afe0, afe1])] + #[idle(resources=[net_interface, mac_addr, eth_mac, iir_state, iir_ch, afe0, afe1])] fn idle(mut c: idle::Context) -> ! { let mut socket_set_entries: [_; 8] = Default::default(); let mut sockets = diff --git a/src/pounder/dds_output.rs b/src/pounder/dds_output.rs index b847d28..f242033 100644 --- a/src/pounder/dds_output.rs +++ b/src/pounder/dds_output.rs @@ -1,43 +1,21 @@ +use super::QspiInterface; use crate::hrtimer::HighResTimerE; use stm32h7xx_hal as hal; pub struct DdsOutput { - profiles: heapless::spsc::Queue<[u32; 4], heapless::consts::U32>, - update_timer: hal::timer::Timer, + _qspi: QspiInterface, io_update_trigger: HighResTimerE, } impl DdsOutput { - pub fn new( - mut timer: hal::timer::Timer, - io_update_trigger: HighResTimerE, - ) -> Self { - timer.pause(); - timer.reset_counter(); - timer.clear_uif_bit(); - timer.listen(hal::timer::Event::TimeOut); - + pub fn new(_qspi: QspiInterface, io_update_trigger: HighResTimerE) -> Self { Self { - update_timer: timer, + _qspi, io_update_trigger, - profiles: heapless::spsc::Queue::new(), } } - pub fn update_handler(&mut self) { - self.update_timer.clear_uif_bit(); - match self.profiles.dequeue() { - Some(profile) => self.write_profile(profile), - None => self.update_timer.pause(), - } - } - - pub fn push(&mut self, profile: [u32; 4]) { - self.profiles.enqueue(profile).unwrap(); - self.update_timer.resume(); - } - - fn write_profile(&mut self, profile: [u32; 4]) { + pub fn write_profile(&mut self, profile: [u32; 4]) { let regs = unsafe { &*hal::stm32::QUADSPI::ptr() }; unsafe { core::ptr::write_volatile( diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index d8db728..d20f200 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -262,7 +262,6 @@ impl ad9959::Interface for QspiInterface { /// A structure containing implementation for Pounder hardware. pub struct PounderDevices { - pub ad9959: ad9959::Ad9959, mcp23017: mcp23017::MCP23017>, attenuator_spi: hal::spi::Spi, adc1: hal::adc::Adc, @@ -283,7 +282,7 @@ impl PounderDevices { /// * `adc2_in_p` - The input channel for the RF power measurement on IN1. pub fn new( mcp23017: mcp23017::MCP23017>, - ad9959: ad9959::Ad9959, + ad9959: &mut ad9959::Ad9959, attenuator_spi: hal::spi::Spi, adc1: hal::adc::Adc, adc2: hal::adc::Adc, @@ -292,7 +291,6 @@ impl PounderDevices { ) -> Result { let mut devices = Self { mcp23017, - ad9959, attenuator_spi, adc1, adc2, @@ -317,87 +315,18 @@ impl PounderDevices { .map_err(|_| Error::I2c)?; // Select the on-board clock with a 4x prescaler (400MHz). - devices.select_onboard_clock(4u8)?; - - // Run the DDS in stream-only mode (no read support). - devices.ad9959.interface.start_stream().unwrap(); - - Ok(devices) - } - - /// Select the an external for the DDS reference clock source. - /// - /// Args: - /// * `frequency` - The frequency of the external clock source. - /// * `multiplier` - The multiplier of the reference clock to use in the DDS. - fn select_external_clock( - &mut self, - frequency: f32, - prescaler: u8, - ) -> Result<(), Error> { - self.mcp23017 - .digital_write(EXT_CLK_SEL_PIN, true) - .map_err(|_| Error::I2c)?; - self.ad9959 - .configure_system_clock(frequency, prescaler) - .map_err(|_| Error::Dds)?; - - Ok(()) - } - - /// Select the onboard oscillator for the DDS reference clock source. - /// - /// Args: - /// * `multiplier` - The multiplier of the reference clock to use in the DDS. - fn select_onboard_clock(&mut self, multiplier: u8) -> Result<(), Error> { - self.mcp23017 + devices + .mcp23017 .digital_write(EXT_CLK_SEL_PIN, false) .map_err(|_| Error::I2c)?; - self.ad9959 - .configure_system_clock(100_000_000f32, multiplier) + ad9959 + .configure_system_clock(100_000_000f32, 4) .map_err(|_| Error::Dds)?; - Ok(()) - } + // Run the DDS in stream-only mode (no read support). + ad9959.interface.start_stream().unwrap(); - /// Configure the Pounder DDS clock. - /// - /// Args: - /// * `config` - The configuration of the DDS clock desired. - pub fn configure_dds_clock( - &mut self, - config: DdsClockConfig, - ) -> Result<(), Error> { - if config.external_clock { - self.select_external_clock( - config.reference_clock, - config.multiplier, - ) - } else { - self.select_onboard_clock(config.multiplier) - } - } - - /// Get the pounder DDS clock configuration - /// - /// Returns: - /// The current pounder DDS clock configuration. - pub fn get_dds_clock_config(&mut self) -> Result { - let external_clock = self - .mcp23017 - .digital_read(EXT_CLK_SEL_PIN) - .map_err(|_| Error::I2c)?; - let multiplier = self - .ad9959 - .get_reference_clock_multiplier() - .map_err(|_| Error::Dds)?; - let reference_clock = self.ad9959.get_reference_clock_frequency(); - - Ok(DdsClockConfig { - multiplier, - reference_clock, - external_clock, - }) + Ok(devices) } }