Refactoring DDS output control
This commit is contained in:
parent
1c8e385e6d
commit
585613f48f
144
src/main.rs
144
src/main.rs
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue