diff --git a/Cargo.lock b/Cargo.lock index bb74654..3957ff6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,13 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "ad9959" -version = "0.1.0" -dependencies = [ - "bit_field", - "embedded-hal", -] - [[package]] name = "aligned" version = "0.3.2" @@ -54,12 +46,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "bit_field" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" - [[package]] name = "bitflags" version = "1.2.1" @@ -441,7 +427,6 @@ dependencies = [ name = "stabilizer" version = "0.3.0" dependencies = [ - "ad9959", "asm-delay", "cortex-m", "cortex-m-log", diff --git a/Cargo.toml b/Cargo.toml index 05df738..ac00002 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "stabilizer", "stm32h7xx-hal", - "ad9959", ] [profile.dev] diff --git a/ad9959/.gitignore b/ad9959/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/ad9959/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/ad9959/Cargo.toml b/ad9959/Cargo.toml deleted file mode 100644 index f234470..0000000 --- a/ad9959/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "ad9959" -version = "0.1.0" -authors = ["Ryan Summers "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -embedded-hal = {version = "0.2.3", features = ["unproven"]} -bit_field = "0.10.0" diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs deleted file mode 100644 index 5346bdc..0000000 --- a/ad9959/src/lib.rs +++ /dev/null @@ -1,350 +0,0 @@ -#![no_std] - -use bit_field::BitField; -use embedded_hal::{ - digital::v2::OutputPin, - blocking::delay::DelayMs, -}; - -/// A device driver for the AD9959 direct digital synthesis (DDS) chip. -/// -/// This chip provides four independently controllable digital-to-analog output sinusoids with -/// configurable phase, amplitude, and frequency. All channels are inherently synchronized as they -/// are derived off a common system clock. -/// -/// The chip contains a configurable PLL and supports system clock frequencies up to 500 MHz. -/// -/// The chip supports a number of serial interfaces to improve data throughput, including normal, -/// dual, and quad SPI configurations. -pub struct Ad9959 { - interface: INTERFACE, - delay: DELAY, - reference_clock_frequency: u32, - system_clock_multiplier: u8, - io_update: UPDATE, -} - -pub trait Interface { - type Error; - - fn configure_mode(&mut self, mode: Mode) -> Result<(), Self::Error>; - - fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>; - - fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; -} - -#[derive(Copy, Clone, PartialEq)] -pub enum Mode { - SingleBitTwoWire = 0b00, - SingleBitThreeWire = 0b01, - TwoBitSerial = 0b10, - FourBitSerial = 0b11, -} - -/// The configuration registers within the AD9959 DDS device. The values of each register are -/// equivalent to the address. -pub enum Register { - CSR = 0x00, - FR1 = 0x01, - FR2 = 0x02, - CFR = 0x03, - CFTW0 = 0x04, - CPOW0 = 0x05, - ACR = 0x06, - LSRR = 0x07, - RDW = 0x08, - FDW = 0x09, - CW1 = 0x0a, - CW2 = 0x0b, - CW3 = 0x0c, - CW4 = 0x0d, - CW5 = 0x0e, - CW6 = 0x0f, - CW7 = 0x10, - CW8 = 0x11, - CW9 = 0x12, - CW10 = 0x13, - CW11 = 0x14, - CW12 = 0x15, - CW13 = 0x16, - CW14 = 0x17, - CW15 = 0x18, -} - -/// Specifies an output channel of the AD9959 DDS chip. -pub enum Channel { - One = 0, - Two = 1, - Three = 2, - Four = 3, -} - -/// Possible errors generated by the AD9959 driver. -#[derive(Debug)] -pub enum Error { - Interface(InterfaceE), - Bounds, - Pin, - Frequency, -} - -impl From for Error { - fn from(interface_error: InterfaceE) -> Self { - Error::Interface(interface_error) - } -} - -impl Ad9959 -where - INTERFACE: Interface, - DELAY: DelayMs, - UPDATE: OutputPin, - -{ - pub fn new(interface: INTERFACE, - reset_pin: &mut RST, - io_update: UPDATE, - delay: DELAY, - desired_mode: Mode, - clock_frequency: u32, - multiplier: u8) -> Result> - where - RST: OutputPin, - { - let mut ad9959 = Ad9959 { - interface: interface, - io_update: io_update, - delay: delay, - reference_clock_frequency: clock_frequency, - system_clock_multiplier: 1, - }; - - ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; - - // Reset the AD9959 - reset_pin.set_high().or_else(|_| Err(Error::Pin))?; - - // Delay for a clock cycle to allow the device to reset. - ad9959.delay.delay_ms((1000.0 / clock_frequency as f32) as u8); - - reset_pin.set_low().or_else(|_| Err(Error::Pin))?; - - 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)?; - - // Configure the interface to the desired mode. - ad9959.interface.configure_mode(Mode::FourBitSerial)?; - - // Latch the configuration registers to make them active. - ad9959.latch_configuration()?; - - ad9959.interface.configure_mode(desired_mode)?; - - // Set the clock frequency to configure the device as necessary. - ad9959.configure_system_clock(clock_frequency, multiplier)?; - Ok(ad9959) - } - - fn latch_configuration(&mut self) -> Result<(), Error> { - 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 - // full SYNC_CLK pulse to register. For safety, we latch for 5 here. - self.delay.delay_ms((5000.0 / self.system_clock_frequency()) as u8); - self.io_update.set_low().or_else(|_| Err(Error::Pin))?; - - Ok(()) - } - - /// Configure the internal system clock of the chip. - /// - /// 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. - /// - /// Returns: - /// The actual frequency configured for the internal system clock. - pub fn configure_system_clock(&mut self, - reference_clock_frequency: u32, - prescaler: u8) -> Result> - { - self.reference_clock_frequency = reference_clock_frequency; - - if prescaler != 1 && (prescaler > 20 || prescaler < 4) { - return Err(Error::Bounds); - } - - let frequency = prescaler as f64 * self.reference_clock_frequency as f64; - if frequency > 500_000_000.0f64 { - return Err(Error::Frequency); - } - - // 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); - - 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; - - Ok(self.system_clock_frequency()) - } - - /// Perform a self-test of the communication interface. - /// - /// Note: - /// This modifies the existing channel enables. They are restored upon exit. - /// - /// Returns: - /// True if the self test succeeded. False otherwise. - pub fn self_test(&mut self) -> Result> { - let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; - let old_csr = csr[0]; - - // Enable all channels. - csr[0].set_bits(4..8, 0xF); - self.interface.write(Register::CSR as u8, &csr)?; - - // Read back the enable. - csr[0] = 0; - self.interface.read(Register::CSR as u8, &mut csr)?; - if csr[0].get_bits(4..8) != 0xF { - return Ok(false); - } - - // Clear all channel enables. - csr[0].set_bits(4..8, 0x0); - self.interface.write(Register::CSR as u8, &csr)?; - - // Read back the enable. - csr[0] = 0xFF; - self.interface.read(Register::CSR as u8, &mut csr)?; - if csr[0].get_bits(4..8) != 0 { - return Ok(false); - } - - // Restore the CSR. - csr[0] = old_csr; - self.interface.write(Register::CSR as u8, &csr)?; - - Ok(true) - } - - fn system_clock_frequency(&self) -> f64 { - self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64 - } - - /// Enable an output channel. - pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> { - let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; - csr[0].set_bit(channel as usize + 4, true); - self.interface.write(Register::CSR as u8, &csr)?; - - Ok(()) - } - - /// Disable an output channel. - pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> { - let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; - csr[0].set_bit(channel as usize + 4, false); - self.interface.write(Register::CSR as u8, &csr)?; - - Ok(()) - } - - 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)?; - - 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.write(register as u8, &data)?; - - // Latch the configuration and 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.latch_configuration()?; - 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. - /// - /// Returns: - /// The actual programmed phase offset of the channel in degrees. - 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 * 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) - } - - /// Configure the amplitude of a specified channel. - /// - /// Arguments: - /// * `channel` - The channel to configure the frequency of. - /// * `amplitude` - A normalized amplitude setting [0, 1]. - /// - /// Returns: - /// The actual normalized amplitude of the channel relative to full-scale range. - pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result> { - if amplitude < 0.0 || amplitude > 1.0 { - 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]]; - - // 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)); - - self.modify_channel(channel, Register::ACR, &acr)?; - - Ok(amplitude_control as f32 / 1_u16.wrapping_shl(10) as f32) - } - - /// Configure the frequency of a specified channel. - /// - /// Arguments: - /// * `channel` - The channel to configure the frequency of. - /// * `frequency` - The desired output frequency in Hz. - /// - /// Returns: - /// The actual programmed frequency of the channel. - pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result> { - if frequency < 0.0 || frequency > self.system_clock_frequency() { - return Err(Error::Bounds); - } - - // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the - // frequency tuning word and f_s is the system clock rate. - let tuning_word: u32 = ((frequency as f64 / self.system_clock_frequency()) - * 1u64.wrapping_shl(32) as f64) as u32; - - 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()) - } -} diff --git a/stabilizer/Cargo.toml b/stabilizer/Cargo.toml index ffbd1e4..711476b 100644 --- a/stabilizer/Cargo.toml +++ b/stabilizer/Cargo.toml @@ -49,9 +49,6 @@ version = "0.6" features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"] default-features = false -[dependencies.ad9959] -path = "../ad9959" - [dependencies.stm32h7-ethernet] git = "https://github.com/quartiq/stm32h7-ethernet.git" features = ["stm32h743v"] diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 1840360..1b42929 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -1,4 +1,4 @@ -//#![deny(warnings)] +#![deny(warnings)] #![allow(clippy::missing_safety_doc)] #![no_std] #![no_main] @@ -27,10 +27,7 @@ extern crate panic_halt; #[macro_use] extern crate log; -use nb; - // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; -use asm_delay; use rtfm::cyccnt::{Instant, U32Ext}; use cortex_m_rt::exception; use cortex_m; @@ -51,7 +48,6 @@ use smoltcp as net; static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); mod eth; -mod pounder; mod server; mod afe; @@ -133,8 +129,6 @@ const APP: () = { _eth_mac: ethernet::EthernetMAC, mac_addr: net::wire::EthernetAddress, - //pounder: pounder::PounderDevices, - #[init([[0.; 5]; 2])] iir_state: [IIRState; 2], #[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])] @@ -167,8 +161,6 @@ const APP: () = { clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); - let mut delay = hal::delay::Delay::new(cp.SYST, clocks.clocks); - let gpioa = dp.GPIOA.split(&mut clocks.ahb4); let gpiob = dp.GPIOB.split(&mut clocks.ahb4); let gpioc = dp.GPIOC.split(&mut clocks.ahb4); @@ -282,88 +274,6 @@ const APP: () = { spi }; - // let pounder_devices = { - // let ad9959 = { - // let qspi_interface = { - // // Instantiate the QUADSPI pins and peripheral interface. - // // TODO: Place these into a pins structure that is provided to the QSPI - // // constructor. - // let _qspi_clk = gpiob.pb2.into_alternate_af9(); - // let _qspi_ncs = gpioc.pc11.into_alternate_af9(); - // let _qspi_io0 = gpioe.pe7.into_alternate_af10(); - // let _qspi_io1 = gpioe.pe8.into_alternate_af10(); - // let _qspi_io2 = gpioe.pe9.into_alternate_af10(); - // let _qspi_io3 = gpioe.pe10.into_alternate_af10(); - - // let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); - // pounder::QspiInterface::new(qspi).unwrap() - // }; - - // let mut reset_pin = gpioa.pa0.into_push_pull_output(); - // let io_update = gpiog.pg7.into_push_pull_output(); - - - // let asm_delay = { - // let frequency_hz = clocks.clocks.c_ck().0; - // asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz)) - // }; - - // ad9959::Ad9959::new(qspi_interface, - // &mut reset_pin, - // io_update, - // asm_delay, - // ad9959::Mode::FourBitSerial, - // 100_000_000, - // 5).unwrap() - // }; - - // let io_expander = { - // let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); - // let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); - // let i2c1 = dp.I2C1.i2c((scl, sda), 100.khz(), &clocks); - // mcp23017::MCP23017::default(i2c1).unwrap() - // }; - - // let spi = { - // let spi_mosi = gpiod.pd7.into_alternate_af5(); - // let spi_miso = gpioa.pa6.into_alternate_af5(); - // let spi_sck = gpiog.pg11.into_alternate_af5(); - - // let config = hal::spi::Config::new(hal::spi::Mode{ - // polarity: hal::spi::Polarity::IdleHigh, - // phase: hal::spi::Phase::CaptureOnSecondTransition, - // }) - // .frame_size(8); - - // dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) - // }; - - // let adc1 = { - // let mut adc = dp.ADC1.adc(&mut delay, &mut clocks); - // adc.calibrate(); - - // adc.enable() - // }; - - // let adc2 = { - // let mut adc = dp.ADC2.adc(&mut delay, &mut clocks); - // adc.calibrate(); - - // adc.enable() - // }; - - // let adc1_in_p = gpiof.pf11.into_analog(); - // let adc2_in_p = gpiof.pf14.into_analog(); - - // pounder::PounderDevices::new(io_expander, - // ad9959, - // spi, - // adc1, - // adc2, - // adc1_in_p, - // 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 = gpiod.pd12.into_push_pull_output(); @@ -470,7 +380,6 @@ const APP: () = { _afe2: afe2, timer: timer2, - //pounder: pounder_devices, eeprom_i2c: eeprom_i2c, net_interface: network_interface, diff --git a/stabilizer/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs deleted file mode 100644 index d9785f8..0000000 --- a/stabilizer/src/pounder/attenuators.rs +++ /dev/null @@ -1,57 +0,0 @@ -use super::error::Error; -use super::DdsChannel; - -pub trait AttenuatorInterface { - fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> Result { - 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]; - self.read_all(&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)?; - - // Finally, latch the output of the updated channel to force it into an active state. - self.latch(channel)?; - - Ok(attenuation_code as f32 / 2.0) - } - - fn read(&mut self, channel: DdsChannel) -> 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)?; - - // 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. - let attenuation_code = (!channels[channel as usize]).wrapping_shr(2); - - // Convert the desired channel code into dB of attenuation. - Ok(attenuation_code as f32 / 2.0) - } - - fn reset(&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>; -} 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 deleted file mode 100644 index d29e3bd..0000000 --- a/stabilizer/src/pounder/mod.rs +++ /dev/null @@ -1,265 +0,0 @@ -use mcp23017; -use ad9959; - -pub mod error; -pub 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::{ - blocking::spi::Transfer, - adc::OneShot -}; - -#[allow(dead_code)] -const OSC_EN_N_PIN: u8 = 8 + 7; - -const EXT_CLK_SEL_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; - -pub struct QspiInterface { - pub qspi: hal::qspi::Qspi, - mode: ad9959::Mode, -} - -impl QspiInterface { - pub fn new(mut qspi: hal::qspi::Qspi) -> Result { - qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?; - Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire }) - } -} - -impl ad9959::Interface for QspiInterface { - type Error = Error; - - fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> { - self.mode = mode; - - Ok(()) - } - - fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> { - if (addr & 0x80) != 0 { - return Err(Error::InvalidAddress); - } - - // The QSPI interface implementation always operates in 4-bit mode because the AD9959 uses - // IO3 as SYNC_IO in some output modes. In order for writes to be successful, SYNC_IO must - // be driven low. However, the QSPI peripheral forces IO3 high when operating in 1 or 2 bit - // modes. As a result, any writes while in single- or dual-bit modes has to instead write - // the data encoded into 4-bit QSPI data so that IO3 can be driven low. - match self.mode { - ad9959::Mode::SingleBitTwoWire => { - // Encode the data into a 4-bit QSPI pattern. - - // In 4-bit mode, we can send 2 bits of address and data per byte transfer. As - // such, we need at least 4x more bytes than the length of data. To avoid dynamic - // allocation, we assume the maximum transaction length for single-bit-two-wire is - // 2 bytes. - let mut encoded_data: [u8; 12] = [0; 12]; - - if (data.len() * 4) > (encoded_data.len() - 4) { - return Err(Error::Bounds); - } - - // Encode the address into the first 4 bytes. - for address_bit in 0..8 { - let offset: u8 = { - if address_bit % 2 == 0 { - 4 - } else { - 0 - } - }; - - if addr & address_bit != 0 { - encoded_data[(address_bit >> 1) as usize] |= 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 { - let offset: u8 = { - if address_bit % 2 == 0 { - 4 - } else { - 0 - } - }; - - if byte & address_bit != 0 { - encoded_data[(byte_index + 1) * 4 + (address_bit >> 1) as usize] |= 1 << offset; - } - } - } - - let (encoded_address, encoded_payload) = { - let end_index = (1 + data.len()) * 4; - (encoded_data[0], &encoded_data[1..end_index]) - }; - - self.qspi.write(encoded_address, &encoded_payload).map_err(|_| Error::Qspi) - }, - ad9959::Mode::FourBitSerial => { - self.qspi.write(addr, &data).map_err(|_| Error::Qspi) - }, - _ => { - Err(Error::Qspi) - } - } - } - - fn read(&mut self, addr: u8, mut dest: &mut [u8]) -> Result<(), Error> { - if (addr & 0x80) != 0 { - 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 { - return Err(Error::Qspi); - } - - self.qspi.read(0x80_u8 | addr, &mut dest).map_err(|_| Error::Qspi) - } -} - -pub struct PounderDevices { - pub ad9959: ad9959::Ad9959>>, - mcp23017: mcp23017::MCP23017>, - attenuator_spi: hal::spi::Spi, - adc1: hal::adc::Adc, - adc2: hal::adc::Adc, - adc1_in_p: hal::gpio::gpiof::PF11, - adc2_in_p: hal::gpio::gpiof::PF14, -} - -impl PounderDevices -where - DELAY: embedded_hal::blocking::delay::DelayMs, -{ - pub fn new(mcp23017: mcp23017::MCP23017>, - ad9959: ad9959::Ad9959>>, - attenuator_spi: hal::spi::Spi, - adc1: hal::adc::Adc, - adc2: hal::adc::Adc, - adc1_in_p: hal::gpio::gpiof::PF11, - adc2_in_p: hal::gpio::gpiof::PF14, - ) -> Result { - let mut devices = Self { - mcp23017, - ad9959, - attenuator_spi, - adc1, - adc2, - adc1_in_p, - adc2_in_p, - }; - - // 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.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)?; - - Ok(devices) - } - - pub fn select_external_clock(&mut self, frequency: u32, 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)?; - - Ok(()) - } - - pub 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)?; - - Ok(()) - } -} - -impl AttenuatorInterface for PounderDevices -{ - fn reset(&mut self) -> Result<(), Error> { - self.mcp23017.digital_write(ATT_RST_N_PIN, true).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)?; - - Ok(()) - } - - fn latch(&mut self, channel: DdsChannel) -> 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, - }; - - self.mcp23017.digital_write(pin, true).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(pin, false).map_err(|_| Error::I2c)?; - - Ok(()) - } - - fn read_all(&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> { - let mut result = [0_u8; 4]; - result.clone_from_slice(channels); - self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?; - - Ok(()) - } -} - -impl PowerMeasurementInterface for PounderDevices { - fn sample_converter(&mut self, channel: InputChannel) -> Result { - let adc_scale = match channel { - InputChannel::Zero => { - 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 => { - 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 - }, - }; - - // Convert analog percentage to voltage. - Ok(adc_scale * 3.3) - } -} diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs deleted file mode 100644 index dd7a3ec..0000000 --- a/stabilizer/src/pounder/rf_power.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::Error; -use super::InputChannel; - -pub trait PowerMeasurementInterface { - fn sample_converter(&mut self, channel: InputChannel) -> Result; - - fn measure_power(&mut self, channel: InputChannel) -> Result { - let analog_measurement = self.sample_converter(channel)?; - - // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at - // 100MHz. - Ok(analog_measurement / 0.0517) - } -} 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, -}