Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructuring channel power state to an enum #183

Merged
merged 10 commits into from
Feb 3, 2022
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 @@ -624,7 +626,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 @@ -636,12 +638,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 @@ -656,10 +660,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 @@ -672,7 +681,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 @@ -682,8 +691,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 @@ -707,8 +725,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 @@ -736,7 +754,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 @@ -840,19 +858,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,
ryan-summers marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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