Updating code comments
This commit is contained in:
parent
818047775b
commit
c4979f08f6
|
@ -24,6 +24,7 @@ pub struct Ad9959<INTERFACE, DELAY, UPDATE> {
|
||||||
io_update: UPDATE,
|
io_update: UPDATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait that allows a HAL to provide a means of communicating with the AD9959.
|
||||||
pub trait Interface {
|
pub trait Interface {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
|
@ -34,6 +35,8 @@ pub trait Interface {
|
||||||
fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>;
|
fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to
|
||||||
|
/// the configuration bits of the DDS CSR register.
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
SingleBitTwoWire = 0b00,
|
SingleBitTwoWire = 0b00,
|
||||||
|
@ -103,6 +106,17 @@ where
|
||||||
UPDATE: OutputPin<Error = PinE>,
|
UPDATE: OutputPin<Error = PinE>,
|
||||||
|
|
||||||
{
|
{
|
||||||
|
/// Construct and initialize the DDS.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `interface` - An interface to the DDS.
|
||||||
|
/// * `reset_pin` - A pin connected to the DDS reset input.
|
||||||
|
/// * `io_update` - A pin connected to the DDS io_update input.
|
||||||
|
/// * `delay` - A delay implementation for blocking operation for specific amounts of time.
|
||||||
|
/// * `desired_mode` - The desired communication mode of the interface to the DDS.
|
||||||
|
/// * `clock_frequency` - The clock frequency of the reference clock input.
|
||||||
|
/// * `multiplier` - The desired clock multiplier for the system clock. This multiplies
|
||||||
|
/// `clock_frequency` to generate the system clock.
|
||||||
pub fn new<RST>(interface: INTERFACE,
|
pub fn new<RST>(interface: INTERFACE,
|
||||||
reset_pin: &mut RST,
|
reset_pin: &mut RST,
|
||||||
io_update: UPDATE,
|
io_update: UPDATE,
|
||||||
|
@ -158,6 +172,7 @@ where
|
||||||
Ok(ad9959)
|
Ok(ad9959)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Latch the DDS configuration to ensure it is active on the output channels.
|
||||||
fn latch_configuration(&mut self) -> Result<(), Error<InterfaceE>> {
|
fn latch_configuration(&mut self) -> Result<(), Error<InterfaceE>> {
|
||||||
self.io_update.set_high().or_else(|_| Err(Error::Pin))?;
|
self.io_update.set_high().or_else(|_| Err(Error::Pin))?;
|
||||||
// The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one
|
// The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one
|
||||||
|
@ -205,10 +220,12 @@ where
|
||||||
Ok(self.system_clock_frequency())
|
Ok(self.system_clock_frequency())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current reference clock frequency in Hz.
|
||||||
pub fn get_reference_clock_frequency(&self) -> f32 {
|
pub fn get_reference_clock_frequency(&self) -> f32 {
|
||||||
self.reference_clock_frequency
|
self.reference_clock_frequency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current reference clock multiplier.
|
||||||
pub fn get_reference_clock_multiplier(&mut self) -> Result<u8, Error<InterfaceE>> {
|
pub fn get_reference_clock_multiplier(&mut self) -> Result<u8, Error<InterfaceE>> {
|
||||||
let mut fr1: [u8; 3] = [0, 0, 0];
|
let mut fr1: [u8; 3] = [0, 0, 0];
|
||||||
self.interface.read(Register::FR1 as u8, &mut fr1)?;
|
self.interface.read(Register::FR1 as u8, &mut fr1)?;
|
||||||
|
@ -257,6 +274,7 @@ where
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current system clock frequency in Hz.
|
||||||
fn system_clock_frequency(&self) -> f64 {
|
fn system_clock_frequency(&self) -> f64 {
|
||||||
self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64
|
self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64
|
||||||
}
|
}
|
||||||
|
@ -281,6 +299,7 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine if an output channel is enabled.
|
||||||
pub fn is_enabled(&mut self, channel: Channel) -> Result<bool, Error<InterfaceE>> {
|
pub fn is_enabled(&mut self, channel: Channel) -> Result<bool, Error<InterfaceE>> {
|
||||||
let mut csr: [u8; 1] = [0; 1];
|
let mut csr: [u8; 1] = [0; 1];
|
||||||
self.interface.read(Register::CSR as u8, &mut csr)?;
|
self.interface.read(Register::CSR as u8, &mut csr)?;
|
||||||
|
@ -288,7 +307,15 @@ where
|
||||||
Ok(csr[0].get_bit(channel as usize + 4))
|
Ok(csr[0].get_bit(channel as usize + 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update an output channel configuration register.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The channel to configure.
|
||||||
|
/// * `register` - The register to update.
|
||||||
|
/// * `data` - The contents to write to the provided register.
|
||||||
fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error<InterfaceE>> {
|
fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error<InterfaceE>> {
|
||||||
|
// Disable all other outputs so that we can update the configuration register of only the
|
||||||
|
// specified channel.
|
||||||
let mut csr: [u8; 1] = [0];
|
let mut csr: [u8; 1] = [0];
|
||||||
self.interface.read(Register::CSR as u8, &mut csr)?;
|
self.interface.read(Register::CSR as u8, &mut csr)?;
|
||||||
|
|
||||||
|
@ -308,7 +335,15 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a configuration register of a specific channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The channel to read.
|
||||||
|
/// * `register` - The register to read.
|
||||||
|
/// * `data` - A location to store the read register contents.
|
||||||
fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error<InterfaceE>> {
|
fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error<InterfaceE>> {
|
||||||
|
// Disable all other channels in the CSR so that we can read the configuration register of
|
||||||
|
// only the desired channel.
|
||||||
let mut csr: [u8; 1] = [0];
|
let mut csr: [u8; 1] = [0];
|
||||||
self.interface.read(Register::CSR as u8, &mut csr)?;
|
self.interface.read(Register::CSR as u8, &mut csr)?;
|
||||||
|
|
||||||
|
@ -343,6 +378,13 @@ where
|
||||||
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current phase of a specified channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The channel to get the phase of.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The phase of the channel in turns.
|
||||||
pub fn get_phase(&mut self, channel: Channel) -> Result<f32, Error<InterfaceE>> {
|
pub fn get_phase(&mut self, channel: Channel) -> Result<f32, Error<InterfaceE>> {
|
||||||
let mut phase_offset: [u8; 2] = [0; 2];
|
let mut phase_offset: [u8; 2] = [0; 2];
|
||||||
self.read_channel(channel, Register::CPOW0, &mut phase_offset)?;
|
self.read_channel(channel, Register::CPOW0, &mut phase_offset)?;
|
||||||
|
@ -387,6 +429,13 @@ where
|
||||||
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the configured amplitude of a channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The channel to get the amplitude of.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The normalized amplitude of the channel.
|
||||||
pub fn get_amplitude(&mut self, channel: Channel) -> Result<f32, Error<InterfaceE>> {
|
pub fn get_amplitude(&mut self, channel: Channel) -> Result<f32, Error<InterfaceE>> {
|
||||||
let mut acr: [u8; 3] = [0; 3];
|
let mut acr: [u8; 3] = [0; 3];
|
||||||
self.read_channel(channel, Register::ACR, &mut acr)?;
|
self.read_channel(channel, Register::ACR, &mut acr)?;
|
||||||
|
@ -421,6 +470,13 @@ where
|
||||||
Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency())
|
Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the frequency of a channel.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * `channel` - The channel to get the frequency of.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The frequency of the channel in Hz.
|
||||||
pub fn get_frequency(&mut self, channel: Channel) -> Result<f64, Error<InterfaceE>> {
|
pub fn get_frequency(&mut self, channel: Channel) -> Result<f64, Error<InterfaceE>> {
|
||||||
// Read the frequency tuning word for the channel.
|
// Read the frequency tuning word for the channel.
|
||||||
let mut tuning_word: [u8; 4] = [0; 4];
|
let mut tuning_word: [u8; 4] = [0; 4];
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub enum Gain {
|
||||||
G10 = 0b11
|
G10 = 0b11
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A programmable gain amplifier that allows for setting the gain via GPIO.
|
||||||
pub struct ProgrammableGainAmplifier<A0, A1> {
|
pub struct ProgrammableGainAmplifier<A0, A1> {
|
||||||
a0: A0,
|
a0: A0,
|
||||||
a1: A1
|
a1: A1
|
||||||
|
@ -38,6 +39,11 @@ where
|
||||||
A1: embedded_hal::digital::v2::StatefulOutputPin,
|
A1: embedded_hal::digital::v2::StatefulOutputPin,
|
||||||
A1::Error: core::fmt::Debug,
|
A1::Error: core::fmt::Debug,
|
||||||
{
|
{
|
||||||
|
/// Construct a new programmable gain driver.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `a0` - An output connected to the A0 input of the amplifier.
|
||||||
|
/// * `a1` - An output connected to the A1 input of the amplifier.
|
||||||
pub fn new(a0: A0, a1: A1) -> Self
|
pub fn new(a0: A0, a1: A1) -> Self
|
||||||
{
|
{
|
||||||
let mut afe = Self { a0: a0, a1: a1};
|
let mut afe = Self { a0: a0, a1: a1};
|
||||||
|
@ -47,6 +53,7 @@ where
|
||||||
afe
|
afe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the gain of the front-end.
|
||||||
pub fn set_gain(&mut self, gain: Gain) {
|
pub fn set_gain(&mut self, gain: Gain) {
|
||||||
if (gain as u8 & 0b01) != 0 {
|
if (gain as u8 & 0b01) != 0 {
|
||||||
self.a0.set_high().unwrap();
|
self.a0.set_high().unwrap();
|
||||||
|
@ -61,6 +68,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the programmed gain of the analog front-end.
|
||||||
pub fn get_gain(&self) -> Result<Gain, ()> {
|
pub fn get_gain(&self) -> Result<Gain, ()> {
|
||||||
let mut code: u8 = 0;
|
let mut code: u8 = 0;
|
||||||
if self.a0.is_set_high().unwrap() {
|
if self.a0.is_set_high().unwrap() {
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
use super::{Channel, Error};
|
use super::{Channel, Error};
|
||||||
|
|
||||||
|
/// Provide an interface for managing digital attenuators on Pounder hardware.
|
||||||
|
///
|
||||||
|
/// Note: The digital attenuators do not allow read-back of attenuation. To circumvent this, this
|
||||||
|
/// driver maintains the attenuation code in both the shift register as well as the latched output
|
||||||
|
/// register of the attenuators. This allows the "active" attenuation code to be read back by
|
||||||
|
/// reading the shfit register. The downside of this approach is that any read is destructive, so a
|
||||||
|
/// read-writeback approach is employed.
|
||||||
pub trait AttenuatorInterface {
|
pub trait AttenuatorInterface {
|
||||||
|
|
||||||
|
/// Set the attenuation of a single channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The pounder channel to configure the attenuation of.
|
||||||
|
/// * `attenuation` - The desired attenuation of the channel in dB. This has a resolution of
|
||||||
|
/// 0.5dB.
|
||||||
fn set_attenuation(&mut self, channel: Channel, attenuation: f32) -> Result<f32, Error> {
|
fn set_attenuation(&mut self, channel: Channel, attenuation: f32) -> Result<f32, Error> {
|
||||||
if attenuation > 31.5 || attenuation < 0.0 {
|
if attenuation > 31.5 || attenuation < 0.0 {
|
||||||
return Err(Error::Bounds);
|
return Err(Error::Bounds);
|
||||||
|
@ -27,6 +41,13 @@ pub trait AttenuatorInterface {
|
||||||
Ok(attenuation_code as f32 / 2.0)
|
Ok(attenuation_code as f32 / 2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the attenuation of a channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The channel to get the attenuation of.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The programmed attenuation of the channel in dB.
|
||||||
fn get_attenuation(&mut self, channel: Channel) -> Result<f32, Error> {
|
fn get_attenuation(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||||
let mut channels = [0_u8; 4];
|
let mut channels = [0_u8; 4];
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,7 @@ pub struct DdsClockConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<ad9959::Channel> for Channel {
|
impl Into<ad9959::Channel> for Channel {
|
||||||
|
/// Translate pounder channels to DDS output channels.
|
||||||
fn into(self) -> ad9959::Channel {
|
fn into(self) -> ad9959::Channel {
|
||||||
match self {
|
match self {
|
||||||
Channel::In0 => ad9959::Channel::Two,
|
Channel::In0 => ad9959::Channel::Two,
|
||||||
|
@ -90,13 +91,21 @@ impl Into<ad9959::Channel> for Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A structure for the QSPI interface for the DDS.
|
||||||
pub struct QspiInterface {
|
pub struct QspiInterface {
|
||||||
pub qspi: hal::qspi::Qspi,
|
pub qspi: hal::qspi::Qspi,
|
||||||
mode: ad9959::Mode,
|
mode: ad9959::Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QspiInterface {
|
impl QspiInterface {
|
||||||
|
/// Initialize the QSPI interface.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `qspi` - The QSPI peripheral driver.
|
||||||
pub fn new(mut qspi: hal::qspi::Qspi) -> Result<Self, Error> {
|
pub fn new(mut qspi: hal::qspi::Qspi) -> Result<Self, Error> {
|
||||||
|
// This driver only supports operation in 4-bit mode due to bus inconsistencies between the
|
||||||
|
// QSPI peripheral and the DDS. Instead, we will bit-bang communications in
|
||||||
|
// single-bit-two-wire to the DDS to configure it to 4-bit operation.
|
||||||
qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?;
|
qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?;
|
||||||
Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire })
|
Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire })
|
||||||
}
|
}
|
||||||
|
@ -105,12 +114,21 @@ impl QspiInterface {
|
||||||
impl ad9959::Interface for QspiInterface {
|
impl ad9959::Interface for QspiInterface {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
/// Configure the operations mode of the interface.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `mode` - The newly desired operational mode.
|
||||||
fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> {
|
fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> {
|
||||||
self.mode = mode;
|
self.mode = mode;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write data over QSPI to the DDS.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `addr` - The address to write over QSPI to the DDS.
|
||||||
|
/// * `data` - The data to write.
|
||||||
fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> {
|
fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> {
|
||||||
if (addr & 0x80) != 0 {
|
if (addr & 0x80) != 0 {
|
||||||
return Err(Error::InvalidAddress);
|
return Err(Error::InvalidAddress);
|
||||||
|
@ -206,6 +224,7 @@ impl ad9959::Interface for QspiInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A structure containing implementation for Pounder hardware.
|
||||||
pub struct PounderDevices<DELAY> {
|
pub struct PounderDevices<DELAY> {
|
||||||
pub ad9959: ad9959::Ad9959<QspiInterface,
|
pub ad9959: ad9959::Ad9959<QspiInterface,
|
||||||
DELAY,
|
DELAY,
|
||||||
|
@ -222,6 +241,15 @@ impl<DELAY> PounderDevices<DELAY>
|
||||||
where
|
where
|
||||||
DELAY: embedded_hal::blocking::delay::DelayMs<u8>,
|
DELAY: embedded_hal::blocking::delay::DelayMs<u8>,
|
||||||
{
|
{
|
||||||
|
/// Construct and initialize pounder-specific hardware.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `ad9959` - The DDS driver for the pounder hardware.
|
||||||
|
/// * `attenuator_spi` - A SPI interface to control digital attenuators.
|
||||||
|
/// * `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.
|
||||||
|
/// * `adc2_in_p` - The input channel for the RF power measurement on IN1.
|
||||||
pub fn new(mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
|
pub fn new(mcp23017: mcp23017::MCP23017<hal::i2c::I2c<hal::stm32::I2C1>>,
|
||||||
ad9959: ad9959::Ad9959<QspiInterface,
|
ad9959: ad9959::Ad9959<QspiInterface,
|
||||||
DELAY,
|
DELAY,
|
||||||
|
@ -256,6 +284,11 @@ where
|
||||||
Ok(devices)
|
Ok(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select the an external for the DDS reference clock source.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `frequency` - The frequency of the external clock source.
|
||||||
|
/// * `multiplier` - The multiplier of the reference clock to use in the DDS.
|
||||||
fn select_external_clock(&mut self, frequency: f32, prescaler: u8) -> Result<(), Error>{
|
fn select_external_clock(&mut self, frequency: f32, prescaler: u8) -> Result<(), Error>{
|
||||||
self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?;
|
self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?;
|
||||||
self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?;
|
self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?;
|
||||||
|
@ -263,13 +296,21 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> {
|
/// Select the onboard oscillator for the DDS reference clock source.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `multiplier` - The multiplier of the reference clock to use in the DDS.
|
||||||
|
fn select_onboard_clock(&mut self, multiplier: u8) -> Result<(), Error> {
|
||||||
self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?;
|
self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?;
|
||||||
self.ad9959.configure_system_clock(100_000_000f32, prescaler).map_err(|_| Error::Dds)?;
|
self.ad9959.configure_system_clock(100_000_000f32, multiplier).map_err(|_| Error::Dds)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configure the Pounder DDS clock.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `config` - The configuration of the DDS clock desired.
|
||||||
pub fn configure_dds_clock(&mut self, config: DdsClockConfig) -> Result<(), Error> {
|
pub fn configure_dds_clock(&mut self, config: DdsClockConfig) -> Result<(), Error> {
|
||||||
if config.external_clock {
|
if config.external_clock {
|
||||||
self.select_external_clock(config.reference_clock, config.multiplier)
|
self.select_external_clock(config.reference_clock, config.multiplier)
|
||||||
|
@ -278,6 +319,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the pounder DDS clock configuration
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The current pounder DDS clock configuration.
|
||||||
pub fn get_dds_clock_config(&mut self) -> Result<DdsClockConfig, Error> {
|
pub fn get_dds_clock_config(&mut self) -> Result<DdsClockConfig, Error> {
|
||||||
let external_clock = self.mcp23017.digital_read(EXT_CLK_SEL_PIN).map_err(|_| Error::I2c)?;
|
let external_clock = self.mcp23017.digital_read(EXT_CLK_SEL_PIN).map_err(|_| Error::I2c)?;
|
||||||
let multiplier = self.ad9959.get_reference_clock_multiplier().map_err(|_| Error::Dds)?;
|
let multiplier = self.ad9959.get_reference_clock_multiplier().map_err(|_| Error::Dds)?;
|
||||||
|
@ -286,6 +331,13 @@ where
|
||||||
Ok(DdsClockConfig{multiplier, reference_clock, external_clock})
|
Ok(DdsClockConfig{multiplier, reference_clock, external_clock})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the state of a Pounder input channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The pounder channel to get the state of. Must be an input channel
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The read-back channel input state.
|
||||||
pub fn get_input_channel_state(&mut self, channel: Channel) -> Result<InputChannelState, Error> {
|
pub fn get_input_channel_state(&mut self, channel: Channel) -> Result<InputChannelState, Error> {
|
||||||
match channel {
|
match channel {
|
||||||
Channel::In0 | Channel::In1 => {
|
Channel::In0 | Channel::In1 => {
|
||||||
|
@ -304,6 +356,13 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the state of a DDS channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The pounder channel to get the state of.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The read-back channel state.
|
||||||
fn get_dds_channel_state(&mut self, channel: Channel) -> Result<DdsChannelState, Error> {
|
fn get_dds_channel_state(&mut self, channel: Channel) -> Result<DdsChannelState, Error> {
|
||||||
let frequency = self.ad9959.get_frequency(channel.into()).map_err(|_| Error::Dds)?;
|
let frequency = self.ad9959.get_frequency(channel.into()).map_err(|_| Error::Dds)?;
|
||||||
let phase_offset = self.ad9959.get_phase(channel.into()).map_err(|_| Error::Dds)?;
|
let phase_offset = self.ad9959.get_phase(channel.into()).map_err(|_| Error::Dds)?;
|
||||||
|
@ -313,6 +372,13 @@ where
|
||||||
Ok(DdsChannelState {phase_offset, frequency, amplitude, enabled})
|
Ok(DdsChannelState {phase_offset, frequency, amplitude, enabled})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the state of a DDS output channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The pounder channel to get the output state of. Must be an output channel.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The read-back output channel state.
|
||||||
pub fn get_output_channel_state(&mut self, channel: Channel) -> Result<OutputChannelState, Error> {
|
pub fn get_output_channel_state(&mut self, channel: Channel) -> Result<OutputChannelState, Error> {
|
||||||
match channel {
|
match channel {
|
||||||
Channel::Out0 | Channel::Out1 => {
|
Channel::Out0 | Channel::Out1 => {
|
||||||
|
@ -328,6 +394,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
pub fn set_channel_state(&mut self, channel: Channel, state: ChannelState) -> Result<(), Error> {
|
||||||
self.ad9959.set_frequency(channel.into(), state.parameters.frequency).map_err(|_| Error::Dds)?;
|
self.ad9959.set_frequency(channel.into(), state.parameters.frequency).map_err(|_| Error::Dds)?;
|
||||||
self.ad9959.set_phase(channel.into(), state.parameters.phase_offset).map_err(|_| Error::Dds)?;
|
self.ad9959.set_phase(channel.into(), state.parameters.phase_offset).map_err(|_| Error::Dds)?;
|
||||||
|
@ -347,6 +418,7 @@ where
|
||||||
|
|
||||||
impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
||||||
{
|
{
|
||||||
|
/// Reset all of the attenuators to a power-on default state.
|
||||||
fn reset_attenuators(&mut self) -> Result<(), Error> {
|
fn reset_attenuators(&mut self) -> Result<(), Error> {
|
||||||
self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?;
|
self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?;
|
||||||
// TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is
|
// TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is
|
||||||
|
@ -356,6 +428,10 @@ impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Latch a configuration into a digital attenuator.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The attenuator channel to latch.
|
||||||
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> {
|
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> {
|
||||||
let pin = match channel {
|
let pin = match channel {
|
||||||
Channel::In0 => ATT_LE0_PIN,
|
Channel::In0 => ATT_LE0_PIN,
|
||||||
|
@ -372,12 +448,20 @@ impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read the raw attenuation codes stored in the attenuator shift registers.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channels` - A slice to store the channel readings into.
|
||||||
fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> {
|
fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> {
|
||||||
self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?;
|
self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write the attenuator shift registers.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channels` - The data to write into the attenuators.
|
||||||
fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error> {
|
fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error> {
|
||||||
let mut result = [0_u8; 4];
|
let mut result = [0_u8; 4];
|
||||||
result.clone_from_slice(channels);
|
result.clone_from_slice(channels);
|
||||||
|
@ -388,6 +472,13 @@ impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<DELAY> PowerMeasurementInterface for PounderDevices<DELAY> {
|
impl<DELAY> PowerMeasurementInterface for PounderDevices<DELAY> {
|
||||||
|
/// Sample an ADC channel.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The channel to sample.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// The sampled voltage of the specified channel.
|
||||||
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error> {
|
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||||
let adc_scale = match channel {
|
let adc_scale = match channel {
|
||||||
Channel::In0 => {
|
Channel::In0 => {
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
use super::{Error, Channel};
|
use super::{Error, Channel};
|
||||||
|
|
||||||
|
/// Provide an interface to measure RF input power in dB.
|
||||||
pub trait PowerMeasurementInterface {
|
pub trait PowerMeasurementInterface {
|
||||||
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error>;
|
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error>;
|
||||||
|
|
||||||
|
/// Measure the power of an inpu channel in dB.
|
||||||
|
///
|
||||||
|
/// Note: This function assumes the input channel is connected to an AD8363 output.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `channel` - The pounder channel to measure the power of.
|
||||||
fn measure_power(&mut self, channel: Channel) -> Result<f32, Error> {
|
fn measure_power(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||||
let analog_measurement = self.sample_converter(channel)?;
|
let analog_measurement = self.sample_converter(channel)?;
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ impl<'a> Request<'a> {
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
|
|
||||||
|
/// Remove all double quotation marks from the `value` field of a response.
|
||||||
fn sanitize_value(&mut self) {
|
fn sanitize_value(&mut self) {
|
||||||
let mut new_value: String<U256> = String::new();
|
let mut new_value: String<U256> = String::new();
|
||||||
for byte in self.value.as_str().chars() {
|
for byte in self.value.as_str().chars() {
|
||||||
|
@ -76,6 +77,8 @@ impl Response {
|
||||||
self.value = new_value;
|
self.value = new_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove all double quotation marks from the `value` field of a response and wrap it in single
|
||||||
|
/// quotes.
|
||||||
fn wrap_and_sanitize_value(&mut self) {
|
fn wrap_and_sanitize_value(&mut self) {
|
||||||
let mut new_value: String<U256> = String::new();
|
let mut new_value: String<U256> = String::new();
|
||||||
new_value.push('\'').unwrap();
|
new_value.push('\'').unwrap();
|
||||||
|
@ -91,6 +94,13 @@ impl Response {
|
||||||
self.value = new_value;
|
self.value = new_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a successful reply.
|
||||||
|
///
|
||||||
|
/// Note: `value` will be sanitized to convert all single quotes to double quotes.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `attrbute` - The attribute of the success.
|
||||||
|
/// * `value` - The value of the attribute.
|
||||||
pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self
|
pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self
|
||||||
{
|
{
|
||||||
let mut res = Self { code: 200, attribute: String::from(attribute), value: String::from(value)};
|
let mut res = Self { code: 200, attribute: String::from(attribute), value: String::from(value)};
|
||||||
|
@ -98,6 +108,13 @@ impl Response {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct an error reply.
|
||||||
|
///
|
||||||
|
/// Note: `message` will be sanitized to convert all single quotes to double quotes.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `attrbute` - The attribute of the success.
|
||||||
|
/// * `message` - The message denoting the error.
|
||||||
pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self
|
pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self
|
||||||
{
|
{
|
||||||
let mut res = Self { code: 400, attribute: String::from(attribute), value: String::from(message)};
|
let mut res = Self { code: 400, attribute: String::from(attribute), value: String::from(message)};
|
||||||
|
@ -105,6 +122,13 @@ impl Response {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct a custom reply.
|
||||||
|
///
|
||||||
|
/// Note: `message` will be sanitized to convert all single quotes to double quotes.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `attrbute` - The attribute of the success.
|
||||||
|
/// * `message` - The message denoting the status.
|
||||||
pub fn custom<'a>(code: i32, message : &'a str) -> Self
|
pub fn custom<'a>(code: i32, message : &'a str) -> Self
|
||||||
{
|
{
|
||||||
let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)};
|
let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)};
|
||||||
|
@ -134,6 +158,7 @@ pub struct Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
|
/// Construct a new server object for managing requests.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
|
@ -141,6 +166,11 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Poll the server for potential data updates.
|
||||||
|
///
|
||||||
|
/// Args:
|
||||||
|
/// * `socket` - The socket to check contents from.
|
||||||
|
/// * `f` - A closure that can be called if a request has been received on the server.
|
||||||
pub fn poll<F>(
|
pub fn poll<F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
socket: &mut net::socket::TcpSocket,
|
socket: &mut net::socket::TcpSocket,
|
||||||
|
@ -173,6 +203,10 @@ impl Server {
|
||||||
let r = from_slice::<Request>(&self.data[..self.data.len() - 1]);
|
let r = from_slice::<Request>(&self.data[..self.data.len() - 1]);
|
||||||
match r {
|
match r {
|
||||||
Ok(mut res) => {
|
Ok(mut res) => {
|
||||||
|
// Note that serde_json_core doesn't escape quotations within a string.
|
||||||
|
// To account for this, we manually translate all single quotes to
|
||||||
|
// double quotes. This occurs because we doubly-serialize this field in
|
||||||
|
// some cases.
|
||||||
res.restore_value();
|
res.restore_value();
|
||||||
let response = f(&res);
|
let response = f(&res);
|
||||||
json_reply(socket, &response);
|
json_reply(socket, &response);
|
||||||
|
|
Loading…
Reference in New Issue