pounder_test/src/pounder/dds_output.rs

112 lines
3.6 KiB
Rust
Raw Normal View History

2020-12-03 00:40:24 +08:00
///! The DdsOutput is used as an output stream to the pounder DDS.
2020-11-17 21:23:56 +08:00
use super::QspiInterface;
2020-11-17 17:51:31 +08:00
use crate::hrtimer::HighResTimerE;
2020-12-03 00:01:40 +08:00
use ad9959::{Channel, DdsConfig, ProfileSerializer};
2020-11-17 17:51:31 +08:00
use stm32h7xx_hal as hal;
2020-12-03 00:40:24 +08:00
/// The DDS profile update stream.
2020-11-17 17:51:31 +08:00
pub struct DdsOutput {
2020-11-17 21:23:56 +08:00
_qspi: QspiInterface,
2020-11-17 17:51:31 +08:00
io_update_trigger: HighResTimerE,
2020-12-03 00:01:40 +08:00
config: DdsConfig,
2020-11-17 17:51:31 +08:00
}
impl DdsOutput {
2020-12-03 00:40:24 +08:00
/// Construct a new DDS output stream.
///
/// # Note
/// It is assumed that the QSPI stream and the IO_Update trigger timer have been configured in a
/// way such that the profile has sufficient time to be written before the IO_Update signal is
/// generated.
///
/// # Args
/// * `qspi` - The QSPI interface to the run the stream on.
/// * `io_update_trigger` - The HighResTimerE used to generate IO_Update pulses.
/// * `dds_config` - The frozen DDS configuration.
2020-12-03 00:01:40 +08:00
pub fn new(
2020-12-03 00:40:24 +08:00
mut qspi: QspiInterface,
2020-12-03 00:01:40 +08:00
io_update_trigger: HighResTimerE,
dds_config: DdsConfig,
) -> Self {
2020-12-03 01:08:49 +08:00
qspi.start_stream().unwrap();
2020-11-17 17:51:31 +08:00
Self {
2020-12-03 00:01:40 +08:00
config: dds_config,
2020-12-03 01:08:49 +08:00
_qspi: qspi,
2020-11-17 17:51:31 +08:00
io_update_trigger,
}
}
2020-12-03 00:40:24 +08:00
/// Get a builder for serializing a Pounder DDS profile.
2020-12-03 00:01:40 +08:00
pub fn builder(&mut self) -> ProfileBuilder {
let builder = self.config.builder();
ProfileBuilder {
dds_stream: self,
serializer: builder,
}
}
2020-12-03 00:40:24 +08:00
/// Write a profile to the stream.
///
/// # Note:
/// If a profile of more than 4 words is provided, it is possible that the QSPI interface will
/// stall execution.
///
/// # Args
/// * `profile` - The serialized DDS profile to write.
2020-12-03 00:01:40 +08:00
fn write_profile(&mut self, profile: &[u32]) {
// Note(unsafe): We own the QSPI interface, so it is safe to access the registers in a raw
// fashion.
2020-11-17 17:51:31 +08:00
let regs = unsafe { &*hal::stm32::QUADSPI::ptr() };
2020-12-03 00:01:40 +08:00
2020-12-07 17:55:09 +08:00
if regs.sr.read().flevel() != 0 {
warn!("QSPI stalling")
}
2020-12-03 00:01:40 +08:00
for word in profile.iter() {
// Note(unsafe): We are writing to the SPI TX FIFO in a raw manner for performance. This
// is safe because we know the data register is a valid address to write to.
unsafe {
core::ptr::write_volatile(
&regs.dr as *const _ as *mut u32,
*word,
);
}
2020-11-17 17:51:31 +08:00
}
2020-12-03 00:40:24 +08:00
2020-11-17 17:51:31 +08:00
// Trigger the IO_update signal generating timer to asynchronous create the IO_Update pulse.
self.io_update_trigger.trigger();
}
}
2020-12-03 00:01:40 +08:00
2020-12-03 00:40:24 +08:00
/// A temporary builder for serializing and writing profiles.
2020-12-03 00:01:40 +08:00
pub struct ProfileBuilder<'a> {
dds_stream: &'a mut DdsOutput,
serializer: ProfileSerializer,
}
impl<'a> ProfileBuilder<'a> {
2020-12-03 00:40:24 +08:00
/// Update a number of channels with the provided configuration
///
/// # Args
/// * `channels` - A list of channels to apply the configuration to.
/// * `ftw` - If provided, indicates a frequency tuning word for the channels.
/// * `pow` - If provided, indicates a phase offset word for the channels.
/// * `acr` - If provided, indicates the amplitude control register for the channels.
2020-12-03 00:01:40 +08:00
pub fn update_channels(
mut self,
channels: &[Channel],
ftw: Option<u32>,
pow: Option<u16>,
acr: Option<u16>,
) -> Self {
self.serializer.update_channels(channels, ftw, pow, acr);
self
}
2020-12-03 00:40:24 +08:00
/// Write the profile to the DDS asynchronously.
2020-12-03 00:01:40 +08:00
pub fn write_profile(mut self) {
let profile = self.serializer.finalize();
self.dds_stream.write_profile(profile);
}
}