From bc62b007ad00d6cd5aa8b3b06f8b15060d72e935 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Aug 2020 19:45:10 +0200 Subject: [PATCH 1/5] Adding support for sequenced timing --- src/booster_channels.rs | 11 +++++++++++ src/main.rs | 5 ++++- src/rf_channel.rs | 29 +++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/booster_channels.rs b/src/booster_channels.rs index ccb2f48a..d3a8a02c 100644 --- a/src/booster_channels.rs +++ b/src/booster_channels.rs @@ -331,4 +331,15 @@ impl BoosterChannels { None => Err(Error::NotPresent), } } + + /// Update the states of RF channels as necessary. + pub fn update(&mut self) { + for channel in Channel::into_enum_iter() { + self.mux.select_bus(Some(channel.into())).unwrap(); + + if let Some(rf_channel) = &mut self.channels[channel as usize] { + rf_channel.process_state().unwrap(); + } + } + } } diff --git a/src/main.rs b/src/main.rs index d1349a2f..ebd22ad1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,8 @@ const APP: () = { #[init] fn init(c: init::Context) -> init::LateResources { - let cp = cortex_m::peripheral::Peripherals::take().unwrap(); + let mut cp = cortex_m::peripheral::Peripherals::take().unwrap(); + cp.DWT.enable_cycle_counter(); // Initialize the chip let rcc = c.device.RCC.constrain(); @@ -217,6 +218,8 @@ const APP: () = { #[idle(resources=[channels])] fn idle(_: idle::Context) -> ! { loop { + // TODO: Only need to call this periodically (e.g. every 60-200ms or so). + c.resources.channels.update(); asm::nop(); } } diff --git a/src/rf_channel.rs b/src/rf_channel.rs index 527c39e4..61c161e9 100644 --- a/src/rf_channel.rs +++ b/src/rf_channel.rs @@ -22,6 +22,8 @@ use stm32f4xx_hal::{ prelude::*, }; +use rtic::cyccnt::{Instant, Duration}; + // Convenience type definition for all I2C devices on the bus. type I2cDevice = BusProxy; @@ -32,6 +34,12 @@ pub struct SupplyMeasurements { pub i_p28v0ch: f32, } +pub enum ChannelState { + Disabled, + Enabling(Instant), + Active, +} + // Macro magic to generate an enum that looks like: // // ```rust @@ -263,6 +271,7 @@ pub struct RfChannel { input_power_transform: LinearTransformation, reflected_power_transform: LinearTransformation, output_power_transform: LinearTransformation, + state: ChannelState, } impl RfChannel { @@ -313,6 +322,7 @@ impl RfChannel { 1.5 / 0.035, -35.6 + 19.8 + 10.0, ), + state: ChannelState::Disabled, }; channel.set_interlock_thresholds(0.0, 0.0).unwrap(); @@ -417,7 +427,7 @@ impl RfChannel { self.pins.alert.is_low().unwrap() } - /// Enable the channel and power it up. + /// Start the enable process for channel and power it up. pub fn enable(&mut self) -> Result<(), Error> { // TODO: Power-up the channel. self.i2c_devices @@ -425,12 +435,25 @@ impl RfChannel { .set_voltage(3.2) .expect("Failed to disable RF bias voltage"); self.pins.enable_power.set_high().unwrap(); + + // We have just started the supply sequencer for the RF channel power rail. This may take + // some time. We can't set the bias DAC until those supplies have stabilized. + self.state = ChannelState::Enabling(Instant::now()); + + Ok(()) + } + + /// Finalize the enable process once all RF channel supplies have enabled. + fn finalize_enable(&mut self) -> Result<(), Error> { self.i2c_devices .bias_dac - .set_voltage(self.bias_voltage) + .set_voltage(-1.0 * self.bias_voltage) .expect("Failed to configure RF bias voltage"); + self.pins.signal_on.set_high().unwrap(); + self.state = ChannelState::Active; + Ok(()) } @@ -446,6 +469,8 @@ impl RfChannel { self.pins.enable_power.set_low().unwrap(); + self.state = ChannelState::Disabled; + Ok(()) } From f2c789df47d487435a5ecff542ad2f58f3cfd17e Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Aug 2020 19:47:59 +0200 Subject: [PATCH 2/5] Adding WIP --- src/main.rs | 2 +- src/rf_channel.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index ebd22ad1..4a69e422 100644 --- a/src/main.rs +++ b/src/main.rs @@ -216,7 +216,7 @@ const APP: () = { } #[idle(resources=[channels])] - fn idle(_: idle::Context) -> ! { + fn idle(c: idle::Context) -> ! { loop { // TODO: Only need to call this periodically (e.g. every 60-200ms or so). c.resources.channels.update(); diff --git a/src/rf_channel.rs b/src/rf_channel.rs index 61c161e9..8106da51 100644 --- a/src/rf_channel.rs +++ b/src/rf_channel.rs @@ -34,9 +34,15 @@ pub struct SupplyMeasurements { pub i_p28v0ch: f32, } +/// The current state of an RF channel. pub enum ChannelState { + /// The channel output is disabled. Disabled, + + /// The channel is in the enabling process. Enabling(Instant), + + /// The channel is actively outputting. Active, } From 7db4dd87eaea1d3965873e6aaabbeb5d10e39a68 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Aug 2020 19:48:15 +0200 Subject: [PATCH 3/5] Adding state process --- src/rf_channel.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/rf_channel.rs b/src/rf_channel.rs index 8106da51..5f527ae0 100644 --- a/src/rf_channel.rs +++ b/src/rf_channel.rs @@ -407,6 +407,22 @@ impl RfChannel { Ok(()) } + /// Update the current state of the RF channel. + /// + /// # Note + /// This must be called periodically to facilitate enabling a channel. + pub fn process_state(&mut self) -> Result<(), Error> { + if let ChannelState::Enabling(start_time) = self.state { + // TODO: Replace constant definition of CPU frequency here. + if start_time.elapsed() > Duration::from_cycles(200 * (168_000_000 / 1000)) { + self.finalize_enable()?; + } + } + + Ok(()) + } + + /// Check if the channel is indicating an interlock has tripped. pub fn is_overdriven(&self) -> bool { let input_overdrive = self.pins.input_overdrive.is_low().unwrap(); From b9fc4eed9e2c98103dfc4445a410f270f04aebba Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 19 Aug 2020 19:49:49 +0200 Subject: [PATCH 4/5] Fixing format --- src/rf_channel.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rf_channel.rs b/src/rf_channel.rs index 5f527ae0..1388d425 100644 --- a/src/rf_channel.rs +++ b/src/rf_channel.rs @@ -22,7 +22,7 @@ use stm32f4xx_hal::{ prelude::*, }; -use rtic::cyccnt::{Instant, Duration}; +use rtic::cyccnt::{Duration, Instant}; // Convenience type definition for all I2C devices on the bus. type I2cDevice = BusProxy; @@ -422,7 +422,6 @@ impl RfChannel { Ok(()) } - /// Check if the channel is indicating an interlock has tripped. pub fn is_overdriven(&self) -> bool { let input_overdrive = self.pins.input_overdrive.is_low().unwrap(); @@ -460,7 +459,7 @@ impl RfChannel { // We have just started the supply sequencer for the RF channel power rail. This may take // some time. We can't set the bias DAC until those supplies have stabilized. - self.state = ChannelState::Enabling(Instant::now()); + self.state = ChannelState::Enabling(Instant::now()); Ok(()) } From 812c57660a7d0a498c072de801bab272d2660e06 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 20 Aug 2020 06:42:55 +0200 Subject: [PATCH 5/5] Updating state management --- src/booster_channels.rs | 6 +-- src/error.rs | 1 + src/rf_channel.rs | 86 +++++++++++++++++++++++++++++++++-------- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/booster_channels.rs b/src/booster_channels.rs index d3a8a02c..352d8c9c 100644 --- a/src/booster_channels.rs +++ b/src/booster_channels.rs @@ -239,7 +239,7 @@ impl BoosterChannels { self.mux.select_bus(Some(channel.into())).unwrap(); match &mut self.channels[channel as usize] { - Some(rf_channel) => rf_channel.enable(), + Some(rf_channel) => rf_channel.start_enable(), None => Err(Error::NotPresent), } } @@ -252,7 +252,7 @@ impl BoosterChannels { self.mux.select_bus(Some(channel.into())).unwrap(); match &mut self.channels[channel as usize] { - Some(rf_channel) => rf_channel.disable(), + Some(rf_channel) => rf_channel.start_disable(), None => Err(Error::NotPresent), } } @@ -338,7 +338,7 @@ impl BoosterChannels { self.mux.select_bus(Some(channel.into())).unwrap(); if let Some(rf_channel) = &mut self.channels[channel as usize] { - rf_channel.process_state().unwrap(); + rf_channel.update().unwrap(); } } } diff --git a/src/error.rs b/src/error.rs index 8963f746..e1d7c1a9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ #[derive(Debug, Copy, Clone)] pub enum Error { Invalid, + InvalidState, NotPresent, Interface, Bounds, diff --git a/src/rf_channel.rs b/src/rf_channel.rs index 1388d425..318e98fb 100644 --- a/src/rf_channel.rs +++ b/src/rf_channel.rs @@ -44,6 +44,12 @@ pub enum ChannelState { /// The channel is actively outputting. Active, + + /// The channel has tripped an interlock threshold. + Tripped, + + /// The channel is in the process of shutting down. + Disabling(Instant), } // Macro magic to generate an enum that looks like: @@ -411,12 +417,41 @@ impl RfChannel { /// /// # Note /// This must be called periodically to facilitate enabling a channel. - pub fn process_state(&mut self) -> Result<(), Error> { - if let ChannelState::Enabling(start_time) = self.state { - // TODO: Replace constant definition of CPU frequency here. - if start_time.elapsed() > Duration::from_cycles(200 * (168_000_000 / 1000)) { - self.finalize_enable()?; + pub fn update(&mut self) -> Result<(), Error> { + match self.state { + ChannelState::Enabling(start_time) => { + // The LM3880 requires 180ms to power up all supplies on the channel. We add an + // additional 20ms margin. + + // TODO: Replace constant definition of CPU frequency here. + if start_time.elapsed() > Duration::from_cycles(200 * (168_000_000 / 1000)) { + self.finalize_enable()?; + } + } + + ChannelState::Disabling(start_time) => { + // Note that we use 500ms here due to the worst-case power-sequencing operation of + // the LM3880 that occurs when a channel is disabled immediately after enable. In + // this case, the LM3880 will require 180ms to power up the channel, 120ms to + // stabilize, and then 180ms to power down the channel. + + // TODO: Replace constant definition of CPU frequency here. + if start_time.elapsed() > Duration::from_cycles(500 * (168_000_000 / 1000)) { + self.state = ChannelState::Disabled; + } + } + + ChannelState::Active => { + // We explicitly only check for overdrive conditions once the channel has been + // fully enabled. + if self.is_overdriven() || self.is_alarmed() { + self.state = ChannelState::Tripped; + } } + + // There's nothing to do if the channel is disabled or tripped. + ChannelState::Disabled => {} + ChannelState::Tripped => {} } Ok(()) @@ -449,15 +484,24 @@ impl RfChannel { } /// Start the enable process for channel and power it up. - pub fn enable(&mut self) -> Result<(), Error> { - // TODO: Power-up the channel. + pub fn start_enable(&mut self) -> Result<(), Error> { + // It is explicitly not permitted to enable the channel while the channel is in the process + // of disabling because we would not be guaranteed that the channel would be powered up + // within 200ms. + if let ChannelState::Disabling(_) = self.state { + return Err(Error::InvalidState); + } + + // Place the bias DAC to drive the RF amplifier into pinch-off during the power-up process. self.i2c_devices .bias_dac .set_voltage(3.2) .expect("Failed to disable RF bias voltage"); + + // Start the LM3880 power supply sequencer. self.pins.enable_power.set_high().unwrap(); - // We have just started the supply sequencer for the RF channel power rail. This may take + // We have just started the supply sequencer for the RF channel power rail. This will take // some time. We can't set the bias DAC until those supplies have stabilized. self.state = ChannelState::Enabling(Instant::now()); @@ -466,20 +510,28 @@ impl RfChannel { /// Finalize the enable process once all RF channel supplies have enabled. fn finalize_enable(&mut self) -> Result<(), Error> { - self.i2c_devices - .bias_dac - .set_voltage(-1.0 * self.bias_voltage) - .expect("Failed to configure RF bias voltage"); + // It is only valid to finish the enable process if we have previously started enabling. + if let ChannelState::Enabling(_) = self.state { + self.i2c_devices + .bias_dac + .set_voltage(-1.0 * self.bias_voltage) + .expect("Failed to configure RF bias voltage"); - self.pins.signal_on.set_high().unwrap(); + self.pins.signal_on.set_high().unwrap(); - self.state = ChannelState::Active; + self.state = ChannelState::Active; - Ok(()) + Ok(()) + } else { + Err(Error::InvalidState) + } } /// Disable the channel and power it off. - pub fn disable(&mut self) -> Result<(), Error> { + pub fn start_disable(&mut self) -> Result<(), Error> { + // The RF channel may be unconditionally disabled at any point to aid in preventing damage. + // The effect of this is that we must assume worst-case power-down timing, which increases + // the time until we can enable a channel after a power-down. self.pins.signal_on.set_low().unwrap(); // Set the bias DAC output into pinch-off. @@ -490,7 +542,7 @@ impl RfChannel { self.pins.enable_power.set_low().unwrap(); - self.state = ChannelState::Disabled; + self.state = ChannelState::Disabling(Instant::now()); Ok(()) }