From f1a58b781164bf2764c264feeaa9aaed1143b69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 25 May 2021 15:21:09 +0000 Subject: [PATCH 01/10] pounder: simplify attenuator spi interface --- src/hardware/pounder/attenuators.rs | 19 ++++++++----------- src/hardware/pounder/mod.rs | 21 ++------------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/hardware/pounder/attenuators.rs b/src/hardware/pounder/attenuators.rs index 156da9f..092338e 100644 --- a/src/hardware/pounder/attenuators.rs +++ b/src/hardware/pounder/attenuators.rs @@ -30,16 +30,16 @@ 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_attenuators(&mut channels)?; + self.transfer_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) << 2; - self.write_all_attenuators(&channels)?; + self.transfer_attenuators(&mut channels)?; // Finally, latch the output of the updated channel to force it into an active state. - self.latch_attenuators(channel)?; + self.latch_attenuator(channel)?; Ok(attenuation_code as f32 / 2.0) } @@ -57,8 +57,8 @@ pub trait AttenuatorInterface { // 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_attenuators(&mut channels)?; - self.write_all_attenuators(&channels)?; + self.transfer_attenuators(&mut channels)?; + self.transfer_attenuators(&mut 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 @@ -74,13 +74,10 @@ pub trait AttenuatorInterface { fn reset_attenuators(&mut self) -> Result<(), Error>; - fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error>; - fn read_all_attenuators( + fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error>; + + fn transfer_attenuators( &mut self, channels: &mut [u8; 4], ) -> Result<(), Error>; - fn write_all_attenuators( - &mut self, - channels: &[u8; 4], - ) -> Result<(), Error>; } diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 32a8e80..dd5ce95 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -344,7 +344,7 @@ impl AttenuatorInterface for PounderDevices { /// /// Args: /// * `channel` - The attenuator channel to latch. - fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> { + fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { Channel::In0 => ATT_LE0_PIN, Channel::In1 => ATT_LE2_PIN, @@ -368,7 +368,7 @@ impl AttenuatorInterface for PounderDevices { /// /// Args: /// * `channels` - A slice to store the channel readings into. - fn read_all_attenuators( + fn transfer_attenuators( &mut self, channels: &mut [u8; 4], ) -> Result<(), Error> { @@ -378,23 +378,6 @@ impl AttenuatorInterface for PounderDevices { Ok(()) } - - /// Write the attenuator shift registers. - /// - /// Args: - /// * `channels` - The data to write into the attenuators. - fn write_all_attenuators( - &mut self, - channels: &[u8; 4], - ) -> Result<(), Error> { - let mut result = [0_u8; 4]; - result.clone_from_slice(channels); - self.attenuator_spi - .transfer(&mut result) - .map_err(|_| Error::Spi)?; - - Ok(()) - } } impl PowerMeasurementInterface for PounderDevices { From a9f5943296b7d324b75d43f2735f4e41be0189ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 25 May 2021 20:59:32 +0000 Subject: [PATCH 02/10] deps: use mcp23017 release --- Cargo.lock | 5 +++-- Cargo.toml | 6 +----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5791b3a..196b050 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,8 +404,9 @@ dependencies = [ [[package]] name = "mcp23017" -version = "0.1.1" -source = "git+https://github.com/lucazulian/mcp23017.git?rev=523d71d#523d71dcb11fc0ea4bd9385ef2527ae7a7eee987" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c32fd6627e73f1cfa95c00ddcdcb5a6a6ddbd10b308d08588a502c018b6e12c" dependencies = [ "embedded-hal", ] diff --git a/Cargo.toml b/Cargo.toml index 2eb2430..0ecfe66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,17 +45,13 @@ ad9959 = { path = "ad9959" } miniconf = "0.1.0" shared-bus = {version = "0.2.2", features = ["cortex-m"] } serde-json-core = "0.3" +mcp23017 = "1.0" # rtt-target bump [dependencies.rtt-logger] git = "https://github.com/quartiq/rtt-logger.git" rev = "70b0eb5" -# rewrite -[dependencies.mcp23017] -git = "https://github.com/lucazulian/mcp23017.git" -rev = "523d71d" - [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] version = "0.9.0" From fc78b087eeba5343d25dd90f729e4bf1a5180dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 25 May 2021 20:59:48 +0000 Subject: [PATCH 03/10] attenuators: use robust latching sequence To ensure that eatch call to latch() generates a rising edge, first assert low, then high, not the other way round. --- src/hardware/pounder/mod.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index dd5ce95..e7f072e 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -313,12 +313,7 @@ impl PounderDevices { .map_err(|_| Error::I2c)?; devices .mcp23017 - .write_gpio(mcp23017::Port::GPIOB, 1 << 5) - .map_err(|_| Error::I2c)?; - - devices - .mcp23017 - .digital_write(EXT_CLK_SEL_PIN, false) + .write_gpio(mcp23017::Port::GPIOB, 0x2F) .map_err(|_| Error::I2c)?; Ok(devices) @@ -351,23 +346,25 @@ impl AttenuatorInterface for PounderDevices { Channel::Out0 => ATT_LE1_PIN, Channel::Out1 => ATT_LE3_PIN, }; - self.mcp23017 - .digital_write(pin, true) + .digital_write(pin, false) + .map_err(|_| Error::I2c)?; + self.mcp23017 + .digital_write(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(pin, false) + .digital_write(pin, true) .map_err(|_| Error::I2c)?; - Ok(()) } /// Read the raw attenuation codes stored in the attenuator shift registers. /// /// Args: - /// * `channels` - A slice to store the channel readings into. + /// * `channels` - A 4 byte slice to be shifted into the + /// attenuators and to contain the data shifted out. fn transfer_attenuators( &mut self, channels: &mut [u8; 4], From 79f883810433a55427791abb76a7163cadac07d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 14:09:26 +0000 Subject: [PATCH 04/10] rf_power: fix measurement --- src/hardware/pounder/rf_power.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/hardware/pounder/rf_power.rs b/src/hardware/pounder/rf_power.rs index ac82b5d..977bd64 100644 --- a/src/hardware/pounder/rf_power.rs +++ b/src/hardware/pounder/rf_power.rs @@ -1,20 +1,22 @@ use super::{Channel, Error}; -/// Provide an interface to measure RF input power in dB. +/// Provide an interface to measure RF input power in dBm. pub trait PowerMeasurementInterface { fn sample_converter(&mut self, channel: Channel) -> Result; /// Measure the power of an input channel in dBm. /// - /// Note: This function assumes the input channel is connected to an AD8363 output. - /// /// Args: - /// * `channel` - The pounder channel to measure the power of in dBm. + /// * `channel` - The pounder input channel to measure the power of. + /// + /// Returns: + /// Power in dBm after the digitally controlled attenuator before the amplifier. 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 - // 100MHz. It also indicates a y-intercept of -58dBm. - Ok(analog_measurement / 0.0517 - 58.0) + // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7 mV/dB at + // 100MHz with an intercept of -58 dBm. + // It is placed behind a 20 dB tap. + Ok(analog_measurement * (1. / 0.0517) + (-58. + 20.)) } } From 94f60c100c7396bba9adab1d98d0ba18aa3a97b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 14:18:46 +0000 Subject: [PATCH 05/10] pounder io extender: hack around some bug --- src/hardware/pounder/mod.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index e7f072e..2e95141 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -17,6 +17,7 @@ use rf_power::PowerMeasurementInterface; use embedded_hal::{adc::OneShot, blocking::spi::Transfer}; +#[allow(dead_code)] const EXT_CLK_SEL_PIN: u8 = 8 + 7; #[allow(dead_code)] const OSC_EN_N_PIN: u8 = 8 + 6; @@ -300,16 +301,16 @@ impl PounderDevices { adc2_in_p, }; - // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator - // selected, attenuators out of reset. Note that testing indicates the output state needs to - // be set first to properly update the output registers. + // Configure power-on-default state for pounder. All LEDs are off, on-board oscillator + // selected and enabled, attenuators out of reset. Note that testing indicates the + // output state needs to be set first to properly update the output registers. devices .mcp23017 .all_pin_mode(mcp23017::PinMode::OUTPUT) .map_err(|_| Error::I2c)?; devices .mcp23017 - .write_gpio(mcp23017::Port::GPIOA, 0x3F) + .write_gpio(mcp23017::Port::GPIOA, 0x00) .map_err(|_| Error::I2c)?; devices .mcp23017 @@ -324,12 +325,11 @@ impl AttenuatorInterface for PounderDevices { /// Reset all of the attenuators to a power-on default state. fn reset_attenuators(&mut self) -> Result<(), Error> { self.mcp23017 - .digital_write(ATT_RST_N_PIN, false) + .write_gpio(mcp23017::Port::GPIOB, 0x0f) .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. + // Duration of one I2C transaction is sufficiently long. self.mcp23017 - .digital_write(ATT_RST_N_PIN, true) + .write_gpio(mcp23017::Port::GPIOB, 0x2f) .map_err(|_| Error::I2c)?; Ok(()) @@ -341,21 +341,17 @@ impl AttenuatorInterface for PounderDevices { /// * `channel` - The attenuator channel to latch. fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { - Channel::In0 => ATT_LE0_PIN, - Channel::In1 => ATT_LE2_PIN, - Channel::Out0 => ATT_LE1_PIN, - Channel::Out1 => ATT_LE3_PIN, + Channel::In0 => 0, + Channel::Out0 => 1, + Channel::In1 => 2, + Channel::Out1 => 3, }; self.mcp23017 - .digital_write(pin, false) + .write_gpio(mcp23017::Port::GPIOB, 0x2f & !(1 << pin)) .map_err(|_| Error::I2c)?; + // Duration of one I2C transaction is sufficiently long. self.mcp23017 - .digital_write(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(pin, true) + .write_gpio(mcp23017::Port::GPIOB, 0x2f) .map_err(|_| Error::I2c)?; Ok(()) } From 3616f1fa5aacf8a2376f85c9a96c7a251df957f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 14:53:32 +0000 Subject: [PATCH 06/10] pounder: fix attenuator indices (latch and shiftreg) Before the discriminant was used despite being a compiler implementaiton detail. This now fixes the discriminant to match byte index in the attenuator shift register and latch-enable index of the gpio extender. --- src/hardware/pounder/attenuators.rs | 2 +- src/hardware/pounder/mod.rs | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/hardware/pounder/attenuators.rs b/src/hardware/pounder/attenuators.rs index 092338e..ef22814 100644 --- a/src/hardware/pounder/attenuators.rs +++ b/src/hardware/pounder/attenuators.rs @@ -35,7 +35,7 @@ pub trait AttenuatorInterface { // 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) << 2; + channels[channel as usize] = !(attenuation_code << 2); self.transfer_attenuators(&mut channels)?; // Finally, latch the output of the updated channel to force it into an active state. diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 2e95141..9e4c228 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -41,10 +41,10 @@ pub enum Error { #[derive(Debug, Copy, Clone)] #[allow(dead_code)] pub enum Channel { - In0, - In1, - Out0, - Out1, + In0 = 0, + Out0 = 1, + In1 = 2, + Out1 = 3, } #[derive(Serialize, Deserialize, Copy, Clone, Debug)] @@ -340,12 +340,7 @@ impl AttenuatorInterface for PounderDevices { /// Args: /// * `channel` - The attenuator channel to latch. fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error> { - let pin = match channel { - Channel::In0 => 0, - Channel::Out0 => 1, - Channel::In1 => 2, - Channel::Out1 => 3, - }; + let pin = channel as u8; self.mcp23017 .write_gpio(mcp23017::Port::GPIOB, 0x2f & !(1 << pin)) .map_err(|_| Error::I2c)?; From 2368a4f6edfe12c2673c03e439c0345e886df2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 15:30:11 +0000 Subject: [PATCH 07/10] pounder: enum for gpio ext pins This is currently unused but it's better to have enums than a set of assorted constants. --- src/hardware/configuration.rs | 2 +- src/hardware/pounder/mod.rs | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index e2eb653..d74b729 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -802,7 +802,7 @@ pub fn setup( let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); let i2c1 = device.I2C1.i2c( (scl, sda), - 100.khz(), + 400.khz(), ccdr.peripheral.I2C1, &ccdr.clocks, ); diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 9e4c228..7257217 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -17,15 +17,21 @@ use rf_power::PowerMeasurementInterface; use embedded_hal::{adc::OneShot, blocking::spi::Transfer}; -#[allow(dead_code)] -const EXT_CLK_SEL_PIN: u8 = 8 + 7; -#[allow(dead_code)] -const OSC_EN_N_PIN: u8 = 8 + 6; -const ATT_RST_N_PIN: u8 = 8 + 5; -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; +pub enum GpioPin { + Led4Green = 0, + Led5Red = 1, + Led6Green = 2, + Led7Red = 3, + Led8Green = 4, + Led9Red = 5, + AttLe0 = 8 + 0, + AttLe1 = 8 + 1, + AttLe2 = 8 + 2, + AttLe3 = 8 + 3, + AttRstN = 8 + 5, + OscEnN = 8 + 6, + ExtClkSel = 8 + 7, +} #[derive(Debug, Copy, Clone)] pub enum Error { From 4f9113cb45b189791e134e20d0893a1121aaf8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 16:06:55 +0000 Subject: [PATCH 08/10] ad9959: refactor pad() --- ad9959/src/lib.rs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 21dfa21..4a66349 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -596,6 +596,22 @@ impl ProfileSerializer { self.index += value.len() + 1; } + fn pad(&mut self) { + // Pad the buffer to 32-bit (4 byte) alignment by adding dummy writes to CSR and LSRR. + match self.index & 3 { + 3 => { + // For a level of 3, we have to pad with 5 bytes to align things. + self.add_write(Register::CSR, &[(self.mode as u8) << 1]); + self.add_write(Register::LSRR, &[0, 0]); + } + 2 => self.add_write(Register::CSR, &[(self.mode as u8) << 1]), + 1 => self.add_write(Register::LSRR, &[0, 0]), + 0 => {} + + _ => unreachable!(), + } + } + /// Get the serialized profile as a slice of 32-bit words. /// /// # Note @@ -604,21 +620,8 @@ impl ProfileSerializer { /// /// # Returns /// A slice of `u32` words representing the serialized profile. - pub fn finalize<'a>(&'a mut self) -> &[u32] { - // Pad the buffer to 32-bit alignment by adding dummy writes to CSR and LSRR. - let padding = 4 - (self.index % 4); - match padding { - 1 => { - // For a pad size of 1, we have to pad with 5 bytes to align things. - self.add_write(Register::CSR, &[(self.mode as u8) << 1]); - self.add_write(Register::LSRR, &[0, 0]); - } - 2 => self.add_write(Register::CSR, &[(self.mode as u8) << 1]), - 3 => self.add_write(Register::LSRR, &[0, 0]), - 4 => {} - - _ => unreachable!(), - } + pub fn finalize<'a>(&'a mut self) -> &'a [u32] { + self.pad(); unsafe { core::slice::from_raw_parts::<'a, u32>( &self.data as *const _ as *const u32, From da7fc08c15de77362312926052fb8d3c5076ff4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 31 May 2021 18:25:34 +0200 Subject: [PATCH 09/10] pounder: add comment on channel enum --- src/hardware/pounder/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 7257217..3f4cd3d 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -44,6 +44,8 @@ pub enum Error { Adc, } +/// The numerical value (discriminant) of the Channel enum is the index in the attenuator shift +/// register as well as the attenuator latch enable signal index on the GPIO extender. #[derive(Debug, Copy, Clone)] #[allow(dead_code)] pub enum Channel { From 4c01a8f92925d8657ddeef76f5bc8fa2815d08f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 31 May 2021 20:41:18 +0200 Subject: [PATCH 10/10] pounder: clippy --- ad9959/src/lib.rs | 1 + src/hardware/pounder/mod.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 4a66349..cafe4fe 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -43,6 +43,7 @@ pub enum Mode { /// The configuration registers within the AD9959 DDS device. The values of each register are /// equivalent to the address. +#[allow(clippy::upper_case_acronyms)] pub enum Register { CSR = 0x00, FR1 = 0x01, diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 3f4cd3d..3105632 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -24,7 +24,7 @@ pub enum GpioPin { Led7Red = 3, Led8Green = 4, Led9Red = 5, - AttLe0 = 8 + 0, + AttLe0 = 8, AttLe1 = 8 + 1, AttLe2 = 8 + 2, AttLe3 = 8 + 3, @@ -89,14 +89,14 @@ pub struct DdsClockConfig { pub external_clock: bool, } -impl Into for Channel { +impl From for ad9959::Channel { /// Translate pounder channels to DDS output channels. - 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, + fn from(other: Channel) -> Self { + match other { + Channel::In0 => Self::Two, + Channel::In1 => Self::Four, + Channel::Out0 => Self::One, + Channel::Out1 => Self::Three, } } }