From 519fa860587639159e481069214c1e5ba12d2577 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 10 Jun 2020 12:40:44 +0200 Subject: [PATCH] Adding work after functional DDS interface --- Cargo.toml | 2 +- ad9959/src/lib.rs | 131 ++++++++++++--- stabilizer/src/main.rs | 113 ++++++++----- stabilizer/src/pounder/attenuators.rs | 29 ++-- stabilizer/src/pounder/error.rs | 11 -- stabilizer/src/pounder/mod.rs | 228 +++++++++++++++++++++----- stabilizer/src/pounder/rf_power.rs | 7 +- stabilizer/src/pounder/types.rs | 16 -- stabilizer/src/server.rs | 69 +++++++- 9 files changed, 439 insertions(+), 167 deletions(-) delete mode 100644 stabilizer/src/pounder/error.rs delete mode 100644 stabilizer/src/pounder/types.rs diff --git a/Cargo.toml b/Cargo.toml index 05df738..ec83f63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ [profile.dev] codegen-units = 1 incremental = false -opt-level = 3 +opt-level = 1 [profile.release] debug = true diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 5346bdc..75277e6 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -19,7 +19,7 @@ use embedded_hal::{ pub struct Ad9959 { interface: INTERFACE, delay: DELAY, - reference_clock_frequency: u32, + reference_clock_frequency: f32, system_clock_multiplier: u8, io_update: UPDATE, } @@ -84,6 +84,7 @@ pub enum Channel { #[derive(Debug)] pub enum Error { Interface(InterfaceE), + Check, Bounds, Pin, Frequency, @@ -107,7 +108,7 @@ where io_update: UPDATE, delay: DELAY, desired_mode: Mode, - clock_frequency: u32, + clock_frequency: f32, multiplier: u8) -> Result> where RST: OutputPin, @@ -120,7 +121,7 @@ where system_clock_multiplier: 1, }; - ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; + ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; // Reset the AD9959 reset_pin.set_high().or_else(|_| Err(Error::Pin))?; @@ -130,12 +131,12 @@ where reset_pin.set_low().or_else(|_| Err(Error::Pin))?; - ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; + ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; // Program the interface configuration in the AD9959. Default to all channels enabled. let mut csr: [u8; 1] = [0xF0]; - csr[0].set_bits(1..3, desired_mode as u8); - ad9959.interface.write(0, &csr)?; + csr[0].set_bits(1..3, desired_mode as u8); + ad9959.interface.write(Register::CSR as u8, &csr)?; // Configure the interface to the desired mode. ad9959.interface.configure_mode(Mode::FourBitSerial)?; @@ -145,6 +146,13 @@ where 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. ad9959.configure_system_clock(clock_frequency, multiplier)?; Ok(ad9959) @@ -164,21 +172,21 @@ where /// /// Arguments: /// * `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: /// The actual frequency configured for the internal system clock. pub fn configure_system_clock(&mut self, - reference_clock_frequency: u32, - prescaler: u8) -> Result> + reference_clock_frequency: f32, + multiplier: u8) -> Result> { 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); } - 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 { return Err(Error::Frequency); } @@ -186,17 +194,28 @@ where // TODO: Update / disable any enabled channels? let mut fr1: [u8; 3] = [0, 0, 0]; 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; fr1[0].set_bit(7, vco_range); self.interface.write(Register::FR1 as u8, &fr1)?; - self.system_clock_multiplier = prescaler; + self.system_clock_multiplier = multiplier; 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> { + 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. /// /// Note: @@ -262,6 +281,13 @@ where Ok(()) } + pub fn is_enabled(&mut self, channel: Channel) -> Result> { + 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> { let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -282,22 +308,48 @@ where Ok(()) } + fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error> { + 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. /// /// Arguments: /// * `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: - /// 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> { - if phase_turns > 1.0 || phase_turns < 0.0 { - return Err(Error::Bounds); - } + let phase_offset: u16 = (phase_turns * (1 << 14) as f32) as u16 & 0x3FFFu16; - let phase_offset: u16 = (phase_turns * 1u32.wrapping_shl(14) as f32) as u16; self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?; - Ok((phase_offset as f32 / 1u32.wrapping_shl(14) as f32) * 360.0) + + Ok((phase_offset as f32) / ((1 << 14) as f32)) + } + + pub fn get_phase(&mut self, channel: Channel) -> Result> { + let mut phase_offset: [u8; 2] = [0; 2]; + 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. @@ -313,17 +365,38 @@ where return Err(Error::Bounds); } - let amplitude_control: u16 = (amplitude / 1u16.wrapping_shl(10) as f32) as u16; - let mut acr: [u8; 3] = [0, amplitude_control.to_be_bytes()[0], amplitude_control.to_be_bytes()[1]]; + let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16; + + let mut acr: [u8; 3] = [0; 3]; // 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 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)?; - 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> { + 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. @@ -347,4 +420,14 @@ where 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()) } + + pub fn get_frequency(&mut self, channel: Channel) -> Result> { + // 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) + } } diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 8e0b07c..a514c05 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -113,8 +113,8 @@ type AFE2 = afe::ProgrammableGainAmplifier< macro_rules! route_request { ($request:ident, - readable_attributes: [$(($read_attribute:tt, $getter:tt)),*], - modifiable_attributes: [$(($write_attribute:tt, $TYPE:ty, $setter:tt)),*]) => { + readable_attributes: [$($read_attribute:tt: $getter:tt),*], + modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => { match $request.req { server::AccessRequest::Read => { match $request.attribute { @@ -126,17 +126,13 @@ macro_rules! route_request { "Failed to set attribute"), }; - let encoded_data: String = match serde_json_core::to_string(&value) { + let encoded_data: String = match serde_json_core::to_string(&value) { Ok(data) => data, Err(_) => return server::Response::error($request.attribute, "Failed to encode attribute value"), }; - // Encoding data into a string surrounds it with qutotations. Because this - // 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::success($request.attribute, &encoded_data) }, )* _ => server::Response::error($request.attribute, "Unknown attribute") @@ -146,22 +142,14 @@ macro_rules! route_request { match $request.attribute { $( $write_attribute => { - // To avoid sending double quotations in the request, they are eliminated on - // the sender side. However, to properly deserialize the data, quotes need - // to be added back. - let mut value: String = 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()) { + let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) { Ok(data) => data, Err(_) => return server::Response::error($request.attribute, "Failed to decode 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, "Failed to set attribute"), } @@ -344,6 +332,17 @@ const APP: () = { 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 ad9959 = { let qspi_interface = { @@ -375,7 +374,7 @@ const APP: () = { io_update, asm_delay, ad9959::Mode::FourBitSerial, - 100_000_000, + 100_000_000f32, 5).unwrap() }; @@ -426,16 +425,6 @@ const APP: () = { 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 sda = gpiof.pf0.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); // 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::Two, 0.75); @@ -572,7 +561,7 @@ const APP: () = { 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) -> ! { let mut socket_set_entries: [_; 8] = Default::default(); @@ -617,7 +606,7 @@ const APP: () = { info!("Got request: {:?}", req); route_request!(req, readable_attributes: [ - ("stabilizer/iir/state", (|| { + "stabilizer/iir/state": (|| { let state = c.resources.iir_state.lock(|iir_state| server::Status { t: time, @@ -628,13 +617,32 @@ const APP: () = { }); Ok::(state) - })), - ("stabilizer/afe1/gain", (|| c.resources.afe1.get_gain())), - ("stabilizer/afe2/gain", (|| c.resources.afe2.get_gain())) + }), + "stabilizer/afe1/gain": (|| c.resources.afe1.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: [ - ("stabilizer/iir1/state", server::IirRequest, (|req: server::IirRequest| { + "stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| { c.resources.iir_ch.lock(|iir_ch| { if req.channel > 1 { return Err(()); @@ -644,8 +652,8 @@ const APP: () = { Ok::(req) }) - })), - ("stabilizer/iir2/state", server::IirRequest, (|req: server::IirRequest| { + }), + "stabilizer/iir2/state": server::IirRequest, (|req: server::IirRequest| { c.resources.iir_ch.lock(|iir_ch| { if req.channel > 1 { return Err(()); @@ -655,13 +663,32 @@ const APP: () = { Ok::(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)) - })), - ("stabilizer/afe2/gain", afe::Gain, (|gain| { + }), + "stabilizer/afe2/gain": afe::Gain, (|gain| { Ok::<(), ()>(c.resources.afe2.set_gain(gain)) - })) + }) ] ) }); diff --git a/stabilizer/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs index d9785f8..5e1dc8f 100644 --- a/stabilizer/src/pounder/attenuators.rs +++ b/stabilizer/src/pounder/attenuators.rs @@ -1,8 +1,7 @@ -use super::error::Error; -use super::DdsChannel; +use super::{Channel, Error}; pub trait AttenuatorInterface { - fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> Result { + fn set_attenuation(&mut self, channel: Channel, attenuation: f32) -> Result { if attenuation > 31.5 || attenuation < 0.0 { 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. // This ensures the staging register and the output register are always in sync. 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 // 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. - channels[channel as usize] = !attenuation_code.wrapping_shl(2); - self.write_all(&channels)?; + channels[channel as usize] = (!attenuation_code) << 2; + self.write_all_attenuators(&channels)?; // 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) } - fn read(&mut self, channel: DdsChannel) -> Result { + fn get_attenuation(&mut self, channel: Channel) -> Result { 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. - self.read_all(&mut channels)?; - self.write_all(&channels)?; + self.read_all_attenuators(&mut channels)?; + self.write_all_attenuators(&channels)?; // 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 @@ -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 // the shift occurs before the inversion, the upper 2 bits (which would then be don't // 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. 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 read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; - fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error>; + fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error>; + fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; + fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error>; } diff --git a/stabilizer/src/pounder/error.rs b/stabilizer/src/pounder/error.rs deleted file mode 100644 index b3cc596..0000000 --- a/stabilizer/src/pounder/error.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[derive(Debug)] -pub enum Error { - Spi, - I2c, - DDS, - Qspi, - Bounds, - InvalidAddress, - InvalidChannel, - Adc, -} diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs index d29e3bd..cef9f81 100644 --- a/stabilizer/src/pounder/mod.rs +++ b/stabilizer/src/pounder/mod.rs @@ -1,16 +1,13 @@ use mcp23017; use ad9959; -pub mod error; -pub mod attenuators; +use serde::{Serialize, Deserialize}; +mod attenuators; mod rf_power; -pub mod types; use super::hal; -use error::Error; use attenuators::AttenuatorInterface; -use types::{DdsChannel, InputChannel}; use rf_power::PowerMeasurementInterface; use embedded_hal::{ @@ -18,17 +15,79 @@ use embedded_hal::{ adc::OneShot }; +const EXT_CLK_SEL_PIN: u8 = 8 + 7; #[allow(dead_code)] -const OSC_EN_N_PIN: u8 = 8 + 7; - -const EXT_CLK_SEL_PIN: u8 = 8 + 6; - +const OSC_EN_N_PIN: u8 = 8 + 6; 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_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 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 qspi: hal::qspi::Qspi, @@ -78,32 +137,40 @@ impl ad9959::Interface for QspiInterface { // Encode the address into the first 4 bytes. for address_bit in 0..8 { let offset: u8 = { - if address_bit % 2 == 0 { + if address_bit % 2 != 0 { 4 } else { 0 } }; - if addr & address_bit != 0 { - encoded_data[(address_bit >> 1) as usize] |= 1 << offset; + // Encode MSB first. Least significant bits are placed at the most significant + // 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. for byte_index in 0..data.len() { let byte = data[byte_index]; - for address_bit in 0..8 { + for bit in 0..8 { let offset: u8 = { - if address_bit % 2 == 0 { + if bit % 2 != 0 { 4 } else { 0 } }; - if byte & address_bit != 0 { - encoded_data[(byte_index + 1) * 4 + (address_bit >> 1) as usize] |= 1 << offset; + // Encode MSB first. Least significant bits are placed at the most + // 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); } - // It is not possible to read data from the AD9959 in single bit two wire mode because the - // QSPI interface assumes that data is always received on IO1. - if self.mode == ad9959::Mode::SingleBitTwoWire { + // This implementation only supports operation (read) in four-bit-serial mode. + if self.mode != ad9959::Mode::FourBitSerial { return Err(Error::Qspi); } @@ -178,27 +244,100 @@ where // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator // selected, attenuators out of reset. - devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0xF).map_err(|_| Error::I2c)?; - devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, - 1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0x3F).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, 1 << 5).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). - devices.select_onboard_clock(5u8)?; + devices.select_onboard_clock(4u8)?; 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.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::DDS)?; + self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?; 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.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 { + 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 { + 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 { + 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 { + 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(()) } @@ -206,21 +345,21 @@ where impl AttenuatorInterface for PounderDevices { - fn reset(&mut self) -> Result<(), Error> { - self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?; + 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 // 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(()) } - fn latch(&mut self, channel: DdsChannel) -> Result<(), Error> { + fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { - DdsChannel::Zero => ATT_LE1_PIN, - DdsChannel::One => ATT_LE0_PIN, - DdsChannel::Two => ATT_LE3_PIN, - DdsChannel::Three => ATT_LE2_PIN, + Channel::In0 => ATT_LE0_PIN, + Channel::In1 => ATT_LE2_PIN, + Channel::Out0 => ATT_LE1_PIN, + Channel::Out1 => ATT_LE3_PIN, }; self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?; @@ -231,13 +370,13 @@ impl AttenuatorInterface for PounderDevices 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)?; 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]; result.clone_from_slice(channels); self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?; @@ -247,16 +386,17 @@ impl AttenuatorInterface for PounderDevices } impl PowerMeasurementInterface for PounderDevices { - fn sample_converter(&mut self, channel: InputChannel) -> Result { + fn sample_converter(&mut self, channel: Channel) -> Result { 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)?; 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)?; adc_reading as f32 / self.adc2.max_sample() as f32 }, + _ => return Err(Error::InvalidChannel), }; // Convert analog percentage to voltage. diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs index dd7a3ec..31e6bd4 100644 --- a/stabilizer/src/pounder/rf_power.rs +++ b/stabilizer/src/pounder/rf_power.rs @@ -1,10 +1,9 @@ -use super::Error; -use super::InputChannel; +use super::{Error, Channel}; pub trait PowerMeasurementInterface { - fn sample_converter(&mut self, channel: InputChannel) -> Result; + fn sample_converter(&mut self, channel: Channel) -> Result; - fn measure_power(&mut self, channel: InputChannel) -> Result { + fn measure_power(&mut self, channel: Channel) -> Result { let analog_measurement = self.sample_converter(channel)?; // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at diff --git a/stabilizer/src/pounder/types.rs b/stabilizer/src/pounder/types.rs deleted file mode 100644 index 9938bfe..0000000 --- a/stabilizer/src/pounder/types.rs +++ /dev/null @@ -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, -} diff --git a/stabilizer/src/server.rs b/stabilizer/src/server.rs index adf2fdc..1a21dfb 100644 --- a/stabilizer/src/server.rs +++ b/stabilizer/src/server.rs @@ -27,10 +27,10 @@ pub enum AccessRequest { } #[derive(Deserialize, Serialize, Debug)] -pub struct Request<'a, 'b> { +pub struct Request<'a> { pub req: AccessRequest, pub attribute: &'a str, - pub value: &'b str, + pub value: String, } #[derive(Serialize, Deserialize)] @@ -42,24 +42,74 @@ pub struct IirRequest { #[derive(Serialize)] pub struct Response { code: i32, - attribute: String, - value: String, + attribute: String, + value: String, +} + +impl<'a> Request<'a> { + pub fn restore_value(&mut self) { + let mut new_value: String = 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 { + + fn sanitize_value(&mut self) { + let mut new_value: String = 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 = 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 { - 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 { - 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 { - 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(socket: &mut net::socket::TcpSocket, msg: &T) { - let mut u: String = to_string(msg).unwrap(); + let mut u: String = to_string(msg).unwrap(); u.push('\n').unwrap(); socket.write_str(&u).unwrap(); } @@ -122,7 +172,8 @@ impl Server { } else { let r = from_slice::(&self.data[..self.data.len() - 1]); match r { - Ok(res) => { + Ok(mut res) => { + res.restore_value(); let response = f(&res); json_reply(socket, &response); },