2020-06-10 18:40:44 +08:00
|
|
|
use super::{Channel, Error};
|
2020-06-09 00:20:10 +08:00
|
|
|
|
2020-06-11 17:51:52 +08:00
|
|
|
/// 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.
|
2020-06-09 00:20:10 +08:00
|
|
|
pub trait AttenuatorInterface {
|
2020-06-11 17:51:52 +08:00
|
|
|
/// 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.
|
2020-06-16 22:22:12 +08:00
|
|
|
fn set_attenuation(
|
|
|
|
&mut self,
|
|
|
|
channel: Channel,
|
|
|
|
attenuation: f32,
|
|
|
|
) -> Result<f32, Error> {
|
2020-06-09 00:20:10 +08:00
|
|
|
if attenuation > 31.5 || attenuation < 0.0 {
|
|
|
|
return Err(Error::Bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the attenuation code to program into the attenuator. The attenuator uses a
|
|
|
|
// code where the LSB is 0.5 dB.
|
|
|
|
let attenuation_code = (attenuation * 2.0) as u8;
|
|
|
|
|
|
|
|
// Read all the channels, modify the channel of interest, and write all the channels back.
|
|
|
|
// This ensures the staging register and the output register are always in sync.
|
|
|
|
let mut channels = [0_u8; 4];
|
2020-06-10 18:40:44 +08:00
|
|
|
self.read_all_attenuators(&mut channels)?;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the
|
|
|
|
// attenuator code into the upper 6 bits of the register value. Note that the attenuator
|
|
|
|
// treats inputs as active-low, so the code is inverted before writing.
|
2020-06-10 18:40:44 +08:00
|
|
|
channels[channel as usize] = (!attenuation_code) << 2;
|
|
|
|
self.write_all_attenuators(&channels)?;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// Finally, latch the output of the updated channel to force it into an active state.
|
2020-06-10 18:40:44 +08:00
|
|
|
self.latch_attenuators(channel)?;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
Ok(attenuation_code as f32 / 2.0)
|
|
|
|
}
|
|
|
|
|
2020-06-11 17:51:52 +08:00
|
|
|
/// Get the attenuation of a channel.
|
|
|
|
///
|
|
|
|
/// Args:
|
|
|
|
/// * `channel` - The channel to get the attenuation of.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// The programmed attenuation of the channel in dB.
|
2020-06-10 18:40:44 +08:00
|
|
|
fn get_attenuation(&mut self, channel: Channel) -> Result<f32, Error> {
|
2020-06-09 00:20:10 +08:00
|
|
|
let mut channels = [0_u8; 4];
|
|
|
|
|
|
|
|
// Reading the data always shifts data out of the staging registers, so we perform a
|
|
|
|
// duplicate write-back to ensure the staging register is always equal to the output
|
|
|
|
// register.
|
2020-06-10 18:40:44 +08:00
|
|
|
self.read_all_attenuators(&mut channels)?;
|
|
|
|
self.write_all_attenuators(&channels)?;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// The attenuation code is stored in the upper 6 bits of the register, where each LSB
|
|
|
|
// represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result
|
|
|
|
// (before the shift) has the affect of transforming the bits of interest (and the
|
|
|
|
// dont-care bits) into an active-high state and then masking off the don't care bits. If
|
|
|
|
// the shift occurs before the inversion, the upper 2 bits (which would then be don't
|
|
|
|
// care) would contain erroneous data.
|
2020-06-10 18:40:44 +08:00
|
|
|
let attenuation_code = (!channels[channel as usize]) >> 2;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
|
|
|
// Convert the desired channel code into dB of attenuation.
|
|
|
|
Ok(attenuation_code as f32 / 2.0)
|
|
|
|
}
|
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
fn reset_attenuators(&mut self) -> Result<(), Error>;
|
2020-06-09 00:20:10 +08:00
|
|
|
|
2020-06-10 18:40:44 +08:00
|
|
|
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error>;
|
2020-06-16 22:22:12 +08:00
|
|
|
fn read_all_attenuators(
|
|
|
|
&mut self,
|
|
|
|
channels: &mut [u8; 4],
|
|
|
|
) -> Result<(), Error>;
|
|
|
|
fn write_all_attenuators(
|
|
|
|
&mut self,
|
|
|
|
channels: &[u8; 4],
|
|
|
|
) -> Result<(), Error>;
|
2020-06-09 00:20:10 +08:00
|
|
|
}
|