Adding work after functional DDS interface
This commit is contained in:
parent
8b4d5397be
commit
519fa86058
@ -9,7 +9,7 @@ members = [
|
|||||||
[profile.dev]
|
[profile.dev]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
incremental = false
|
incremental = false
|
||||||
opt-level = 3
|
opt-level = 1
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
@ -19,7 +19,7 @@ use embedded_hal::{
|
|||||||
pub struct Ad9959<INTERFACE, DELAY, UPDATE> {
|
pub struct Ad9959<INTERFACE, DELAY, UPDATE> {
|
||||||
interface: INTERFACE,
|
interface: INTERFACE,
|
||||||
delay: DELAY,
|
delay: DELAY,
|
||||||
reference_clock_frequency: u32,
|
reference_clock_frequency: f32,
|
||||||
system_clock_multiplier: u8,
|
system_clock_multiplier: u8,
|
||||||
io_update: UPDATE,
|
io_update: UPDATE,
|
||||||
}
|
}
|
||||||
@ -84,6 +84,7 @@ pub enum Channel {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error<InterfaceE> {
|
pub enum Error<InterfaceE> {
|
||||||
Interface(InterfaceE),
|
Interface(InterfaceE),
|
||||||
|
Check,
|
||||||
Bounds,
|
Bounds,
|
||||||
Pin,
|
Pin,
|
||||||
Frequency,
|
Frequency,
|
||||||
@ -107,7 +108,7 @@ where
|
|||||||
io_update: UPDATE,
|
io_update: UPDATE,
|
||||||
delay: DELAY,
|
delay: DELAY,
|
||||||
desired_mode: Mode,
|
desired_mode: Mode,
|
||||||
clock_frequency: u32,
|
clock_frequency: f32,
|
||||||
multiplier: u8) -> Result<Self, Error<InterfaceE>>
|
multiplier: u8) -> Result<Self, Error<InterfaceE>>
|
||||||
where
|
where
|
||||||
RST: OutputPin,
|
RST: OutputPin,
|
||||||
@ -135,7 +136,7 @@ where
|
|||||||
// Program the interface configuration in the AD9959. Default to all channels enabled.
|
// Program the interface configuration in the AD9959. Default to all channels enabled.
|
||||||
let mut csr: [u8; 1] = [0xF0];
|
let mut csr: [u8; 1] = [0xF0];
|
||||||
csr[0].set_bits(1..3, desired_mode as u8);
|
csr[0].set_bits(1..3, desired_mode as u8);
|
||||||
ad9959.interface.write(0, &csr)?;
|
ad9959.interface.write(Register::CSR as u8, &csr)?;
|
||||||
|
|
||||||
// Configure the interface to the desired mode.
|
// Configure the interface to the desired mode.
|
||||||
ad9959.interface.configure_mode(Mode::FourBitSerial)?;
|
ad9959.interface.configure_mode(Mode::FourBitSerial)?;
|
||||||
@ -145,6 +146,13 @@ where
|
|||||||
|
|
||||||
ad9959.interface.configure_mode(desired_mode)?;
|
ad9959.interface.configure_mode(desired_mode)?;
|
||||||
|
|
||||||
|
// Read back the CSR to ensure it specifies the mode correctly.
|
||||||
|
let mut updated_csr: [u8; 1] = [0];
|
||||||
|
ad9959.interface.read(Register::CSR as u8, &mut updated_csr)?;
|
||||||
|
if updated_csr[0] != csr[0] {
|
||||||
|
return Err(Error::Check);
|
||||||
|
}
|
||||||
|
|
||||||
// Set the clock frequency to configure the device as necessary.
|
// Set the clock frequency to configure the device as necessary.
|
||||||
ad9959.configure_system_clock(clock_frequency, multiplier)?;
|
ad9959.configure_system_clock(clock_frequency, multiplier)?;
|
||||||
Ok(ad9959)
|
Ok(ad9959)
|
||||||
@ -164,21 +172,21 @@ where
|
|||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core.
|
/// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core.
|
||||||
/// * `prescaler` - The frequency prescaler of the system clock. Must be 1 or 4-20.
|
/// * `multiplier` - The frequency multiplier of the system clock. Must be 1 or 4-20.
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// The actual frequency configured for the internal system clock.
|
/// The actual frequency configured for the internal system clock.
|
||||||
pub fn configure_system_clock(&mut self,
|
pub fn configure_system_clock(&mut self,
|
||||||
reference_clock_frequency: u32,
|
reference_clock_frequency: f32,
|
||||||
prescaler: u8) -> Result<f64, Error<InterfaceE>>
|
multiplier: u8) -> Result<f64, Error<InterfaceE>>
|
||||||
{
|
{
|
||||||
self.reference_clock_frequency = reference_clock_frequency;
|
self.reference_clock_frequency = reference_clock_frequency;
|
||||||
|
|
||||||
if prescaler != 1 && (prescaler > 20 || prescaler < 4) {
|
if multiplier != 1 && (multiplier > 20 || multiplier < 4) {
|
||||||
return Err(Error::Bounds);
|
return Err(Error::Bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
let frequency = prescaler as f64 * self.reference_clock_frequency as f64;
|
let frequency = multiplier as f64 * self.reference_clock_frequency as f64;
|
||||||
if frequency > 500_000_000.0f64 {
|
if frequency > 500_000_000.0f64 {
|
||||||
return Err(Error::Frequency);
|
return Err(Error::Frequency);
|
||||||
}
|
}
|
||||||
@ -186,17 +194,28 @@ where
|
|||||||
// TODO: Update / disable any enabled channels?
|
// TODO: Update / disable any enabled channels?
|
||||||
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)?;
|
||||||
fr1[0].set_bits(2..=6, prescaler);
|
fr1[0].set_bits(2..=6, multiplier);
|
||||||
|
|
||||||
let vco_range = frequency > 255e6;
|
let vco_range = frequency > 255e6;
|
||||||
fr1[0].set_bit(7, vco_range);
|
fr1[0].set_bit(7, vco_range);
|
||||||
|
|
||||||
self.interface.write(Register::FR1 as u8, &fr1)?;
|
self.interface.write(Register::FR1 as u8, &fr1)?;
|
||||||
self.system_clock_multiplier = prescaler;
|
self.system_clock_multiplier = multiplier;
|
||||||
|
|
||||||
Ok(self.system_clock_frequency())
|
Ok(self.system_clock_frequency())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_reference_clock_frequency(&self) -> f32 {
|
||||||
|
self.reference_clock_frequency
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
Ok(fr1[0].get_bits(2..=6) as u8)
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform a self-test of the communication interface.
|
/// Perform a self-test of the communication interface.
|
||||||
///
|
///
|
||||||
/// Note:
|
/// Note:
|
||||||
@ -262,6 +281,13 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
Ok(csr[0].get_bit(channel as usize + 4))
|
||||||
|
}
|
||||||
|
|
||||||
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>> {
|
||||||
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)?;
|
||||||
@ -282,22 +308,48 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error<InterfaceE>> {
|
||||||
|
let mut csr: [u8; 1] = [0];
|
||||||
|
self.interface.read(Register::CSR as u8, &mut csr)?;
|
||||||
|
|
||||||
|
let mut new_csr = csr;
|
||||||
|
new_csr[0].set_bits(4..8, 0);
|
||||||
|
new_csr[0].set_bit(4 + channel as usize, true);
|
||||||
|
|
||||||
|
self.interface.write(Register::CSR as u8, &new_csr)?;
|
||||||
|
|
||||||
|
self.interface.read(register as u8, &mut data)?;
|
||||||
|
|
||||||
|
// Restore the previous CSR. Note that the re-enable of the channel happens immediately, so
|
||||||
|
// the CSR update does not need to be latched.
|
||||||
|
self.interface.write(Register::CSR as u8, &csr)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Configure the phase of a specified channel.
|
/// Configure the phase of a specified channel.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// * `channel` - The channel to configure the frequency of.
|
/// * `channel` - The channel to configure the frequency of.
|
||||||
/// * `phase_turns` - The desired phase offset in normalized turns.
|
/// * `phase_turns` - The desired phase offset in turns.
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// The actual programmed phase offset of the channel in degrees.
|
/// The actual programmed phase offset of the channel in turns.
|
||||||
pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result<f32, Error<InterfaceE>> {
|
pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result<f32, Error<InterfaceE>> {
|
||||||
if phase_turns > 1.0 || phase_turns < 0.0 {
|
let phase_offset: u16 = (phase_turns * (1 << 14) as f32) as u16 & 0x3FFFu16;
|
||||||
return Err(Error::Bounds);
|
|
||||||
|
self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?;
|
||||||
|
|
||||||
|
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
||||||
}
|
}
|
||||||
|
|
||||||
let phase_offset: u16 = (phase_turns * 1u32.wrapping_shl(14) as f32) as u16;
|
pub fn get_phase(&mut self, channel: Channel) -> Result<f32, Error<InterfaceE>> {
|
||||||
self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?;
|
let mut phase_offset: [u8; 2] = [0; 2];
|
||||||
Ok((phase_offset as f32 / 1u32.wrapping_shl(14) as f32) * 360.0)
|
self.read_channel(channel, Register::CPOW0, &mut phase_offset)?;
|
||||||
|
|
||||||
|
let phase_offset = u16::from_be_bytes(phase_offset) & 0x3FFFu16;
|
||||||
|
|
||||||
|
Ok((phase_offset as f32) / ((1 << 14) as f32))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the amplitude of a specified channel.
|
/// Configure the amplitude of a specified channel.
|
||||||
@ -313,17 +365,38 @@ where
|
|||||||
return Err(Error::Bounds);
|
return Err(Error::Bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
let amplitude_control: u16 = (amplitude / 1u16.wrapping_shl(10) as f32) as u16;
|
let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16;
|
||||||
let mut acr: [u8; 3] = [0, amplitude_control.to_be_bytes()[0], amplitude_control.to_be_bytes()[1]];
|
|
||||||
|
let mut acr: [u8; 3] = [0; 3];
|
||||||
|
|
||||||
// Enable the amplitude multiplier for the channel if required. The amplitude control has
|
// Enable the amplitude multiplier for the channel if required. The amplitude control has
|
||||||
// full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever
|
// full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever
|
||||||
// full-scale is used.
|
// full-scale is used.
|
||||||
acr[1].set_bit(4, amplitude_control >= 1u16.wrapping_shl(10));
|
if amplitude_control < (1 << 10) {
|
||||||
|
|
||||||
|
let masked_control = amplitude_control & 0x3FF;
|
||||||
|
acr[1] = masked_control.to_be_bytes()[0];
|
||||||
|
acr[2] = masked_control.to_be_bytes()[1];
|
||||||
|
|
||||||
|
// Enable the amplitude multiplier
|
||||||
|
acr[1].set_bit(4, true);
|
||||||
|
}
|
||||||
|
|
||||||
self.modify_channel(channel, Register::ACR, &acr)?;
|
self.modify_channel(channel, Register::ACR, &acr)?;
|
||||||
|
|
||||||
Ok(amplitude_control as f32 / 1_u16.wrapping_shl(10) as f32)
|
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
if acr[1].get_bit(4) {
|
||||||
|
let amplitude_control: u16 = (((acr[1] as u16) << 8) | (acr[2] as u16)) & 0x3FF;
|
||||||
|
Ok(amplitude_control as f32 / (1 << 10) as f32)
|
||||||
|
} else {
|
||||||
|
Ok(1.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the frequency of a specified channel.
|
/// Configure the frequency of a specified channel.
|
||||||
@ -347,4 +420,14 @@ where
|
|||||||
self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?;
|
self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?;
|
||||||
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
self.read_channel(channel, Register::CFTW0, &mut tuning_word)?;
|
||||||
|
let tuning_word = u32::from_be_bytes(tuning_word);
|
||||||
|
|
||||||
|
// Convert the tuning word into a frequency.
|
||||||
|
Ok(tuning_word as f64 * self.system_clock_frequency() / ((1u64 << 32)) as f64)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,8 +113,8 @@ type AFE2 = afe::ProgrammableGainAmplifier<
|
|||||||
|
|
||||||
macro_rules! route_request {
|
macro_rules! route_request {
|
||||||
($request:ident,
|
($request:ident,
|
||||||
readable_attributes: [$(($read_attribute:tt, $getter:tt)),*],
|
readable_attributes: [$($read_attribute:tt: $getter:tt),*],
|
||||||
modifiable_attributes: [$(($write_attribute:tt, $TYPE:ty, $setter:tt)),*]) => {
|
modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => {
|
||||||
match $request.req {
|
match $request.req {
|
||||||
server::AccessRequest::Read => {
|
server::AccessRequest::Read => {
|
||||||
match $request.attribute {
|
match $request.attribute {
|
||||||
@ -126,17 +126,13 @@ macro_rules! route_request {
|
|||||||
"Failed to set attribute"),
|
"Failed to set attribute"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let encoded_data: String<U128> = match serde_json_core::to_string(&value) {
|
let encoded_data: String<U256> = match serde_json_core::to_string(&value) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return server::Response::error($request.attribute,
|
Err(_) => return server::Response::error($request.attribute,
|
||||||
"Failed to encode attribute value"),
|
"Failed to encode attribute value"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Encoding data into a string surrounds it with qutotations. Because this
|
server::Response::success($request.attribute, &encoded_data)
|
||||||
// value is then serialzed into another string, we remove the double
|
|
||||||
// quotations because they cannot be properly escaped.
|
|
||||||
server::Response::success($request.attribute,
|
|
||||||
&encoded_data[1..encoded_data.len()-1])
|
|
||||||
},
|
},
|
||||||
)*
|
)*
|
||||||
_ => server::Response::error($request.attribute, "Unknown attribute")
|
_ => server::Response::error($request.attribute, "Unknown attribute")
|
||||||
@ -146,22 +142,14 @@ macro_rules! route_request {
|
|||||||
match $request.attribute {
|
match $request.attribute {
|
||||||
$(
|
$(
|
||||||
$write_attribute => {
|
$write_attribute => {
|
||||||
// To avoid sending double quotations in the request, they are eliminated on
|
let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) {
|
||||||
// the sender side. However, to properly deserialize the data, quotes need
|
|
||||||
// to be added back.
|
|
||||||
let mut value: String<U128> = String::new();
|
|
||||||
value.push('"').unwrap();
|
|
||||||
value.push_str($request.value).unwrap();
|
|
||||||
value.push('"').unwrap();
|
|
||||||
|
|
||||||
let new_value = match serde_json_core::from_str::<$TYPE>(value.as_str()) {
|
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(_) => return server::Response::error($request.attribute,
|
Err(_) => return server::Response::error($request.attribute,
|
||||||
"Failed to decode value"),
|
"Failed to decode value"),
|
||||||
};
|
};
|
||||||
|
|
||||||
match $setter(new_value) {
|
match $setter(new_value) {
|
||||||
Ok(_) => server::Response::success($request.attribute, $request.value),
|
Ok(_) => server::Response::success($request.attribute, &$request.value),
|
||||||
Err(_) => server::Response::error($request.attribute,
|
Err(_) => server::Response::error($request.attribute,
|
||||||
"Failed to set attribute"),
|
"Failed to set attribute"),
|
||||||
}
|
}
|
||||||
@ -344,6 +332,17 @@ const APP: () = {
|
|||||||
spi
|
spi
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut fp_led_0 = gpiod.pd5.into_push_pull_output();
|
||||||
|
let mut fp_led_1 = gpiod.pd6.into_push_pull_output();
|
||||||
|
let mut fp_led_2 = gpiog.pg4.into_push_pull_output();
|
||||||
|
let mut fp_led_3 = gpiod.pd12.into_push_pull_output();
|
||||||
|
|
||||||
|
fp_led_0.set_low().unwrap();
|
||||||
|
fp_led_1.set_low().unwrap();
|
||||||
|
fp_led_2.set_low().unwrap();
|
||||||
|
fp_led_3.set_low().unwrap();
|
||||||
|
|
||||||
|
|
||||||
let pounder_devices = {
|
let pounder_devices = {
|
||||||
let ad9959 = {
|
let ad9959 = {
|
||||||
let qspi_interface = {
|
let qspi_interface = {
|
||||||
@ -375,7 +374,7 @@ const APP: () = {
|
|||||||
io_update,
|
io_update,
|
||||||
asm_delay,
|
asm_delay,
|
||||||
ad9959::Mode::FourBitSerial,
|
ad9959::Mode::FourBitSerial,
|
||||||
100_000_000,
|
100_000_000f32,
|
||||||
5).unwrap()
|
5).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -426,16 +425,6 @@ const APP: () = {
|
|||||||
adc2_in_p).unwrap()
|
adc2_in_p).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut fp_led_0 = gpiod.pd5.into_push_pull_output();
|
|
||||||
let mut fp_led_1 = gpiod.pd6.into_push_pull_output();
|
|
||||||
let mut fp_led_2 = gpiog.pg4.into_push_pull_output();
|
|
||||||
let mut fp_led_3 = gpiod.pd12.into_push_pull_output();
|
|
||||||
|
|
||||||
fp_led_0.set_low().unwrap();
|
|
||||||
fp_led_1.set_low().unwrap();
|
|
||||||
fp_led_2.set_low().unwrap();
|
|
||||||
fp_led_3.set_low().unwrap();
|
|
||||||
|
|
||||||
let mut eeprom_i2c = {
|
let mut eeprom_i2c = {
|
||||||
let sda = gpiof.pf0.into_alternate_af4().set_open_drain();
|
let sda = gpiof.pf0.into_alternate_af4().set_open_drain();
|
||||||
let scl = gpiof.pf1.into_alternate_af4().set_open_drain();
|
let scl = gpiof.pf1.into_alternate_af4().set_open_drain();
|
||||||
@ -517,7 +506,7 @@ const APP: () = {
|
|||||||
hal::dma::DMAREQ_ID::TIM2_CH2);
|
hal::dma::DMAREQ_ID::TIM2_CH2);
|
||||||
|
|
||||||
// Configure timer 2 to trigger conversions for the ADC
|
// Configure timer 2 to trigger conversions for the ADC
|
||||||
let mut timer2 = dp.TIM2.timer(500.khz(), &mut clocks);
|
let mut timer2 = dp.TIM2.timer(50.khz(), &mut clocks);
|
||||||
timer2.configure_channel(hal::timer::Channel::One, 0.25);
|
timer2.configure_channel(hal::timer::Channel::One, 0.25);
|
||||||
timer2.configure_channel(hal::timer::Channel::Two, 0.75);
|
timer2.configure_channel(hal::timer::Channel::Two, 0.75);
|
||||||
|
|
||||||
@ -572,7 +561,7 @@ const APP: () = {
|
|||||||
c.resources.dac1.send(output).unwrap();
|
c.resources.dac1.send(output).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[idle(resources=[net_interface, mac_addr, eth_mac, iir_state, iir_ch, afe1, afe2])]
|
#[idle(resources=[net_interface, pounder, mac_addr, eth_mac, iir_state, iir_ch, afe1, afe2])]
|
||||||
fn idle(mut c: idle::Context) -> ! {
|
fn idle(mut c: idle::Context) -> ! {
|
||||||
|
|
||||||
let mut socket_set_entries: [_; 8] = Default::default();
|
let mut socket_set_entries: [_; 8] = Default::default();
|
||||||
@ -617,7 +606,7 @@ const APP: () = {
|
|||||||
info!("Got request: {:?}", req);
|
info!("Got request: {:?}", req);
|
||||||
route_request!(req,
|
route_request!(req,
|
||||||
readable_attributes: [
|
readable_attributes: [
|
||||||
("stabilizer/iir/state", (|| {
|
"stabilizer/iir/state": (|| {
|
||||||
let state = c.resources.iir_state.lock(|iir_state|
|
let state = c.resources.iir_state.lock(|iir_state|
|
||||||
server::Status {
|
server::Status {
|
||||||
t: time,
|
t: time,
|
||||||
@ -628,13 +617,32 @@ const APP: () = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Ok::<server::Status, ()>(state)
|
Ok::<server::Status, ()>(state)
|
||||||
})),
|
}),
|
||||||
("stabilizer/afe1/gain", (|| c.resources.afe1.get_gain())),
|
"stabilizer/afe1/gain": (|| c.resources.afe1.get_gain()),
|
||||||
("stabilizer/afe2/gain", (|| c.resources.afe2.get_gain()))
|
"stabilizer/afe2/gain": (|| c.resources.afe2.get_gain()),
|
||||||
|
"pounder/in0": (|| {
|
||||||
|
c.resources.pounder.get_input_channel_state(
|
||||||
|
pounder::Channel::In0)
|
||||||
|
}),
|
||||||
|
"pounder/in1": (|| {
|
||||||
|
c.resources.pounder.get_input_channel_state(
|
||||||
|
pounder::Channel::In1)
|
||||||
|
}),
|
||||||
|
"pounder/out0": (|| {
|
||||||
|
c.resources.pounder.get_output_channel_state(
|
||||||
|
pounder::Channel::Out0)
|
||||||
|
}),
|
||||||
|
"pounder/out1": (|| {
|
||||||
|
c.resources.pounder.get_output_channel_state(
|
||||||
|
pounder::Channel::Out1)
|
||||||
|
}),
|
||||||
|
"pounder/dds/clock": (|| {
|
||||||
|
c.resources.pounder.get_dds_clock_config()
|
||||||
|
})
|
||||||
],
|
],
|
||||||
|
|
||||||
modifiable_attributes: [
|
modifiable_attributes: [
|
||||||
("stabilizer/iir1/state", server::IirRequest, (|req: server::IirRequest| {
|
"stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| {
|
||||||
c.resources.iir_ch.lock(|iir_ch| {
|
c.resources.iir_ch.lock(|iir_ch| {
|
||||||
if req.channel > 1 {
|
if req.channel > 1 {
|
||||||
return Err(());
|
return Err(());
|
||||||
@ -644,8 +652,8 @@ const APP: () = {
|
|||||||
|
|
||||||
Ok::<server::IirRequest, ()>(req)
|
Ok::<server::IirRequest, ()>(req)
|
||||||
})
|
})
|
||||||
})),
|
}),
|
||||||
("stabilizer/iir2/state", server::IirRequest, (|req: server::IirRequest| {
|
"stabilizer/iir2/state": server::IirRequest, (|req: server::IirRequest| {
|
||||||
c.resources.iir_ch.lock(|iir_ch| {
|
c.resources.iir_ch.lock(|iir_ch| {
|
||||||
if req.channel > 1 {
|
if req.channel > 1 {
|
||||||
return Err(());
|
return Err(());
|
||||||
@ -655,13 +663,32 @@ const APP: () = {
|
|||||||
|
|
||||||
Ok::<server::IirRequest, ()>(req)
|
Ok::<server::IirRequest, ()>(req)
|
||||||
})
|
})
|
||||||
})),
|
}),
|
||||||
("stabilizer/afe1/gain", afe::Gain, (|gain| {
|
"pounder/in0": pounder::ChannelState, (|state| {
|
||||||
|
c.resources.pounder.set_channel_state(pounder::Channel::In0,
|
||||||
|
state)
|
||||||
|
}),
|
||||||
|
"pounder/in1": pounder::ChannelState, (|state| {
|
||||||
|
c.resources.pounder.set_channel_state(pounder::Channel::In1,
|
||||||
|
state)
|
||||||
|
}),
|
||||||
|
"pounder/out0": pounder::ChannelState, (|state| {
|
||||||
|
c.resources.pounder.set_channel_state(pounder::Channel::Out0,
|
||||||
|
state)
|
||||||
|
}),
|
||||||
|
"pounder/out1": pounder::ChannelState, (|state| {
|
||||||
|
c.resources.pounder.set_channel_state(pounder::Channel::Out1,
|
||||||
|
state)
|
||||||
|
}),
|
||||||
|
"pounder/dds/clock": pounder::DdsClockConfig, (|config| {
|
||||||
|
c.resources.pounder.configure_dds_clock(config)
|
||||||
|
}),
|
||||||
|
"stabilizer/afe1/gain": afe::Gain, (|gain| {
|
||||||
Ok::<(), ()>(c.resources.afe1.set_gain(gain))
|
Ok::<(), ()>(c.resources.afe1.set_gain(gain))
|
||||||
})),
|
}),
|
||||||
("stabilizer/afe2/gain", afe::Gain, (|gain| {
|
"stabilizer/afe2/gain": afe::Gain, (|gain| {
|
||||||
Ok::<(), ()>(c.resources.afe2.set_gain(gain))
|
Ok::<(), ()>(c.resources.afe2.set_gain(gain))
|
||||||
}))
|
})
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use super::error::Error;
|
use super::{Channel, Error};
|
||||||
use super::DdsChannel;
|
|
||||||
|
|
||||||
pub trait AttenuatorInterface {
|
pub trait AttenuatorInterface {
|
||||||
fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> 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);
|
||||||
}
|
}
|
||||||
@ -14,28 +13,28 @@ pub trait AttenuatorInterface {
|
|||||||
// Read all the channels, modify the channel of interest, and write all the channels back.
|
// 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.
|
// This ensures the staging register and the output register are always in sync.
|
||||||
let mut channels = [0_u8; 4];
|
let mut channels = [0_u8; 4];
|
||||||
self.read_all(&mut channels)?;
|
self.read_all_attenuators(&mut channels)?;
|
||||||
|
|
||||||
// The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the
|
// 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
|
// 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.
|
// treats inputs as active-low, so the code is inverted before writing.
|
||||||
channels[channel as usize] = !attenuation_code.wrapping_shl(2);
|
channels[channel as usize] = (!attenuation_code) << 2;
|
||||||
self.write_all(&channels)?;
|
self.write_all_attenuators(&channels)?;
|
||||||
|
|
||||||
// Finally, latch the output of the updated channel to force it into an active state.
|
// Finally, latch the output of the updated channel to force it into an active state.
|
||||||
self.latch(channel)?;
|
self.latch_attenuators(channel)?;
|
||||||
|
|
||||||
Ok(attenuation_code as f32 / 2.0)
|
Ok(attenuation_code as f32 / 2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(&mut self, channel: DdsChannel) -> 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];
|
||||||
|
|
||||||
// Reading the data always shifts data out of the staging registers, so we perform a
|
// 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
|
// duplicate write-back to ensure the staging register is always equal to the output
|
||||||
// register.
|
// register.
|
||||||
self.read_all(&mut channels)?;
|
self.read_all_attenuators(&mut channels)?;
|
||||||
self.write_all(&channels)?;
|
self.write_all_attenuators(&channels)?;
|
||||||
|
|
||||||
// The attenuation code is stored in the upper 6 bits of the register, where each LSB
|
// 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
|
// represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result
|
||||||
@ -43,15 +42,15 @@ pub trait AttenuatorInterface {
|
|||||||
// dont-care bits) into an active-high state and then masking off the don't care bits. If
|
// 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
|
// the shift occurs before the inversion, the upper 2 bits (which would then be don't
|
||||||
// care) would contain erroneous data.
|
// care) would contain erroneous data.
|
||||||
let attenuation_code = (!channels[channel as usize]).wrapping_shr(2);
|
let attenuation_code = (!channels[channel as usize]) >> 2;
|
||||||
|
|
||||||
// Convert the desired channel code into dB of attenuation.
|
// Convert the desired channel code into dB of attenuation.
|
||||||
Ok(attenuation_code as f32 / 2.0)
|
Ok(attenuation_code as f32 / 2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Result<(), Error>;
|
fn reset_attenuators(&mut self) -> Result<(), Error>;
|
||||||
|
|
||||||
fn latch(&mut self, channel: DdsChannel) -> Result<(), Error>;
|
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error>;
|
||||||
fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>;
|
fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>;
|
||||||
fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error>;
|
fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Spi,
|
|
||||||
I2c,
|
|
||||||
DDS,
|
|
||||||
Qspi,
|
|
||||||
Bounds,
|
|
||||||
InvalidAddress,
|
|
||||||
InvalidChannel,
|
|
||||||
Adc,
|
|
||||||
}
|
|
@ -1,16 +1,13 @@
|
|||||||
use mcp23017;
|
use mcp23017;
|
||||||
use ad9959;
|
use ad9959;
|
||||||
|
|
||||||
pub mod error;
|
use serde::{Serialize, Deserialize};
|
||||||
pub mod attenuators;
|
mod attenuators;
|
||||||
mod rf_power;
|
mod rf_power;
|
||||||
pub mod types;
|
|
||||||
|
|
||||||
use super::hal;
|
use super::hal;
|
||||||
|
|
||||||
use error::Error;
|
|
||||||
use attenuators::AttenuatorInterface;
|
use attenuators::AttenuatorInterface;
|
||||||
use types::{DdsChannel, InputChannel};
|
|
||||||
use rf_power::PowerMeasurementInterface;
|
use rf_power::PowerMeasurementInterface;
|
||||||
|
|
||||||
use embedded_hal::{
|
use embedded_hal::{
|
||||||
@ -18,17 +15,79 @@ use embedded_hal::{
|
|||||||
adc::OneShot
|
adc::OneShot
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EXT_CLK_SEL_PIN: u8 = 8 + 7;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const OSC_EN_N_PIN: u8 = 8 + 7;
|
const OSC_EN_N_PIN: u8 = 8 + 6;
|
||||||
|
|
||||||
const EXT_CLK_SEL_PIN: u8 = 8 + 6;
|
|
||||||
|
|
||||||
const ATT_RST_N_PIN: u8 = 8 + 5;
|
const ATT_RST_N_PIN: u8 = 8 + 5;
|
||||||
|
|
||||||
const ATT_LE0_PIN: u8 = 8 + 0;
|
|
||||||
const ATT_LE1_PIN: u8 = 8 + 1;
|
|
||||||
const ATT_LE2_PIN: u8 = 8 + 2;
|
|
||||||
const ATT_LE3_PIN: u8 = 8 + 3;
|
const ATT_LE3_PIN: u8 = 8 + 3;
|
||||||
|
const ATT_LE2_PIN: u8 = 8 + 2;
|
||||||
|
const ATT_LE1_PIN: u8 = 8 + 1;
|
||||||
|
const ATT_LE0_PIN: u8 = 8 + 0;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Error {
|
||||||
|
Spi,
|
||||||
|
I2c,
|
||||||
|
Dds,
|
||||||
|
Qspi,
|
||||||
|
Bounds,
|
||||||
|
InvalidAddress,
|
||||||
|
InvalidChannel,
|
||||||
|
Adc,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Channel {
|
||||||
|
In0,
|
||||||
|
In1,
|
||||||
|
Out0,
|
||||||
|
Out1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||||
|
pub struct DdsChannelState {
|
||||||
|
pub phase_offset: f32,
|
||||||
|
pub frequency: f64,
|
||||||
|
pub amplitude: f32,
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||||
|
pub struct ChannelState {
|
||||||
|
pub parameters: DdsChannelState,
|
||||||
|
pub attenuation: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||||
|
pub struct InputChannelState {
|
||||||
|
pub attenuation: f32,
|
||||||
|
pub power: f32,
|
||||||
|
pub mixer: DdsChannelState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||||
|
pub struct OutputChannelState {
|
||||||
|
pub attenuation: f32,
|
||||||
|
pub channel: DdsChannelState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||||
|
pub struct DdsClockConfig {
|
||||||
|
pub multiplier: u8,
|
||||||
|
pub reference_clock: f32,
|
||||||
|
pub external_clock: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ad9959::Channel> for Channel {
|
||||||
|
fn into(self) -> ad9959::Channel {
|
||||||
|
match self {
|
||||||
|
Channel::In0 => ad9959::Channel::Two,
|
||||||
|
Channel::In1 => ad9959::Channel::Four,
|
||||||
|
Channel::Out0 => ad9959::Channel::One,
|
||||||
|
Channel::Out1 => ad9959::Channel::Three,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct QspiInterface {
|
pub struct QspiInterface {
|
||||||
pub qspi: hal::qspi::Qspi,
|
pub qspi: hal::qspi::Qspi,
|
||||||
@ -78,32 +137,40 @@ impl ad9959::Interface for QspiInterface {
|
|||||||
// Encode the address into the first 4 bytes.
|
// Encode the address into the first 4 bytes.
|
||||||
for address_bit in 0..8 {
|
for address_bit in 0..8 {
|
||||||
let offset: u8 = {
|
let offset: u8 = {
|
||||||
if address_bit % 2 == 0 {
|
if address_bit % 2 != 0 {
|
||||||
4
|
4
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if addr & address_bit != 0 {
|
// Encode MSB first. Least significant bits are placed at the most significant
|
||||||
encoded_data[(address_bit >> 1) as usize] |= 1 << offset;
|
// byte.
|
||||||
|
let byte_position = 3 - (address_bit >> 1) as usize;
|
||||||
|
|
||||||
|
if addr & (1 << address_bit) != 0 {
|
||||||
|
encoded_data[byte_position] |= 1 << offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the data into the remaining bytes.
|
// Encode the data into the remaining bytes.
|
||||||
for byte_index in 0..data.len() {
|
for byte_index in 0..data.len() {
|
||||||
let byte = data[byte_index];
|
let byte = data[byte_index];
|
||||||
for address_bit in 0..8 {
|
for bit in 0..8 {
|
||||||
let offset: u8 = {
|
let offset: u8 = {
|
||||||
if address_bit % 2 == 0 {
|
if bit % 2 != 0 {
|
||||||
4
|
4
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if byte & address_bit != 0 {
|
// Encode MSB first. Least significant bits are placed at the most
|
||||||
encoded_data[(byte_index + 1) * 4 + (address_bit >> 1) as usize] |= 1 << offset;
|
// significant byte.
|
||||||
|
let byte_position = 3 - (bit >> 1) as usize;
|
||||||
|
|
||||||
|
if byte & (1 << bit) != 0 {
|
||||||
|
encoded_data[(byte_index + 1) * 4 + byte_position] |= 1 << offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,9 +196,8 @@ impl ad9959::Interface for QspiInterface {
|
|||||||
return Err(Error::InvalidAddress);
|
return Err(Error::InvalidAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is not possible to read data from the AD9959 in single bit two wire mode because the
|
// This implementation only supports operation (read) in four-bit-serial mode.
|
||||||
// QSPI interface assumes that data is always received on IO1.
|
if self.mode != ad9959::Mode::FourBitSerial {
|
||||||
if self.mode == ad9959::Mode::SingleBitTwoWire {
|
|
||||||
return Err(Error::Qspi);
|
return Err(Error::Qspi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,27 +244,100 @@ where
|
|||||||
|
|
||||||
// Configure power-on-default state for pounder. All LEDs are on, on-board oscillator
|
// Configure power-on-default state for pounder. All LEDs are on, on-board oscillator
|
||||||
// selected, attenuators out of reset.
|
// selected, attenuators out of reset.
|
||||||
devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0xF).map_err(|_| Error::I2c)?;
|
devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0x3F).map_err(|_| Error::I2c)?;
|
||||||
devices.mcp23017.write_gpio(mcp23017::Port::GPIOB,
|
devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, 1 << 5).map_err(|_| Error::I2c)?;
|
||||||
1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?;
|
|
||||||
devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?;
|
devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?;
|
||||||
|
|
||||||
// Select the on-board clock with a 5x prescaler (500MHz).
|
// Select the on-board clock with a 5x prescaler (500MHz).
|
||||||
devices.select_onboard_clock(5u8)?;
|
devices.select_onboard_clock(4u8)?;
|
||||||
|
|
||||||
Ok(devices)
|
Ok(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_external_clock(&mut self, frequency: u32, 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)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> {
|
fn select_onboard_clock(&mut self, prescaler: 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_000, prescaler).map_err(|_| Error::DDS)?;
|
self.ad9959.configure_system_clock(100_000_000f32, prescaler).map_err(|_| Error::Dds)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_dds_clock(&mut self, config: DdsClockConfig) -> Result<(), Error> {
|
||||||
|
if config.external_clock {
|
||||||
|
self.select_external_clock(config.reference_clock, config.multiplier)
|
||||||
|
} else {
|
||||||
|
self.select_onboard_clock(config.multiplier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
let reference_clock = self.ad9959.get_reference_clock_frequency();
|
||||||
|
|
||||||
|
Ok(DdsClockConfig{multiplier, reference_clock, external_clock})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_input_channel_state(&mut self, channel: Channel) -> Result<InputChannelState, Error> {
|
||||||
|
match channel {
|
||||||
|
Channel::In0 | Channel::In1 => {
|
||||||
|
let channel_state = self.get_dds_channel_state(channel)?;
|
||||||
|
|
||||||
|
let attenuation = self.get_attenuation(channel)?;
|
||||||
|
let power = self.measure_power(channel)?;
|
||||||
|
|
||||||
|
Ok(InputChannelState {
|
||||||
|
attenuation: attenuation,
|
||||||
|
power: power,
|
||||||
|
mixer: channel_state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidChannel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
let amplitude = self.ad9959.get_amplitude(channel.into()).map_err(|_| Error::Dds)?;
|
||||||
|
let enabled = self.ad9959.is_enabled(channel.into()).map_err(|_| Error::Dds)?;
|
||||||
|
|
||||||
|
Ok(DdsChannelState {phase_offset, frequency, amplitude, enabled})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_output_channel_state(&mut self, channel: Channel) -> Result<OutputChannelState, Error> {
|
||||||
|
match channel {
|
||||||
|
Channel::Out0 | Channel::Out1 => {
|
||||||
|
let channel_state = self.get_dds_channel_state(channel)?;
|
||||||
|
let attenuation = self.get_attenuation(channel)?;
|
||||||
|
|
||||||
|
Ok(OutputChannelState {
|
||||||
|
attenuation: attenuation,
|
||||||
|
channel: channel_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidChannel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
self.ad9959.set_amplitude(channel.into(), state.parameters.amplitude).map_err(|_| Error::Dds)?;
|
||||||
|
|
||||||
|
if state.parameters.enabled {
|
||||||
|
self.ad9959.enable_channel(channel.into()).map_err(|_| Error::Dds)?;
|
||||||
|
} else {
|
||||||
|
self.ad9959.disable_channel(channel.into()).map_err(|_| Error::Dds)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_attenuation(channel, state.attenuation)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -206,21 +345,21 @@ where
|
|||||||
|
|
||||||
impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
||||||
{
|
{
|
||||||
fn reset(&mut self) -> Result<(), Error> {
|
fn reset_attenuators(&mut self) -> Result<(), Error> {
|
||||||
self.mcp23017.digital_write(ATT_RST_N_PIN, true).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
|
||||||
// sufficient. Document the delay here.
|
// sufficient. Document the delay here.
|
||||||
self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?;
|
self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn latch(&mut self, channel: DdsChannel) -> Result<(), Error> {
|
fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> {
|
||||||
let pin = match channel {
|
let pin = match channel {
|
||||||
DdsChannel::Zero => ATT_LE1_PIN,
|
Channel::In0 => ATT_LE0_PIN,
|
||||||
DdsChannel::One => ATT_LE0_PIN,
|
Channel::In1 => ATT_LE2_PIN,
|
||||||
DdsChannel::Two => ATT_LE3_PIN,
|
Channel::Out0 => ATT_LE1_PIN,
|
||||||
DdsChannel::Three => ATT_LE2_PIN,
|
Channel::Out1 => ATT_LE3_PIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?;
|
self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?;
|
||||||
@ -231,13 +370,13 @@ impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_all(&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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_all(&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);
|
||||||
self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?;
|
self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?;
|
||||||
@ -247,16 +386,17 @@ impl<DELAY> AttenuatorInterface for PounderDevices<DELAY>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<DELAY> PowerMeasurementInterface for PounderDevices<DELAY> {
|
impl<DELAY> PowerMeasurementInterface for PounderDevices<DELAY> {
|
||||||
fn sample_converter(&mut self, channel: InputChannel) -> Result<f32, Error> {
|
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error> {
|
||||||
let adc_scale = match channel {
|
let adc_scale = match channel {
|
||||||
InputChannel::Zero => {
|
Channel::In0 => {
|
||||||
let adc_reading: u32 = self.adc1.read(&mut self.adc1_in_p).map_err(|_| Error::Adc)?;
|
let adc_reading: u32 = self.adc1.read(&mut self.adc1_in_p).map_err(|_| Error::Adc)?;
|
||||||
adc_reading as f32 / self.adc1.max_sample() as f32
|
adc_reading as f32 / self.adc1.max_sample() as f32
|
||||||
},
|
},
|
||||||
InputChannel::One => {
|
Channel::In1 => {
|
||||||
let adc_reading: u32 = self.adc2.read(&mut self.adc2_in_p).map_err(|_| Error::Adc)?;
|
let adc_reading: u32 = self.adc2.read(&mut self.adc2_in_p).map_err(|_| Error::Adc)?;
|
||||||
adc_reading as f32 / self.adc2.max_sample() as f32
|
adc_reading as f32 / self.adc2.max_sample() as f32
|
||||||
},
|
},
|
||||||
|
_ => return Err(Error::InvalidChannel),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert analog percentage to voltage.
|
// Convert analog percentage to voltage.
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use super::Error;
|
use super::{Error, Channel};
|
||||||
use super::InputChannel;
|
|
||||||
|
|
||||||
pub trait PowerMeasurementInterface {
|
pub trait PowerMeasurementInterface {
|
||||||
fn sample_converter(&mut self, channel: InputChannel) -> Result<f32, Error>;
|
fn sample_converter(&mut self, channel: Channel) -> Result<f32, Error>;
|
||||||
|
|
||||||
fn measure_power(&mut self, channel: InputChannel) -> 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)?;
|
||||||
|
|
||||||
// The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at
|
// The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum DdsChannel {
|
|
||||||
Zero,
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
Three,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum InputChannel {
|
|
||||||
Zero,
|
|
||||||
One,
|
|
||||||
}
|
|
@ -27,10 +27,10 @@ pub enum AccessRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct Request<'a, 'b> {
|
pub struct Request<'a> {
|
||||||
pub req: AccessRequest,
|
pub req: AccessRequest,
|
||||||
pub attribute: &'a str,
|
pub attribute: &'a str,
|
||||||
pub value: &'b str,
|
pub value: String<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -42,24 +42,74 @@ pub struct IirRequest {
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
code: i32,
|
code: i32,
|
||||||
attribute: String<U128>,
|
attribute: String<U256>,
|
||||||
value: String<U128>,
|
value: String<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> {
|
||||||
|
pub fn restore_value(&mut self) {
|
||||||
|
let mut new_value: String<U256> = String::new();
|
||||||
|
for byte in self.value.as_str().chars() {
|
||||||
|
if byte == '\'' {
|
||||||
|
new_value.push('"').unwrap();
|
||||||
|
} else {
|
||||||
|
new_value.push(byte).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.value = new_value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
|
|
||||||
|
fn sanitize_value(&mut self) {
|
||||||
|
let mut new_value: String<U256> = String::new();
|
||||||
|
for byte in self.value.as_str().chars() {
|
||||||
|
if byte == '"' {
|
||||||
|
new_value.push('\'').unwrap();
|
||||||
|
} else {
|
||||||
|
new_value.push(byte).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_and_sanitize_value(&mut self) {
|
||||||
|
let mut new_value: String<U256> = String::new();
|
||||||
|
new_value.push('\'').unwrap();
|
||||||
|
for byte in self.value.as_str().chars() {
|
||||||
|
if byte == '"' {
|
||||||
|
new_value.push('\'').unwrap();
|
||||||
|
} else {
|
||||||
|
new_value.push(byte).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_value.push('\'').unwrap();
|
||||||
|
|
||||||
|
self.value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self
|
pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self
|
||||||
{
|
{
|
||||||
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)};
|
||||||
|
res.sanitize_value();
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self
|
pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self
|
||||||
{
|
{
|
||||||
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)};
|
||||||
|
res.wrap_and_sanitize_value();
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn custom<'a>(code: i32, message : &'a str) -> Self
|
pub fn custom<'a>(code: i32, message : &'a str) -> Self
|
||||||
{
|
{
|
||||||
Self { code: code, attribute: String::from(""), value: String::from(message)}
|
let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)};
|
||||||
|
res.wrap_and_sanitize_value();
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +123,7 @@ pub struct Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn json_reply<T: Serialize>(socket: &mut net::socket::TcpSocket, msg: &T) {
|
pub fn json_reply<T: Serialize>(socket: &mut net::socket::TcpSocket, msg: &T) {
|
||||||
let mut u: String<U128> = to_string(msg).unwrap();
|
let mut u: String<U512> = to_string(msg).unwrap();
|
||||||
u.push('\n').unwrap();
|
u.push('\n').unwrap();
|
||||||
socket.write_str(&u).unwrap();
|
socket.write_str(&u).unwrap();
|
||||||
}
|
}
|
||||||
@ -122,7 +172,8 @@ impl Server {
|
|||||||
} else {
|
} else {
|
||||||
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(res) => {
|
Ok(mut res) => {
|
||||||
|
res.restore_value();
|
||||||
let response = f(&res);
|
let response = f(&res);
|
||||||
json_reply(socket, &response);
|
json_reply(socket, &response);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user