Refactoring DDS output control

master
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 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(
pounder::PounderDevices::new(
io_expander,
ad9959,
io_update_trigger,
spi,
adc1,
adc2,
@ -763,13 +729,46 @@ const APP: () = {
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)
DdsOutput::new(timer3, io_update_trigger)
};
// 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) {
if let Some(pounder) = c.resources.pounder {
if let Some(profile) = c.resources.dds_output.update_handler() {
pounder.ad9959.interface.write_profile(profile).unwrap();
pounder.io_update_trigger.trigger();
}
}
c.resources.dds_output.update_handler();
}
#[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;
c.resources.pounder.lock(|pounder| {
if let Some(pounder) = pounder {
dds_output.lock(|dds_output| {
let profile = pounder
.ad9959
.serialize_profile(
pounder::Channel::Out0.into(),
100_000_000_f32,
0.0_f32,
*adc0 as f32 / 0xFFFF as f32,
)
.unwrap();
dds_output.push(profile);
});
}
});
if let Some(pounder) = c.resources.pounder {
dds_output.lock(|dds_output| {
let profile = pounder
.ad9959
.serialize_profile(
pounder::Channel::Out0.into(),
100_000_000_f32,
0.0_f32,
*adc0 as f32 / 0xFFFF as f32,
)
.unwrap();
dds_output.push(profile);
});
}
}
c.resources.dacs.next_data(&dac0, &dac1);
@ -944,42 +936,6 @@ const APP: () = {
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| {
c.resources.pounder.lock(|pounder| {
match pounder {

View File

@ -7,7 +7,6 @@ mod rf_power;
pub use dds_output::DdsOutput;
use super::hal;
use super::hrtimer::HighResTimerE;
use attenuators::AttenuatorInterface;
use rf_power::PowerMeasurementInterface;
@ -37,6 +36,7 @@ pub enum Error {
}
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
pub enum Channel {
In0,
In1,
@ -291,7 +291,6 @@ impl ad9959::Interface for QspiInterface {
/// A structure containing implementation for Pounder hardware.
pub struct PounderDevices {
pub ad9959: ad9959::Ad9959<QspiInterface>,
pub io_update_trigger: HighResTimerE,
mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
attenuator_spi: hal::spi::Spi<hal::stm32::SPI1, hal::spi::Enabled, u8>,
adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>,
@ -306,7 +305,6 @@ impl PounderDevices {
/// Args:
/// * `ad9959` - The DDS driver for the pounder hardware.
/// * `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.
/// * `adc2` - The ADC2 peripheral for measuring power.
/// * `adc1_in_p` - The input channel for the RF power measurement on IN0.
@ -314,7 +312,6 @@ impl PounderDevices {
pub fn new(
mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
ad9959: ad9959::Ad9959<QspiInterface>,
io_update_trigger: HighResTimerE,
attenuator_spi: hal::spi::Spi<hal::stm32::SPI1, hal::spi::Enabled, u8>,
adc1: hal::adc::Adc<hal::stm32::ADC1, hal::adc::Enabled>,
adc2: hal::adc::Adc<hal::stm32::ADC2, hal::adc::Enabled>,
@ -323,7 +320,6 @@ impl PounderDevices {
) -> Result<Self, Error> {
let mut devices = Self {
mcp23017,
io_update_trigger,
ad9959,
attenuator_spi,
adc1,
@ -431,33 +427,6 @@ impl PounderDevices {
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 {