From 8f8baa0cee173c89412a6477994305ecbdd2433d Mon Sep 17 00:00:00 2001 From: atse Date: Tue, 20 Aug 2024 17:42:44 +0800 Subject: [PATCH] Add command for flipping output polarity Effectively switches all values related to the directionality of current through the TEC, including current maximums. The swapped status is stored in the flash store. To swap current directions, use the command "pwm polarity_swapped ", where is true when TEC current direction is reversed from the front panel markings. This is needed for IDC cable connections, since the IDC connector and front panel connectors have flipped polarities. --- README.md | 73 +++++++++++++++++++++--------------------- src/channel_state.rs | 2 ++ src/channels.rs | 55 ++++++++++++++++++++++++++----- src/command_handler.rs | 7 ++++ src/command_parser.rs | 24 ++++++++++++++ src/config.rs | 5 ++- 6 files changed, 121 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index bf6cff8..10fd042 100644 --- a/README.md +++ b/README.md @@ -94,42 +94,43 @@ The scope of this setting is per TCP session. Send commands as simple text string terminated by `\n`. Responses are formatted as line-delimited JSON. -| Syntax | Function | -|----------------------------------|-------------------------------------------------------------------------------| -| `report` | Show current input | -| `report mode` | Show current report mode | -| `report mode ` | Set report mode | -| `pwm` | Show current PWM settings | -| `pwm <0/1> max_i_pos ` | Set maximum positive output current | -| `pwm <0/1> max_i_neg ` | Set maximum negative output current | -| `pwm <0/1> max_v ` | Set maximum output voltage | -| `pwm <0/1> i_set ` | Disengage PID, set fixed output current | -| `pwm <0/1> pid` | Let output current to be controlled by the PID | -| `center <0/1> ` | Set the MAX1968 0A-centerpoint to the specified fixed voltage | -| `center <0/1> vref` | Set the MAX1968 0A-centerpoint to measure from VREF | -| `pid` | Show PID configuration | -| `pid <0/1> target ` | Set the PID controller target temperature | -| `pid <0/1> kp ` | Set proportional gain | -| `pid <0/1> ki ` | Set integral gain | -| `pid <0/1> kd ` | Set differential gain | -| `pid <0/1> output_min ` | Set mininum output | -| `pid <0/1> output_max ` | Set maximum output | -| `s-h` | Show Steinhart-Hart equation parameters | -| `s-h <0/1> ` | Set Steinhart-Hart parameter for a channel | -| `postfilter` | Show postfilter settings | -| `postfilter <0/1> off` | Disable postfilter | -| `postfilter <0/1> rate ` | Set postfilter output data rate | -| `load [0/1]` | Restore configuration for channel all/0/1 from flash | -| `save [0/1]` | Save configuration for channel all/0/1 to flash | -| `reset` | Reset the device | -| `dfu` | Reset device and enters USB device firmware update (DFU) mode | -| `ipv4 [Y.Y.Y.Y]` | Configure IPv4 address, netmask length, and optional default gateway | -| `fan` | Show current fan settings and sensors' measurements | -| `fan ` | Set fan power with values from 1 to 100 | -| `fan auto` | Enable automatic fan speed control | -| `fcurve ` | Set fan controller curve coefficients (see *Fan control* section) | -| `fcurve default` | Set fan controller curve coefficients to defaults (see *Fan control* section) | -| `hwrev` | Show hardware revision, and settings related to it | +| Syntax | Function | +|-------------------------------------------|-------------------------------------------------------------------------------| +| `report` | Show current input | +| `report mode` | Show current report mode | +| `report mode ` | Set report mode | +| `pwm` | Show current PWM settings | +| `pwm <0/1> max_i_pos ` | Set maximum positive output current | +| `pwm <0/1> max_i_neg ` | Set maximum negative output current | +| `pwm <0/1> max_v ` | Set maximum output voltage | +| `pwm <0/1> i_set ` | Disengage PID, set fixed output current | +| `pwm <0/1> polarity_swapped ` | Swap output current polarity on channel | +| `pwm <0/1> pid` | Let output current to be controlled by the PID | +| `center <0/1> ` | Set the MAX1968 0A-centerpoint to the specified fixed voltage | +| `center <0/1> vref` | Set the MAX1968 0A-centerpoint to measure from VREF | +| `pid` | Show PID configuration | +| `pid <0/1> target ` | Set the PID controller target temperature | +| `pid <0/1> kp ` | Set proportional gain | +| `pid <0/1> ki ` | Set integral gain | +| `pid <0/1> kd ` | Set differential gain | +| `pid <0/1> output_min ` | Set mininum output | +| `pid <0/1> output_max ` | Set maximum output | +| `s-h` | Show Steinhart-Hart equation parameters | +| `s-h <0/1> ` | Set Steinhart-Hart parameter for a channel | +| `postfilter` | Show postfilter settings | +| `postfilter <0/1> off` | Disable postfilter | +| `postfilter <0/1> rate ` | Set postfilter output data rate | +| `load [0/1]` | Restore configuration for channel all/0/1 from flash | +| `save [0/1]` | Save configuration for channel all/0/1 to flash | +| `reset` | Reset the device | +| `dfu` | Reset device and enters USB device firmware update (DFU) mode | +| `ipv4 [Y.Y.Y.Y]` | Configure IPv4 address, netmask length, and optional default gateway | +| `fan` | Show current fan settings and sensors' measurements | +| `fan ` | Set fan power with values from 1 to 100 | +| `fan auto` | Enable automatic fan speed control | +| `fcurve ` | Set fan controller curve coefficients (see *Fan control* section) | +| `fcurve default` | Set fan controller curve coefficients to defaults (see *Fan control* section) | +| `hwrev` | Show hardware revision, and settings related to it | ## USB diff --git a/src/channel_state.rs b/src/channel_state.rs index 4820131..fdcd26f 100644 --- a/src/channel_state.rs +++ b/src/channel_state.rs @@ -35,6 +35,7 @@ pub struct ChannelState { pub pid_engaged: bool, pub pid: pid::Controller, pub sh: sh::Parameters, + pub polarity_swapped: bool, } impl ChannelState { @@ -51,6 +52,7 @@ impl ChannelState { pid_engaged: false, pid: pid::Controller::new(pid::Parameters::default()), sh: sh::Parameters::default(), + polarity_swapped: false, } } diff --git a/src/channels.rs b/src/channels.rs index 37ba961..5ea9315 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -154,6 +154,12 @@ impl Channels { pub fn set_i(&mut self, channel: usize, i_set: ElectricCurrent) -> ElectricCurrent { let i_set = i_set.min(MAX_TEC_I).max(-MAX_TEC_I); + self.channel_state(channel).i_set = i_set; + let negate = if self.channel_state(channel).polarity_swapped { + -1.0 + } else { + 1.0 + }; let vref_meas = match channel.into() { 0 => self.channel0.vref_meas, 1 => self.channel1.vref_meas, @@ -161,10 +167,9 @@ impl Channels { }; let center_point = vref_meas; let r_sense = ElectricalResistance::new::(R_SENSE); - let voltage = i_set * 10.0 * r_sense + center_point; + let voltage = negate * i_set * 10.0 * r_sense + center_point; let voltage = self.set_dac(channel, voltage); - let i_set = (voltage - center_point) / (10.0 * r_sense); - self.channel_state(channel).i_set = i_set; + let i_set = negate * (voltage - center_point) / (10.0 * r_sense); i_set } @@ -386,19 +391,32 @@ impl Channels { pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) { let max = ElectricCurrent::new::(3.0); - let duty = self.get_pwm(channel, PwmPin::MaxIPos); + let duty = if self.channel_state(channel).polarity_swapped { + self.get_pwm(channel, PwmPin::MaxINeg) + } else { + self.get_pwm(channel, PwmPin::MaxIPos) + }; (duty * max, MAX_TEC_I) } pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) { let max = ElectricCurrent::new::(3.0); - let duty = self.get_pwm(channel, PwmPin::MaxINeg); + let duty = if self.channel_state(channel).polarity_swapped { + self.get_pwm(channel, PwmPin::MaxIPos) + } else { + self.get_pwm(channel, PwmPin::MaxINeg) + }; (duty * max, MAX_TEC_I) } // Get current passing through TEC pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent { - (self.adc_read(channel, PinsAdcReadTarget::ITec, 16) - self.adc_read(channel, PinsAdcReadTarget::VREF, 16)) / ElectricalResistance::new::(0.4) + let tec_i = (self.adc_read(channel, PinsAdcReadTarget::ITec, 16) - self.adc_read(channel, PinsAdcReadTarget::VREF, 16)) / ElectricalResistance::new::(0.4); + if self.channel_state(channel).polarity_swapped { + -tec_i + } else { + tec_i + } } // Get voltage across TEC @@ -443,17 +461,38 @@ impl Channels { pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) { let max = ElectricCurrent::new::(3.0); let duty = (max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero()) / max).get::(); - let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty); + let duty = if self.channel_state(channel).polarity_swapped { + self.set_pwm(channel, PwmPin::MaxINeg, duty) + } else { + self.set_pwm(channel, PwmPin::MaxIPos, duty) + }; (duty * max, max) } pub fn set_max_i_neg(&mut self, channel: usize, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) { let max = ElectricCurrent::new::(3.0); let duty = (max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero()) / max).get::(); - let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty); + let duty = if self.channel_state(channel).polarity_swapped { + self.set_pwm(channel, PwmPin::MaxIPos, duty) + } else { + self.set_pwm(channel, PwmPin::MaxINeg, duty) + }; (duty * max, max) } + pub fn swap_polarity(&mut self, channel: usize, swapped: bool) { + if self.channel_state(channel).polarity_swapped != swapped { + let i_set = self.channel_state(channel).i_set; + let max_i_pos = self.get_max_i_pos(channel).0; + let max_i_neg = self.get_max_i_neg(channel).0; + + self.channel_state(channel).polarity_swapped = swapped; + self.set_i(channel, i_set); + self.set_max_i_pos(channel, max_i_pos); + self.set_max_i_neg(channel, max_i_neg); + } + } + fn report(&mut self, channel: usize) -> Report { let i_set = self.get_i(channel); let i_tec = self.adc_read(channel, PinsAdcReadTarget::ITec, 16); diff --git a/src/command_handler.rs b/src/command_handler.rs index 3f30083..5599aab 100644 --- a/src/command_handler.rs +++ b/src/command_handler.rs @@ -181,6 +181,12 @@ impl Handler { Ok(Handler::Handled) } + fn swap_polarity (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, swapped: bool) -> Result { + channels.swap_polarity(channel, swapped); + send_line(socket, b"{}"); + Ok(Handler::Handled) + } + fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, pin: PwmPin, value: f64) -> Result { match pin { PwmPin::ISet => { @@ -424,6 +430,7 @@ impl Handler { Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels), Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config), Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel), + Command::PwmPolaritySwapped { channel, swapped } => Handler::swap_polarity(socket, channels, channel, swapped), Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, channel, pin, value), Command::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center), Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value), diff --git a/src/command_parser.rs b/src/command_parser.rs index b9e70bb..0e802f8 100644 --- a/src/command_parser.rs +++ b/src/command_parser.rs @@ -159,6 +159,10 @@ pub enum Command { PwmPid { channel: usize, }, + PwmPolaritySwapped { + channel: usize, + swapped: bool, + }, CenterPoint { channel: usize, center: CenterPoint, @@ -239,6 +243,12 @@ fn off_on(input: &[u8]) -> IResult<&[u8], bool> { ))(input) } +fn boolean(input: &[u8]) -> IResult<&[u8], bool> { + alt((value(false, tag("false")), + value(true, tag("true")) + ))(input) +} + fn channel(input: &[u8]) -> IResult<&[u8], usize> { map(one_of("01"), |c| (c as usize) - ('0' as usize))(input) } @@ -321,6 +331,16 @@ fn pwm_pid(input: &[u8]) -> IResult<&[u8], ()> { value((), tag("pid"))(input) } +fn pwm_polarity_swapped(input: &[u8]) -> IResult<&[u8], bool> { + preceded( + tag("polarity_swapped"), + preceded( + whitespace, + boolean, + ) + )(input) +} + fn pwm(input: &[u8]) -> IResult<&[u8], Result> { let (input, _) = tag("pwm")(input)?; alt(( @@ -333,6 +353,10 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result> { let (input, ()) = pwm_pid(input)?; Ok((input, Ok(Command::PwmPid { channel }))) }, + |input| { + let (input, swapped) = pwm_polarity_swapped(input)?; + Ok((input, Ok(Command::PwmPolaritySwapped { channel, swapped }))) + }, |input| { let (input, config) = pwm_setup(input)?; match config { diff --git a/src/config.rs b/src/config.rs index 1649dde..073935d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,6 +20,7 @@ pub struct ChannelConfig { pid_target: f32, pid_engaged: bool, i_set: ElectricCurrent, + polarity_swapped: bool, sh: steinhart_hart::Parameters, pwm: PwmLimits, /// uses variant `PostFilter::Invalid` instead of `None` to save space @@ -45,7 +46,8 @@ impl ChannelConfig { pid: state.pid.parameters.clone(), pid_target: state.pid.target as f32, pid_engaged: state.pid_engaged, - i_set: i_set, + i_set, + polarity_swapped: state.polarity_swapped, sh: state.sh.clone(), pwm, adc_postfilter, @@ -68,6 +70,7 @@ impl ChannelConfig { }; let _ = channels.adc.set_postfilter(channel as u8, adc_postfilter); let _ = channels.set_i(channel, self.i_set); + channels.swap_polarity(channel, self.polarity_swapped); } }