forked from M-Labs/thermostat
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 <ch> polarity_swapped <bool>", where <bool> 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.
This commit is contained in:
parent
dfd4d355f6
commit
f02bf414b5
@ -95,7 +95,7 @@ Send commands as simple text string terminated by `\n`. Responses are
|
|||||||
formatted as line-delimited JSON.
|
formatted as line-delimited JSON.
|
||||||
|
|
||||||
| Syntax | Function |
|
| Syntax | Function |
|
||||||
|----------------------------------|-------------------------------------------------------------------------------|
|
|-------------------------------------------|-------------------------------------------------------------------------------|
|
||||||
| `report` | Show current input |
|
| `report` | Show current input |
|
||||||
| `report mode` | Show current report mode |
|
| `report mode` | Show current report mode |
|
||||||
| `report mode <off/on>` | Set report mode |
|
| `report mode <off/on>` | Set report mode |
|
||||||
@ -104,6 +104,7 @@ formatted as line-delimited JSON.
|
|||||||
| `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current |
|
| `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current |
|
||||||
| `pwm <0/1> max_v <volt>` | Set maximum output voltage |
|
| `pwm <0/1> max_v <volt>` | Set maximum output voltage |
|
||||||
| `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current |
|
| `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current |
|
||||||
|
| `pwm <0/1> polarity_swapped <false/true>` | Swap output current polarity on channel |
|
||||||
| `pwm <0/1> pid` | Let output current to be controlled by the PID |
|
| `pwm <0/1> pid` | Let output current to be controlled by the PID |
|
||||||
| `center <0/1> <volt>` | Set the MAX1968 0A-centerpoint to the specified fixed voltage |
|
| `center <0/1> <volt>` | Set the MAX1968 0A-centerpoint to the specified fixed voltage |
|
||||||
| `center <0/1> vref` | Set the MAX1968 0A-centerpoint to measure from VREF |
|
| `center <0/1> vref` | Set the MAX1968 0A-centerpoint to measure from VREF |
|
||||||
|
@ -35,6 +35,7 @@ pub struct ChannelState {
|
|||||||
pub pid_engaged: bool,
|
pub pid_engaged: bool,
|
||||||
pub pid: pid::Controller,
|
pub pid: pid::Controller,
|
||||||
pub sh: sh::Parameters,
|
pub sh: sh::Parameters,
|
||||||
|
pub polarity_swapped: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelState {
|
impl ChannelState {
|
||||||
@ -51,6 +52,7 @@ impl ChannelState {
|
|||||||
pid_engaged: false,
|
pid_engaged: false,
|
||||||
pid: pid::Controller::new(pid::Parameters::default()),
|
pid: pid::Controller::new(pid::Parameters::default()),
|
||||||
sh: sh::Parameters::default(),
|
sh: sh::Parameters::default(),
|
||||||
|
polarity_swapped: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,6 +158,12 @@ impl Channels {
|
|||||||
|
|
||||||
pub fn set_i(&mut self, channel: usize, i_set: ElectricCurrent) -> ElectricCurrent {
|
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);
|
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() {
|
let vref_meas = match channel.into() {
|
||||||
0 => self.channel0.vref_meas,
|
0 => self.channel0.vref_meas,
|
||||||
1 => self.channel1.vref_meas,
|
1 => self.channel1.vref_meas,
|
||||||
@ -165,10 +171,9 @@ impl Channels {
|
|||||||
};
|
};
|
||||||
let center_point = vref_meas;
|
let center_point = vref_meas;
|
||||||
let r_sense = ElectricalResistance::new::<ohm>(R_SENSE);
|
let r_sense = ElectricalResistance::new::<ohm>(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 voltage = self.set_dac(channel, voltage);
|
||||||
let i_set = (voltage - center_point) / (10.0 * r_sense);
|
let i_set = negate * (voltage - center_point) / (10.0 * r_sense);
|
||||||
self.channel_state(channel).i_set = i_set;
|
|
||||||
i_set
|
i_set
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,18 +391,31 @@ impl Channels {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
|
pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
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_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
|
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
|
pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
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_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
|
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current passing through TEC
|
// Get current passing through TEC
|
||||||
pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent {
|
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::<ohm>(0.4)
|
let tec_i = (self.adc_read(channel, PinsAdcReadTarget::ITec, 16) - self.adc_read(channel, PinsAdcReadTarget::VREF, 16)) / ElectricalResistance::new::<ohm>(0.4);
|
||||||
|
if self.channel_state(channel).polarity_swapped {
|
||||||
|
-tec_i
|
||||||
|
} else {
|
||||||
|
tec_i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get voltage across TEC
|
// Get voltage across TEC
|
||||||
@ -442,17 +460,38 @@ impl Channels {
|
|||||||
pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
pub fn set_max_i_pos(&mut self, channel: usize, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
let max = ElectricCurrent::new::<ampere>(3.0);
|
let max = ElectricCurrent::new::<ampere>(3.0);
|
||||||
let duty = (max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
let duty = (max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
||||||
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_TEC_I_DUTY_TO_CURRENT_RATE, max)
|
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_i_neg(&mut self, channel: usize, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
pub fn set_max_i_neg(&mut self, channel: usize, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) {
|
||||||
let max = ElectricCurrent::new::<ampere>(3.0);
|
let max = ElectricCurrent::new::<ampere>(3.0);
|
||||||
let duty = (max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
let duty = (max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero()) / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
||||||
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_TEC_I_DUTY_TO_CURRENT_RATE, max)
|
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, 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 {
|
fn report(&mut self, channel: usize) -> Report {
|
||||||
let i_set = self.get_i(channel);
|
let i_set = self.get_i(channel);
|
||||||
let i_tec = self.adc_read(channel, PinsAdcReadTarget::ITec, 16);
|
let i_tec = self.adc_read(channel, PinsAdcReadTarget::ITec, 16);
|
||||||
|
@ -181,6 +181,12 @@ impl Handler {
|
|||||||
Ok(Handler::Handled)
|
Ok(Handler::Handled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn swap_polarity (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, swapped: bool) -> Result<Handler, Error> {
|
||||||
|
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<Handler, Error> {
|
fn set_pwm (socket: &mut TcpSocket, channels: &mut Channels, channel: usize, pin: PwmPin, value: f64) -> Result<Handler, Error> {
|
||||||
match pin {
|
match pin {
|
||||||
PwmPin::ISet => {
|
PwmPin::ISet => {
|
||||||
@ -424,6 +430,7 @@ impl Handler {
|
|||||||
Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels),
|
Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels),
|
||||||
Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config),
|
Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config),
|
||||||
Command::PwmPid { channel } => Handler::engage_pid(socket, channels, channel),
|
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::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::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center),
|
||||||
Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value),
|
Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value),
|
||||||
|
@ -159,6 +159,10 @@ pub enum Command {
|
|||||||
PwmPid {
|
PwmPid {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
},
|
},
|
||||||
|
PwmPolaritySwapped {
|
||||||
|
channel: usize,
|
||||||
|
swapped: bool,
|
||||||
|
},
|
||||||
CenterPoint {
|
CenterPoint {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
center: CenterPoint,
|
center: CenterPoint,
|
||||||
@ -239,6 +243,12 @@ fn off_on(input: &[u8]) -> IResult<&[u8], bool> {
|
|||||||
))(input)
|
))(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> {
|
fn channel(input: &[u8]) -> IResult<&[u8], usize> {
|
||||||
map(one_of("01"), |c| (c as usize) - ('0' as usize))(input)
|
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)
|
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<Command, Error>> {
|
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
let (input, _) = tag("pwm")(input)?;
|
let (input, _) = tag("pwm")(input)?;
|
||||||
alt((
|
alt((
|
||||||
@ -333,6 +353,10 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
|||||||
let (input, ()) = pwm_pid(input)?;
|
let (input, ()) = pwm_pid(input)?;
|
||||||
Ok((input, Ok(Command::PwmPid { channel })))
|
Ok((input, Ok(Command::PwmPid { channel })))
|
||||||
},
|
},
|
||||||
|
|input| {
|
||||||
|
let (input, swapped) = pwm_polarity_swapped(input)?;
|
||||||
|
Ok((input, Ok(Command::PwmPolaritySwapped { channel, swapped })))
|
||||||
|
},
|
||||||
|input| {
|
|input| {
|
||||||
let (input, config) = pwm_setup(input)?;
|
let (input, config) = pwm_setup(input)?;
|
||||||
match config {
|
match config {
|
||||||
|
@ -20,6 +20,7 @@ pub struct ChannelConfig {
|
|||||||
pid_target: f32,
|
pid_target: f32,
|
||||||
pid_engaged: bool,
|
pid_engaged: bool,
|
||||||
i_set: ElectricCurrent,
|
i_set: ElectricCurrent,
|
||||||
|
polarity_swapped: bool,
|
||||||
sh: steinhart_hart::Parameters,
|
sh: steinhart_hart::Parameters,
|
||||||
pwm: PwmLimits,
|
pwm: PwmLimits,
|
||||||
/// uses variant `PostFilter::Invalid` instead of `None` to save space
|
/// uses variant `PostFilter::Invalid` instead of `None` to save space
|
||||||
@ -45,7 +46,8 @@ impl ChannelConfig {
|
|||||||
pid: state.pid.parameters.clone(),
|
pid: state.pid.parameters.clone(),
|
||||||
pid_target: state.pid.target as f32,
|
pid_target: state.pid.target as f32,
|
||||||
pid_engaged: state.pid_engaged,
|
pid_engaged: state.pid_engaged,
|
||||||
i_set: i_set,
|
i_set,
|
||||||
|
polarity_swapped: state.polarity_swapped,
|
||||||
sh: state.sh.clone(),
|
sh: state.sh.clone(),
|
||||||
pwm,
|
pwm,
|
||||||
adc_postfilter,
|
adc_postfilter,
|
||||||
@ -68,6 +70,7 @@ impl ChannelConfig {
|
|||||||
};
|
};
|
||||||
let _ = channels.adc.set_postfilter(channel as u8, adc_postfilter);
|
let _ = channels.adc.set_postfilter(channel as u8, adc_postfilter);
|
||||||
let _ = channels.set_i(channel, self.i_set);
|
let _ = channels.set_i(channel, self.i_set);
|
||||||
|
channels.swap_polarity(channel, self.polarity_swapped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user