Updating code comments

master
Ryan Summers 2020-06-11 11:51:52 +02:00
parent 818047775b
commit c4979f08f6
6 changed files with 219 additions and 2 deletions

View File

@ -24,6 +24,7 @@ pub struct Ad9959<INTERFACE, DELAY, UPDATE> {
io_update: UPDATE,
}
/// A trait that allows a HAL to provide a means of communicating with the AD9959.
pub trait Interface {
type Error;
@ -34,6 +35,8 @@ pub trait Interface {
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)]
pub enum Mode {
SingleBitTwoWire = 0b00,
@ -103,6 +106,17 @@ where
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,
reset_pin: &mut RST,
io_update: UPDATE,
@ -158,6 +172,7 @@ where
Ok(ad9959)
}
/// Latch the DDS configuration to ensure it is active on the output channels.
fn latch_configuration(&mut self) -> Result<(), Error<InterfaceE>> {
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
@ -205,10 +220,12 @@ where
Ok(self.system_clock_frequency())
}
/// Get the current reference clock frequency in Hz.
pub fn get_reference_clock_frequency(&self) -> f32 {
self.reference_clock_frequency
}
/// Get the current reference clock multiplier.
pub fn get_reference_clock_multiplier(&mut self) -> Result<u8, Error<InterfaceE>> {
let mut fr1: [u8; 3] = [0, 0, 0];
self.interface.read(Register::FR1 as u8, &mut fr1)?;
@ -257,6 +274,7 @@ where
Ok(true)
}
/// Get the current system clock frequency in Hz.
fn system_clock_frequency(&self) -> f64 {
self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64
}
@ -281,6 +299,7 @@ where
Ok(())
}
/// Determine if an output channel is enabled.
pub fn is_enabled(&mut self, channel: Channel) -> Result<bool, Error<InterfaceE>> {
let mut csr: [u8; 1] = [0; 1];
self.interface.read(Register::CSR as u8, &mut csr)?;
@ -288,7 +307,15 @@ where
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>> {
// Disable all other outputs so that we can update the configuration register of only the
// specified channel.
let mut csr: [u8; 1] = [0];
self.interface.read(Register::CSR as u8, &mut csr)?;
@ -308,7 +335,15 @@ where
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>> {
// 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];
self.interface.read(Register::CSR as u8, &mut csr)?;
@ -343,6 +378,13 @@ where
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>> {
let mut phase_offset: [u8; 2] = [0; 2];
self.read_channel(channel, Register::CPOW0, &mut phase_offset)?;
@ -387,6 +429,13 @@ where
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>> {
let mut acr: [u8; 3] = [0; 3];
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())
}
/// 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>> {
// Read the frequency tuning word for the channel.
let mut tuning_word: [u8; 4] = [0; 4];

View File

@ -12,6 +12,7 @@ pub enum Gain {
G10 = 0b11
}
/// A programmable gain amplifier that allows for setting the gain via GPIO.
pub struct ProgrammableGainAmplifier<A0, A1> {
a0: A0,
a1: A1
@ -38,6 +39,11 @@ where
A1: embedded_hal::digital::v2::StatefulOutputPin,
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
{
let mut afe = Self { a0: a0, a1: a1};
@ -47,6 +53,7 @@ where
afe
}
/// Set the gain of the front-end.
pub fn set_gain(&mut self, gain: Gain) {
if (gain as u8 & 0b01) != 0 {
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, ()> {
let mut code: u8 = 0;
if self.a0.is_set_high().unwrap() {

View File

@ -1,6 +1,20 @@
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 {
/// 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> {
if attenuation > 31.5 || attenuation < 0.0 {
return Err(Error::Bounds);
@ -27,6 +41,13 @@ pub trait AttenuatorInterface {
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> {
let mut channels = [0_u8; 4];

View File

@ -80,6 +80,7 @@ pub struct DdsClockConfig {
}
impl Into<ad9959::Channel> for Channel {
/// Translate pounder channels to DDS output channels.
fn into(self) -> ad9959::Channel {
match self {
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 qspi: hal::qspi::Qspi,
mode: ad9959::Mode,
}
impl QspiInterface {
/// Initialize the QSPI interface.
///
/// Args:
/// * `qspi` - The QSPI peripheral driver.
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)?;
Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire })
}
@ -105,12 +114,21 @@ impl QspiInterface {
impl ad9959::Interface for QspiInterface {
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> {
self.mode = mode;
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> {
if (addr & 0x80) != 0 {
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 ad9959: ad9959::Ad9959<QspiInterface,
DELAY,
@ -222,6 +241,15 @@ impl<DELAY> PounderDevices<DELAY>
where
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>>,
ad9959: ad9959::Ad9959<QspiInterface,
DELAY,
@ -256,6 +284,11 @@ where
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>{
self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?;
self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?;
@ -263,13 +296,21 @@ where
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.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(())
}
/// 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> {
if config.external_clock {
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> {
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)?;
@ -286,6 +331,13 @@ where
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> {
match channel {
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> {
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)?;
@ -313,6 +372,13 @@ where
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> {
match channel {
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> {
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)?;
@ -347,6 +418,7 @@ where
impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
{
/// Reset all of the attenuators to a power-on default state.
fn reset_attenuators(&mut self) -> Result<(), Error> {
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
@ -356,6 +428,10 @@ impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
Ok(())
}
/// Latch a configuration into a digital attenuator.
///
/// Args:
/// * `channel` - The attenuator channel to latch.
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> {
let pin = match channel {
Channel::In0 => ATT_LE0_PIN,
@ -372,12 +448,20 @@ impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
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> {
self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?;
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> {
let mut result = [0_u8; 4];
result.clone_from_slice(channels);
@ -388,6 +472,13 @@ impl<DELAY> AttenuatorInterface 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> {
let adc_scale = match channel {
Channel::In0 => {

View File

@ -1,8 +1,15 @@
use super::{Error, Channel};
/// Provide an interface to measure RF input power in dB.
pub trait PowerMeasurementInterface {
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> {
let analog_measurement = self.sample_converter(channel)?;

View File

@ -63,6 +63,7 @@ impl<'a> Request<'a> {
impl Response {
/// Remove all double quotation marks from the `value` field of a response.
fn sanitize_value(&mut self) {
let mut new_value: String<U256> = String::new();
for byte in self.value.as_str().chars() {
@ -76,6 +77,8 @@ impl Response {
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) {
let mut new_value: String<U256> = String::new();
new_value.push('\'').unwrap();
@ -91,6 +94,13 @@ impl Response {
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
{
let mut res = Self { code: 200, attribute: String::from(attribute), value: String::from(value)};
@ -98,6 +108,13 @@ impl Response {
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
{
let mut res = Self { code: 400, attribute: String::from(attribute), value: String::from(message)};
@ -105,6 +122,13 @@ impl Response {
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
{
let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)};
@ -134,6 +158,7 @@ pub struct Server {
}
impl Server {
/// Construct a new server object for managing requests.
pub fn new() -> Self {
Self {
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>(
&mut self,
socket: &mut net::socket::TcpSocket,
@ -173,6 +203,10 @@ impl Server {
let r = from_slice::<Request>(&self.data[..self.data.len() - 1]);
match r {
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();
let response = f(&res);
json_reply(socket, &response);