diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index d5de1ec..206d90a 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -172,6 +172,17 @@ impl Ad9959 { // Set the clock frequency to configure the device as necessary. ad9959.configure_system_clock(clock_frequency, multiplier)?; + + // Latch the new clock configuration. + io_update.set_high().or(Err(Error::Pin))?; + + // Delay for at least 1 SYNC_CLK period for the update to occur. The SYNC_CLK is guaranteed + // to be at least 250KHz (1/4 of 1MHz minimum REF_CLK). We use 5uS instead of 4uS to + // guarantee conformance with datasheet requirements. + delay.delay_us(5); + + io_update.set_low().or(Err(Error::Pin))?; + Ok(ad9959) } @@ -195,7 +206,7 @@ impl Ad9959 { /// /// Returns: /// The actual frequency configured for the internal system clock. - pub fn configure_system_clock( + fn configure_system_clock( &mut self, reference_clock_frequency: f32, multiplier: u8, diff --git a/src/design_parameters.rs b/src/design_parameters.rs index 414a9e2..98490df 100644 --- a/src/design_parameters.rs +++ b/src/design_parameters.rs @@ -7,3 +7,12 @@ pub const ADC_DAC_SCK_MHZ_MAX: u32 = 50; /// The optimal counting frequency of the hardware timers used for timestamping and sampling. pub const TIMER_FREQUENCY_MHZ: u32 = 100; + +/// The DDS reference clock frequency in MHz. +pub const DDS_REF_CLK_MHZ: u32 = 100; + +/// The multiplier used for the DDS reference clock PLL. +pub const DDS_MULTIPLIER: u8 = 5; + +/// The rate of the DDS SYNC_CLK in MHz is always 1/4 that of the internal PLL clock. +pub const DDS_SYNC_CLK_MHZ: u32 = DDS_REF_CLK_MHZ * DDS_MULTIPLIER as u32 / 4; diff --git a/src/main.rs b/src/main.rs index 30ed33c..18931c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -527,7 +527,7 @@ const APP: () = { delay.delay_ms(2u8); let (pounder_devices, dds_output) = if pounder_pgood.is_high().unwrap() { - let mut ad9959 = { + let ad9959 = { let qspi_interface = { // Instantiate the QUADSPI pins and peripheral interface. let qspi_pins = { @@ -580,8 +580,8 @@ const APP: () = { &mut io_update, &mut delay, ad9959::Mode::FourBitSerial, - 100_000_000_f32, - 5, + design_parameters::DDS_REF_CLK_MHZ as f32 * 1_000_000_f32, + design_parameters::DDS_MULTIPLIER, ) .unwrap(); @@ -660,7 +660,6 @@ const APP: () = { let pounder_devices = pounder::PounderDevices::new( io_expander, - &mut ad9959, spi, adc1, adc2, @@ -686,8 +685,8 @@ const APP: () = { ); // IO_Update should be latched for 4 SYNC_CLK cycles after the QSPI profile - // write. With pounder SYNC_CLK running at 100MHz (1/4 of the pounder reference - // clock of 400MHz), this corresponds to 40ns. To accomodate rounding errors, we + // write. With pounder SYNC_CLK running at 125MHz (1/4 of the pounder reference + // clock of 500MHz), this corresponds to 32ns. To accomodate rounding errors, we // use 50ns instead. // // Profile writes are always 16 bytes, with 2 cycles required per byte, coming @@ -859,8 +858,21 @@ const APP: () = { let tim8 = dp.TIM8.timer(1.khz(), ccdr.peripheral.TIM8, &ccdr.clocks); let mut timestamp_timer = timers::PounderTimestampTimer::new(tim8); + + // Pounder is configured to generate a 400MHz reference clock, so a 125MHz sync-clock is + // output. As a result, dividing the 125MHz sync-clk provides a 31.25MHz tick rate for + // the timestamp timer. 31.25MHz corresponds with a 32ns tick rate. timestamp_timer.set_external_clock(timers::Prescaler::Div4); - timestamp_timer.set_period(128); + timestamp_timer.start(); + + // We want the pounder timestamp timer to overflow once per batch. + let tick_ratio = design_parameters::DDS_SYNC_CLK_MHZ as f32 + / design_parameters::TIMER_FREQUENCY_MHZ as f32; + let period = (tick_ratio + * ADC_SAMPLE_TICKS as f32 + * SAMPLE_BUFFER_SIZE as f32) as u32 + / 4; + timestamp_timer.set_period((period - 1).try_into().unwrap()); let tim8_channels = timestamp_timer.channels(); pounder::timestamp::Timestamper::new( diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index 36a73fd..2811e92 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -275,7 +275,6 @@ impl PounderDevices { /// Construct and initialize pounder-specific hardware. /// /// Args: - /// * `ad9959` - The DDS driver for the pounder hardware. /// * `attenuator_spi` - A SPI interface to control digital attenuators. /// * `adc1` - The ADC1 peripheral for measuring power. /// * `adc2` - The ADC2 peripheral for measuring power. @@ -283,7 +282,6 @@ impl PounderDevices { /// * `adc2_in_p` - The input channel for the RF power measurement on IN1. pub fn new( mcp23017: mcp23017::MCP23017>, - ad9959: &mut ad9959::Ad9959, attenuator_spi: hal::spi::Spi, adc1: hal::adc::Adc, adc2: hal::adc::Adc, @@ -315,14 +313,10 @@ impl PounderDevices { .write_gpio(mcp23017::Port::GPIOB, 1 << 5) .map_err(|_| Error::I2c)?; - // Select the on-board clock with a 4x prescaler (400MHz). devices .mcp23017 .digital_write(EXT_CLK_SEL_PIN, false) .map_err(|_| Error::I2c)?; - ad9959 - .configure_system_clock(100_000_000f32, 4) - .map_err(|_| Error::Dds)?; Ok(devices) } diff --git a/src/timers.rs b/src/timers.rs index 1292dab..10f300f 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -29,6 +29,8 @@ pub enum TriggerSource { } pub enum Prescaler { + Div1 = 0b00, + Div2 = 0b01, Div4 = 0b10, Div8 = 0b11, } @@ -100,17 +102,17 @@ macro_rules! timer_channels { let regs = unsafe { &*hal::stm32::$TY::ptr() }; regs.smcr.modify(|_, w| w.etps().bits(prescaler as u8).ece().set_bit()); - // Use a DIV4 prescaler. - + // Clear any other prescaler configuration. + regs.psc.write(|w| w.psc().bits(0)); } /// Start the timer. #[allow(dead_code)] - pub fn start(mut self) { + pub fn start(&mut self) { // Force a refresh of the frequency settings. self.timer.apply_freq(); - self.timer.reset_counter(); + self.timer.resume(); }