Refactoring DDS output control

This commit is contained in:
Ryan Summers 2020-11-17 10:45:37 +01:00
parent 1c8e385e6d
commit 585613f48f
2 changed files with 51 additions and 126 deletions

View File

@ -592,44 +592,10 @@ const APP: () = {
let adc1_in_p = gpiof.pf11.into_analog(); let adc1_in_p = gpiof.pf11.into_analog();
let adc2_in_p = gpiof.pf14.into_analog(); let adc2_in_p = gpiof.pf14.into_analog();
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
};
Some( Some(
pounder::PounderDevices::new( pounder::PounderDevices::new(
io_expander, io_expander,
ad9959, ad9959,
io_update_trigger,
spi, spi,
adc1, adc1,
adc2, adc2,
@ -763,13 +729,46 @@ const APP: () = {
cp.DWT.enable_cycle_counter(); cp.DWT.enable_cycle_counter();
let dds_output = { 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( let timer3 = dp.TIM3.timer(
SAMPLE_FREQUENCY_KHZ.khz(), SAMPLE_FREQUENCY_KHZ.khz(),
ccdr.peripheral.TIM3, ccdr.peripheral.TIM3,
&ccdr.clocks, &ccdr.clocks,
); );
DdsOutput::new(timer3) DdsOutput::new(timer3, io_update_trigger)
}; };
// Start sampling ADCs. // Start sampling ADCs.
@ -792,14 +791,9 @@ const APP: () = {
} }
} }
#[task(binds = TIM3, resources=[dds_output, pounder], priority = 3)] #[task(binds = TIM3, resources=[dds_output], priority = 3)]
fn dds_update(c: dds_update::Context) { fn dds_update(c: dds_update::Context) {
if let Some(pounder) = c.resources.pounder { c.resources.dds_output.update_handler();
if let Some(profile) = c.resources.dds_output.update_handler() {
pounder.ad9959.interface.write_profile(profile).unwrap();
pounder.io_update_trigger.trigger();
}
}
} }
#[task(binds=DMA1_STR3, resources=[adcs, dacs, pounder, dds_output, iir_state, iir_ch], priority=2)] #[task(binds=DMA1_STR3, resources=[adcs, dacs, pounder, dds_output, iir_state, iir_ch], priority=2)]
@ -828,22 +822,20 @@ const APP: () = {
}; };
let dds_output = &mut c.resources.dds_output; let dds_output = &mut c.resources.dds_output;
c.resources.pounder.lock(|pounder| { if let Some(pounder) = c.resources.pounder {
if let Some(pounder) = pounder { dds_output.lock(|dds_output| {
dds_output.lock(|dds_output| { let profile = pounder
let profile = pounder .ad9959
.ad9959 .serialize_profile(
.serialize_profile( pounder::Channel::Out0.into(),
pounder::Channel::Out0.into(), 100_000_000_f32,
100_000_000_f32, 0.0_f32,
0.0_f32, *adc0 as f32 / 0xFFFF as f32,
*adc0 as f32 / 0xFFFF as f32, )
) .unwrap();
.unwrap(); dds_output.push(profile);
dds_output.push(profile); });
}); }
}
});
} }
c.resources.dacs.next_data(&dac0, &dac1); c.resources.dacs.next_data(&dac0, &dac1);
@ -944,42 +936,6 @@ const APP: () = {
Ok::<server::IirRequest, ()>(req) Ok::<server::IirRequest, ()>(req)
}) })
}), }),
"pounder/in0": pounder::ChannelState, (|state| {
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::In0, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/in1": pounder::ChannelState, (|state| {
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::In1, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/out0": pounder::ChannelState, (|state| {
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::Out0, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/out1": pounder::ChannelState, (|state| {
c.resources.pounder.lock(|pounder| {
match pounder {
Some(pounder) =>
pounder.set_channel_state(pounder::Channel::Out1, state),
_ => Err(pounder::Error::Access),
}
})
}),
"pounder/dds/clock": pounder::DdsClockConfig, (|config| { "pounder/dds/clock": pounder::DdsClockConfig, (|config| {
c.resources.pounder.lock(|pounder| { c.resources.pounder.lock(|pounder| {
match pounder { match pounder {

View File

@ -7,7 +7,6 @@ mod rf_power;
pub use dds_output::DdsOutput; pub use dds_output::DdsOutput;
use super::hal; use super::hal;
use super::hrtimer::HighResTimerE;
use attenuators::AttenuatorInterface; use attenuators::AttenuatorInterface;
use rf_power::PowerMeasurementInterface; use rf_power::PowerMeasurementInterface;
@ -37,6 +36,7 @@ pub enum Error {
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
pub enum Channel { pub enum Channel {
In0, In0,
In1, In1,
@ -291,7 +291,6 @@ impl ad9959::Interface for QspiInterface {
/// A structure containing implementation for Pounder hardware. /// A structure containing implementation for Pounder hardware.
pub struct PounderDevices { pub struct PounderDevices {
pub ad9959: ad9959::Ad9959<QspiInterface>, pub ad9959: ad9959::Ad9959<QspiInterface>,
pub io_update_trigger: HighResTimerE,
mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>, mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
attenuator_spi: hal::spi::Spi<hal::stm32::SPI1, hal::spi::Enabled, u8>, attenuator_spi: hal::spi::Spi<hal::stm32::SPI1, hal::spi::Enabled, u8>,
adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>, adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>,
@ -306,7 +305,6 @@ impl PounderDevices {
/// Args: /// Args:
/// * `ad9959` - The DDS driver for the pounder hardware. /// * `ad9959` - The DDS driver for the pounder hardware.
/// * `attenuator_spi` - A SPI interface to control digital attenuators. /// * `attenuator_spi` - A SPI interface to control digital attenuators.
/// * `io_update_timer` - The HRTimer with the IO_update signal connected to the output.
/// * `adc1` - The ADC1 peripheral for measuring power. /// * `adc1` - The ADC1 peripheral for measuring power.
/// * `adc2` - The ADC2 peripheral for measuring power. /// * `adc2` - The ADC2 peripheral for measuring power.
/// * `adc1_in_p` - The input channel for the RF power measurement on IN0. /// * `adc1_in_p` - The input channel for the RF power measurement on IN0.
@ -314,7 +312,6 @@ impl PounderDevices {
pub fn new( pub fn new(
mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>, mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
ad9959: ad9959::Ad9959<QspiInterface>, ad9959: ad9959::Ad9959<QspiInterface>,
io_update_trigger: HighResTimerE,
attenuator_spi: hal::spi::Spi<hal::stm32::SPI1, hal::spi::Enabled, u8>, attenuator_spi: hal::spi::Spi<hal::stm32::SPI1, hal::spi::Enabled, u8>,
adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>, adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>,
adc2: hal::adc::Adc<hal::stm32::ADC2, hal::adc::Enabled>, adc2: hal::adc::Adc<hal::stm32::ADC2, hal::adc::Enabled>,
@ -323,7 +320,6 @@ impl PounderDevices {
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut devices = Self { let mut devices = Self {
mcp23017, mcp23017,
io_update_trigger,
ad9959, ad9959,
attenuator_spi, attenuator_spi,
adc1, adc1,
@ -431,33 +427,6 @@ impl PounderDevices {
external_clock, external_clock,
}) })
} }
/// Configure a DDS channel.
///
/// Args:
/// * `channel` - The pounder channel to configure.
/// * `state` - The state to configure the channel for.
pub fn set_channel_state(
&mut self,
channel: Channel,
state: ChannelState,
) -> Result<(), Error> {
let profile = self
.ad9959
.serialize_profile(
channel.into(),
state.parameters.frequency,
state.parameters.phase_offset,
state.parameters.amplitude,
)
.map_err(|_| Error::Dds)?;
self.ad9959.interface.write_profile(profile).unwrap();
self.io_update_trigger.trigger();
self.set_attenuation(channel, state.attenuation)?;
Ok(())
}
} }
impl AttenuatorInterface for PounderDevices { impl AttenuatorInterface for PounderDevices {