Adding documentation
This commit is contained in:
parent
91f722f450
commit
677d017c3c
@ -123,8 +123,6 @@ impl<I: Interface> Ad9959<I> {
|
|||||||
// Reset the AD9959
|
// Reset the AD9959
|
||||||
reset_pin.set_high().or(Err(Error::Pin))?;
|
reset_pin.set_high().or(Err(Error::Pin))?;
|
||||||
|
|
||||||
io_update.set_low().or(Err(Error::Pin))?;
|
|
||||||
|
|
||||||
// Delay for at least 1 SYNC_CLK period for the reset to occur. The SYNC_CLK is guaranteed
|
// Delay for at least 1 SYNC_CLK period for the reset to occur. The SYNC_CLK is guaranteed
|
||||||
// to be at least 250KHz (1/4 of 1MHz minimum REF_CLK).
|
// to be at least 250KHz (1/4 of 1MHz minimum REF_CLK).
|
||||||
delay.delay_us(5);
|
delay.delay_us(5);
|
||||||
@ -492,6 +490,13 @@ impl<I: Interface> Ad9959<I> {
|
|||||||
/ (1u64 << 32) as f32)
|
/ (1u64 << 32) as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finalize DDS configuration
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This is intended for when the DDS profiles will be written as a stream of data to the DDS.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// (I, config) where `I` is the interface to the DDS and `config` is the frozen `DdsConfig`.
|
||||||
pub fn freeze(self) -> (I, DdsConfig) {
|
pub fn freeze(self) -> (I, DdsConfig) {
|
||||||
let config = DdsConfig {
|
let config = DdsConfig {
|
||||||
mode: self.communication_mode,
|
mode: self.communication_mode,
|
||||||
@ -500,16 +505,20 @@ impl<I: Interface> Ad9959<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The frozen DDS configuration.
|
||||||
pub struct DdsConfig {
|
pub struct DdsConfig {
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DdsConfig {
|
impl DdsConfig {
|
||||||
|
/// Create a serializer that can be used for generating a serialized DDS profile for writing to
|
||||||
|
/// a QSPI stream.
|
||||||
pub fn builder(&self) -> ProfileSerializer {
|
pub fn builder(&self) -> ProfileSerializer {
|
||||||
ProfileSerializer::new(self.mode)
|
ProfileSerializer::new(self.mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a means of serializing a DDS profile for writing to a stream.
|
||||||
pub struct ProfileSerializer {
|
pub struct ProfileSerializer {
|
||||||
data: [u8; 16],
|
data: [u8; 16],
|
||||||
index: usize,
|
index: usize,
|
||||||
@ -517,6 +526,10 @@ pub struct ProfileSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ProfileSerializer {
|
impl ProfileSerializer {
|
||||||
|
/// Construct a new serializer.
|
||||||
|
///
|
||||||
|
/// # Args
|
||||||
|
/// * `mode` - The communication mode of the DDS.
|
||||||
fn new(mode: Mode) -> Self {
|
fn new(mode: Mode) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mode,
|
mode,
|
||||||
@ -525,6 +538,13 @@ impl ProfileSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update a number of channels with the requested profile.
|
||||||
|
///
|
||||||
|
/// # 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.
|
||||||
pub fn update_channels(
|
pub fn update_channels(
|
||||||
&mut self,
|
&mut self,
|
||||||
channels: &[Channel],
|
channels: &[Channel],
|
||||||
@ -532,12 +552,12 @@ impl ProfileSerializer {
|
|||||||
pow: Option<u16>,
|
pow: Option<u16>,
|
||||||
acr: Option<u16>,
|
acr: Option<u16>,
|
||||||
) {
|
) {
|
||||||
// If there are no updates requested, skip this update cycle.
|
// The user should have provided something to update.
|
||||||
if (ftw.is_none() && acr.is_none() && pow.is_none())
|
assert!(
|
||||||
|| channels.len() == 0
|
(ftw.is_some() || acr.is_some() || pow.is_some())
|
||||||
{
|
&& channels.len() > 0
|
||||||
panic!("Invalid config");
|
);
|
||||||
}
|
|
||||||
let mut csr: u8 = *0u8.set_bits(1..3, self.mode as u8);
|
let mut csr: u8 = *0u8.set_bits(1..3, self.mode as u8);
|
||||||
for channel in channels.iter() {
|
for channel in channels.iter() {
|
||||||
csr.set_bit(4 + *channel as usize, true);
|
csr.set_bit(4 + *channel as usize, true);
|
||||||
@ -558,8 +578,25 @@ impl ProfileSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a register write to the serialization data.
|
||||||
|
fn add_write(&mut self, register: Register, value: &[u8]) {
|
||||||
|
let data = &mut self.data[self.index..];
|
||||||
|
assert!(value.len() + 1 <= data.len());
|
||||||
|
|
||||||
|
data[0] = register as u8;
|
||||||
|
data[1..][..value.len()].copy_from_slice(value);
|
||||||
|
self.index += value.len() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the serialized profile as a slice of 32-bit words.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// The serialized profile will be padded to the next 32-bit word boundary by adding dummy
|
||||||
|
/// writes to the CSR or FR2 registers.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A slice of `u32` words representing the serialized profile.
|
||||||
pub fn finalize<'a>(&'a mut self) -> &[u32] {
|
pub fn finalize<'a>(&'a mut self) -> &[u32] {
|
||||||
//&self.data[..self.index]
|
|
||||||
// Pad the buffer to 32-bit alignment by adding dummy writes to CSR and FR2.
|
// Pad the buffer to 32-bit alignment by adding dummy writes to CSR and FR2.
|
||||||
let padding = 4 - (self.index % 4);
|
let padding = 4 - (self.index % 4);
|
||||||
match padding {
|
match padding {
|
||||||
@ -581,13 +618,4 @@ impl ProfileSerializer {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_write(&mut self, register: Register, value: &[u8]) {
|
|
||||||
let data = &mut self.data[self.index..];
|
|
||||||
assert!(value.len() + 1 <= data.len());
|
|
||||||
|
|
||||||
data[0] = register as u8;
|
|
||||||
data[1..][..value.len()].copy_from_slice(value);
|
|
||||||
self.index += value.len() + 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
|
///! The HRTimer (High Resolution Timer) is used to generate IO_Update pulses to the Pounder DDS.
|
||||||
use crate::hal;
|
use crate::hal;
|
||||||
use hal::rcc::{rec, CoreClocks, ResetEnable};
|
use hal::rcc::{rec, CoreClocks, ResetEnable};
|
||||||
|
|
||||||
|
/// A HRTimer output channel.
|
||||||
pub enum Channel {
|
pub enum Channel {
|
||||||
One,
|
One,
|
||||||
Two,
|
Two,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The high resolution timer. Currently, only Timer E is supported.
|
||||||
pub struct HighResTimerE {
|
pub struct HighResTimerE {
|
||||||
master: hal::stm32::HRTIM_MASTER,
|
master: hal::stm32::HRTIM_MASTER,
|
||||||
timer: hal::stm32::HRTIM_TIME,
|
timer: hal::stm32::HRTIM_TIME,
|
||||||
@ -15,6 +18,7 @@ pub struct HighResTimerE {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HighResTimerE {
|
impl HighResTimerE {
|
||||||
|
/// Construct a new high resolution timer for generating IO_update signals.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
timer_regs: hal::stm32::HRTIM_TIME,
|
timer_regs: hal::stm32::HRTIM_TIME,
|
||||||
master_regs: hal::stm32::HRTIM_MASTER,
|
master_regs: hal::stm32::HRTIM_MASTER,
|
||||||
@ -32,6 +36,18 @@ impl HighResTimerE {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configure the timer to operate in single-shot mode.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This will configure the timer to generate a single pulse on an output channel. The timer
|
||||||
|
/// will only count up once and must be `trigger()`'d after / configured.
|
||||||
|
///
|
||||||
|
/// The output will be asserted from `set_offset` to `set_offset` + `set_duration` in the count.
|
||||||
|
///
|
||||||
|
/// # Args
|
||||||
|
/// * `channel` - The timer output channel to configure.
|
||||||
|
/// * `set_duration` - The duration that the output should be asserted for.
|
||||||
|
/// * `set_offset` - The first time at which the output should be asserted.
|
||||||
pub fn configure_single_shot(
|
pub fn configure_single_shot(
|
||||||
&mut self,
|
&mut self,
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
@ -97,6 +113,7 @@ impl HighResTimerE {
|
|||||||
self.master.mcr.modify(|_, w| w.tecen().set_bit());
|
self.master.mcr.modify(|_, w| w.tecen().set_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a single trigger of the timer to start the output pulse generation.
|
||||||
pub fn trigger(&mut self) {
|
pub fn trigger(&mut self) {
|
||||||
// Generate a reset event to force the timer to start counting.
|
// Generate a reset event to force the timer to start counting.
|
||||||
self.common.cr2.write(|w| w.terst().set_bit());
|
self.common.cr2.write(|w| w.terst().set_bit());
|
||||||
|
@ -766,7 +766,6 @@ const APP: () = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
cp.SCB.enable_icache();
|
cp.SCB.enable_icache();
|
||||||
//cp.SCB.enable_dcache(&mut cp.CPUID);
|
|
||||||
|
|
||||||
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
|
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
|
||||||
// info!("Built on {}", build_info::BUILT_TIME_UTC);
|
// info!("Built on {}", build_info::BUILT_TIME_UTC);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
///! The DdsOutput is used as an output stream to the pounder DDS.
|
||||||
use super::QspiInterface;
|
use super::QspiInterface;
|
||||||
use crate::hrtimer::HighResTimerE;
|
use crate::hrtimer::HighResTimerE;
|
||||||
use ad9959::{Channel, DdsConfig, ProfileSerializer};
|
use ad9959::{Channel, DdsConfig, ProfileSerializer};
|
||||||
use stm32h7xx_hal as hal;
|
use stm32h7xx_hal as hal;
|
||||||
|
|
||||||
|
/// The DDS profile update stream.
|
||||||
pub struct DdsOutput {
|
pub struct DdsOutput {
|
||||||
_qspi: QspiInterface,
|
_qspi: QspiInterface,
|
||||||
io_update_trigger: HighResTimerE,
|
io_update_trigger: HighResTimerE,
|
||||||
@ -10,11 +12,23 @@ pub struct DdsOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DdsOutput {
|
impl DdsOutput {
|
||||||
|
/// 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.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
_qspi: QspiInterface,
|
mut qspi: QspiInterface,
|
||||||
io_update_trigger: HighResTimerE,
|
io_update_trigger: HighResTimerE,
|
||||||
dds_config: DdsConfig,
|
dds_config: DdsConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
qspi.start_stream();
|
||||||
Self {
|
Self {
|
||||||
config: dds_config,
|
config: dds_config,
|
||||||
_qspi,
|
_qspi,
|
||||||
@ -22,6 +36,7 @@ impl DdsOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a builder for serializing a Pounder DDS profile.
|
||||||
pub fn builder(&mut self) -> ProfileBuilder {
|
pub fn builder(&mut self) -> ProfileBuilder {
|
||||||
let builder = self.config.builder();
|
let builder = self.config.builder();
|
||||||
ProfileBuilder {
|
ProfileBuilder {
|
||||||
@ -30,9 +45,15 @@ impl DdsOutput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
fn write_profile(&mut self, profile: &[u32]) {
|
fn write_profile(&mut self, profile: &[u32]) {
|
||||||
assert!(profile.len() <= 16);
|
|
||||||
|
|
||||||
// Note(unsafe): We own the QSPI interface, so it is safe to access the registers in a raw
|
// Note(unsafe): We own the QSPI interface, so it is safe to access the registers in a raw
|
||||||
// fashion.
|
// fashion.
|
||||||
let regs = unsafe { &*hal::stm32::QUADSPI::ptr() };
|
let regs = unsafe { &*hal::stm32::QUADSPI::ptr() };
|
||||||
@ -47,17 +68,26 @@ impl DdsOutput {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger the IO_update signal generating timer to asynchronous create the IO_Update pulse.
|
// Trigger the IO_update signal generating timer to asynchronous create the IO_Update pulse.
|
||||||
self.io_update_trigger.trigger();
|
self.io_update_trigger.trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A temporary builder for serializing and writing profiles.
|
||||||
pub struct ProfileBuilder<'a> {
|
pub struct ProfileBuilder<'a> {
|
||||||
dds_stream: &'a mut DdsOutput,
|
dds_stream: &'a mut DdsOutput,
|
||||||
serializer: ProfileSerializer,
|
serializer: ProfileSerializer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ProfileBuilder<'a> {
|
impl<'a> ProfileBuilder<'a> {
|
||||||
|
/// 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.
|
||||||
pub fn update_channels(
|
pub fn update_channels(
|
||||||
mut self,
|
mut self,
|
||||||
channels: &[Channel],
|
channels: &[Channel],
|
||||||
@ -69,6 +99,7 @@ impl<'a> ProfileBuilder<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write the profile to the DDS asynchronously.
|
||||||
pub fn write_profile(mut self) {
|
pub fn write_profile(mut self) {
|
||||||
let profile = self.serializer.finalize();
|
let profile = self.serializer.finalize();
|
||||||
self.dds_stream.write_profile(profile);
|
self.dds_stream.write_profile(profile);
|
||||||
|
Loading…
Reference in New Issue
Block a user