Skip to content

Commit

Permalink
Merge pull request #183 from quartiq/feature/powerstate
Browse files Browse the repository at this point in the history
Restructuring channel power state to an enum
  • Loading branch information
ryan-summers authored Feb 3, 2022
2 parents 46ed6c8 + 9d861b1 commit 3582849
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 31 deletions.
4 changes: 3 additions & 1 deletion src/hardware/booster_channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ impl BoosterChannels {
.expect("Failed to select channel");

if let Some(channel) = RfChannel::new(manager, pins, delay) {
channels[idx as usize].replace(RfChannelMachine::new(channel));
let mut machine = RfChannelMachine::new(channel);
machine.handle_startup();
channels[idx as usize].replace(machine);
} else {
info!("Channel {} did not enumerate", idx as usize);
}
Expand Down
74 changes: 54 additions & 20 deletions src/hardware/rf_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use minimq::embedded_time::{duration::Extensions, Clock, Instant};

use super::{clock::SystemTimer, platform, I2cBusManager, I2cProxy};
use crate::{
settings::{channel_settings::ChannelSettings, BoosterChannelSettings},
settings::{
channel_settings::ChannelSettings, channel_settings::ChannelState, BoosterChannelSettings,
},
Error,
};
use embedded_hal::blocking::delay::DelayUs;
Expand Down Expand Up @@ -629,7 +631,7 @@ mod sm {

statemachine! {
transitions: {
*Off + InterlockReset / start_powerup = Powerup(Instant<SystemTimer>),
*Off + InterlockReset [guard_powerup] / start_powerup = Powerup(Instant<SystemTimer>),
Off + Disable = Off,
Off + Fault(ChannelFault) / handle_fault = Blocked(ChannelFault),

Expand All @@ -641,12 +643,14 @@ mod sm {
Powered + Disable / start_disable = Powerdown(Instant<SystemTimer>),
Powered + Fault(ChannelFault) / handle_fault = Blocked(ChannelFault),

Enabled + InterlockReset / start_powerup = Powerup(Instant<SystemTimer>),
Enabled + InterlockReset = Enabled,
Enabled + Trip(Interlock) / handle_trip = Tripped(Interlock),
Enabled + DisableRf / disable_rf_switch = Powered,
Enabled + Disable / start_disable = Powerdown(Instant<SystemTimer>),
Enabled + Fault(ChannelFault) / handle_fault = Blocked(ChannelFault),

Tripped(Interlock) + InterlockReset / start_powerup_interlock = Powerup(Instant<SystemTimer>),
Tripped(Interlock) + InterlockReset = Powered,
Tripped(Interlock) + DisableRf = Powered,
Tripped(Interlock) + Disable / start_disable_interlock = Powerdown(Instant<SystemTimer>),
Tripped(Interlock) + Fault(ChannelFault) / handle_fault_interlock = Blocked(ChannelFault),

Expand All @@ -661,10 +665,15 @@ mod sm {
impl sm::StateMachineContext for RfChannel {
/// Handle the occurrence of a tripped interlock.
fn handle_trip(&mut self, interlock: &Interlock) -> Interlock {
self.pins.signal_on.set_low().unwrap();
self.disable_rf_switch();
*interlock
}

/// Turn off the RF output enable switch.
fn disable_rf_switch(&mut self) {
self.pins.signal_on.set_low().unwrap();
}

/// Begin the process of powering up the channel.
///
/// # Returns
Expand All @@ -677,7 +686,7 @@ impl sm::StateMachineContext for RfChannel {
.expect("Failed to disable RF bias voltage");

// Ensure that the RF output is disabled during the power-up process.
self.pins.signal_on.set_low().unwrap();
self.disable_rf_switch();

// Start the LM3880 power supply sequencer.
self.pins.enable_power.set_high().unwrap();
Expand All @@ -687,8 +696,17 @@ impl sm::StateMachineContext for RfChannel {
self.clock.try_now().unwrap() + 200_u32.milliseconds()
}

fn start_powerup_interlock(&mut self, _: &Interlock) -> Instant<SystemTimer> {
self.start_powerup()
/// Guard against powering up the channel.
///
/// # Returns
/// Ok if the channel can power up. Err otherwise.
fn guard_powerup(&mut self) -> Result<(), ()> {
let settings = self.settings.settings();
if settings.state == ChannelState::Off {
Err(())
} else {
Ok(())
}
}

/// Check to see if it's currently acceptable to enable the RF output switch.
Expand All @@ -712,8 +730,8 @@ impl sm::StateMachineContext for RfChannel {
return Err(());
}

// Do not enable output if it shouldn't be disabled due to settings.
if !settings.enabled || settings.rf_disable {
// Do not enable output if it shouldn't be enabled due to settings.
if settings.state != ChannelState::Enabled {
return Err(());
}

Expand Down Expand Up @@ -741,7 +759,7 @@ impl sm::StateMachineContext for RfChannel {
/// # Returns
/// The time at which the powerdown process can be deemed complete.
fn start_disable(&mut self) -> Instant<SystemTimer> {
self.pins.signal_on.set_low().unwrap();
self.disable_rf_switch();

// Set the bias DAC output into pinch-off.
self.devices
Expand Down Expand Up @@ -845,19 +863,35 @@ impl sm::StateMachine<RfChannel> {
self.process_event(sm::Events::Disable).ok();
}

/// Handle initial startup of the channel.
pub fn handle_startup(&mut self) {
// Start powering up the channel. Note that we guard against the current channel
// configuration state here.
self.process_event(sm::Events::InterlockReset).ok();
}

/// Handle an update to channel settings.
pub fn handle_settings(&mut self, settings: &ChannelSettings) -> Result<(), Error> {
self.context_mut().apply_settings(settings)?;

if !settings.enabled {
// If settings has us disabled, it's always okay to blindly power down.
self.process_event(sm::Events::Disable).ok();
} else if settings.enabled != self.context().pins.enable_power.is_high().unwrap()
|| settings.rf_disable != self.context().pins.signal_on.is_low().unwrap()
{
// Our current power state has a mismatch with the settings. Reset ourselves into the
// updated state.
self.process_event(sm::Events::InterlockReset).ok();
match (self.state(), settings.state) {
// It's always acceptable to power off.
(_, ChannelState::Off) => {
self.process_event(sm::Events::Disable).ok();
}

// For bias tuning, we may need to disable the RF switch.
(sm::States::Enabled | sm::States::Tripped(_), ChannelState::Powered) => {
self.process_event(sm::Events::DisableRf).unwrap();
}

(sm::States::Off, ChannelState::Powered | ChannelState::Enabled) => {
self.process_event(sm::Events::InterlockReset).unwrap();
}

// Note: Note: Powered -> Enabled transitions are handled via the periodic `Update`
// service event automatically.
_ => {}
}

Ok(())
Expand Down
34 changes: 25 additions & 9 deletions src/settings/channel_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,29 @@ const EXPECTED_VERSION: SemVersion = SemVersion {
patch: 1,
};

/// Indicates the desired state of a channel.
#[derive(serde::Serialize, serde::Deserialize, Miniconf, Copy, Clone, PartialEq)]
pub enum ChannelState {
/// The channel should be turned off and power should be disconnected.
Off = 0,

/// The channel stages are powered and the RF switch is enabled.
// For compatibility reasons, Enabled is stored with the value equivalent to "true"
Enabled = 1,

/// Stages are powered but RF switch is disabled. Used for bias current tuning.
Powered = 2,
}

/// Represents booster channel-specific configuration values.
#[derive(Miniconf, serde::Serialize, serde::Deserialize, Copy, Clone, PartialEq)]
pub struct ChannelSettings {
pub output_interlock_threshold: f32,
pub bias_voltage: f32,
pub enabled: bool,
pub state: ChannelState,
pub input_power_transform: LinearTransformation,
pub output_power_transform: LinearTransformation,
pub reflected_power_transform: LinearTransformation,

// Note: This field is not persisted to external memory.
#[serde(skip)]
pub rf_disable: bool,
}

impl Default for ChannelSettings {
Expand All @@ -39,8 +49,7 @@ impl Default for ChannelSettings {
Self {
output_interlock_threshold: 0.0,
bias_voltage: -3.2,
enabled: false,
rf_disable: false,
state: ChannelState::Off,

// When operating at 100MHz, the power detectors specify the following output
// characteristics for -10 dBm to 10 dBm (the equation uses slightly different coefficients
Expand All @@ -63,7 +72,7 @@ impl Default for ChannelSettings {
}

/// Represents versioned channel-specific configuration values.
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(serde::Serialize, serde::Deserialize, Copy, Clone)]
struct VersionedChannelData {
version: SemVersion,
settings: ChannelSettings,
Expand Down Expand Up @@ -108,8 +117,15 @@ impl VersionedChannelData {
/// # Args
/// * `config` - The sinara configuration to serialize the booster configuration into.
pub fn serialize_into(&self, config: &mut SinaraConfiguration) {
// We will never store `Powered` in EEPROM, since this is never desired. Cache the current
// power state while we serialize to ensure we only serialize Enabled and Off.
let mut versioned_copy = *self;
if versioned_copy.settings.state == ChannelState::Powered {
versioned_copy.settings.state = ChannelState::Off;
}

let mut buffer: [u8; 64] = [0; 64];
let serialized = postcard::to_slice(self, &mut buffer).unwrap();
let serialized = postcard::to_slice(&versioned_copy, &mut buffer).unwrap();
config.board_data[..serialized.len()].copy_from_slice(serialized);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use channel_settings::BoosterChannelSettings;
pub use global_settings::BoosterSettings;

/// A semantic version control for recording software versions.
#[derive(serde::Serialize, serde::Deserialize, PartialEq)]
#[derive(serde::Serialize, serde::Deserialize, PartialEq, Copy, Clone)]
pub struct SemVersion {
major: u8,
minor: u8,
Expand Down

0 comments on commit 3582849

Please sign in to comment.