Compare commits

...

3 Commits

Author SHA1 Message Date
0fe2d645e2 Show and save set value for PWM limits
Show in PwmSummary the set value, before all PWM duty calculations
instead of the machine value after the calculations. Also save the set
value into the flash store instead of the machine value.
2024-09-30 17:46:42 +08:00
ee26616c09 README: Document PWM value clamping 2024-09-30 17:44:24 +08:00
c3b423b5f4 PwmSummary: Don't show max PWM values
Putting the maximum in PwmSummary has little use - the maximum never
changes throughout the runtime of the firmware and can be replaced by
documentation elsewhere.
2024-09-30 17:44:20 +08:00
5 changed files with 50 additions and 79 deletions

View File

@ -96,10 +96,10 @@ formatted as line-delimited JSON.
|----------------------------------|-------------------------------------------------------------------------------| |----------------------------------|-------------------------------------------------------------------------------|
| `report` | Show current input | | `report` | Show current input |
| `pwm` | Show current PWM settings | | `pwm` | Show current PWM settings |
| `pwm <0/1> max_i_pos <amp>` | Set maximum positive output current | | `pwm <0/1> max_i_pos <amp>` | Set maximum positive output current, clamped to [0, 2] |
| `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current | | `pwm <0/1> max_i_neg <amp>` | Set maximum negative output current, clamped to [0, 2] |
| `pwm <0/1> max_v <volt>` | Set maximum output voltage | | `pwm <0/1> max_v <volt>` | Set maximum output voltage, clamped to [0, 4] |
| `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current | | `pwm <0/1> i_set <amp>` | Disengage PID, set fixed output current, clamped to [-2, 2] |
| `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 |

View File

@ -16,7 +16,7 @@ class Client:
pwm_report = self.get_pwm() pwm_report = self.get_pwm()
for pwm_channel in pwm_report: for pwm_channel in pwm_report:
for limit in ["max_i_neg", "max_i_pos", "max_v"]: for limit in ["max_i_neg", "max_i_pos", "max_v"]:
if pwm_channel[limit]["value"] == 0.0: if pwm_channel[limit] == 0.0:
logging.warning("`{}` limit is set to zero on channel {}".format(limit, pwm_channel["channel"])) logging.warning("`{}` limit is set to zero on channel {}".format(limit, pwm_channel["channel"]))
def _read_line(self): def _read_line(self):
@ -53,16 +53,16 @@ class Client:
Example:: Example::
[{'channel': 0, [{'channel': 0,
'center': 'vref', 'center': 'vref',
'i_set': {'max': 2.9802790335151985, 'value': -0.02002179650216762}, 'i_set': -0.02002179650216762,
'max_i_neg': {'max': 3.0, 'value': 3.0}, 'max_i_neg': 2.0,
'max_v': {'max': 5.988, 'value': 5.988}, 'max_v': 3.988,
'max_i_pos': {'max': 3.0, 'value': 3.0}}, 'max_i_pos': 2.0,
{'channel': 1, {'channel': 1,
'center': 'vref', 'center': 'vref',
'i_set': {'max': 2.9802790335151985, 'value': -0.02002179650216762}, 'i_set': -0.02002179650216762,
'max_i_neg': {'max': 3.0, 'value': 3.0}, 'max_i_neg': 2.0,
'max_v': {'max': 5.988, 'value': 5.988}, 'max_v': 3.988,
'max_i_pos': {'max': 3.0, 'value': 3.0}} 'max_i_pos': 2.0}
] ]
""" """
return self._get_conf("pwm") return self._get_conf("pwm")

View File

@ -16,6 +16,7 @@ use uom::si::{
use crate::{ use crate::{
ad7172, ad7172,
pid, pid,
config::PwmLimits,
steinhart_hart as sh, steinhart_hart as sh,
command_parser::CenterPoint, command_parser::CenterPoint,
}; };
@ -32,6 +33,7 @@ pub struct ChannelState {
pub center: CenterPoint, pub center: CenterPoint,
pub dac_value: ElectricPotential, pub dac_value: ElectricPotential,
pub i_set: ElectricCurrent, pub i_set: ElectricCurrent,
pub pwm_limits: PwmLimits,
pub pid_engaged: bool, pub pid_engaged: bool,
pub pid: pid::Controller, pub pid: pid::Controller,
pub sh: sh::Parameters, pub sh: sh::Parameters,
@ -48,6 +50,11 @@ impl ChannelState {
center: CenterPoint::Vref, center: CenterPoint::Vref,
dac_value: ElectricPotential::new::<volt>(0.0), dac_value: ElectricPotential::new::<volt>(0.0),
i_set: ElectricCurrent::new::<ampere>(0.0), i_set: ElectricCurrent::new::<ampere>(0.0),
pwm_limits: PwmLimits {
max_v: 0.0,
max_i_pos: 0.0,
max_i_neg: 0.0,
},
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(),

View File

@ -353,46 +353,16 @@ impl Channels {
} }
} }
fn get_pwm(&self, channel: usize, pin: PwmPin) -> f64 { pub fn get_max_v(&mut self, channel: usize) -> ElectricPotential {
fn get<P: hal::PwmPin<Duty=u16>>(pin: &P) -> f64 { ElectricPotential::new::<volt>(self.channel_state(channel).pwm_limits.max_v)
let duty = pin.get_duty();
let max = pin.get_max_duty();
duty as f64 / (max as f64)
}
match (channel, pin) {
(_, PwmPin::ISet) =>
panic!("i_set is no pwm pin"),
(0, PwmPin::MaxIPos) =>
get(&self.pwm.max_i_pos0),
(0, PwmPin::MaxINeg) =>
get(&self.pwm.max_i_neg0),
(0, PwmPin::MaxV) =>
get(&self.pwm.max_v0),
(1, PwmPin::MaxIPos) =>
get(&self.pwm.max_i_pos1),
(1, PwmPin::MaxINeg) =>
get(&self.pwm.max_i_neg1),
(1, PwmPin::MaxV) =>
get(&self.pwm.max_v1),
_ =>
unreachable!(),
}
} }
pub fn get_max_v(&mut self, channel: usize) -> (ElectricPotential, ElectricPotential) { pub fn get_max_i_pos(&mut self, channel: usize) -> ElectricCurrent {
let max = 4.0 * ElectricPotential::new::<volt>(3.3); ElectricCurrent::new::<ampere>(self.channel_state(channel).pwm_limits.max_i_pos)
let duty = self.get_pwm(channel, PwmPin::MaxV);
(duty * max, MAX_TEC_V)
} }
pub fn get_max_i_pos(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) { pub fn get_max_i_neg(&mut self, channel: usize) -> ElectricCurrent {
let duty = self.get_pwm(channel, PwmPin::MaxIPos); ElectricCurrent::new::<ampere>(self.channel_state(channel).pwm_limits.max_i_neg)
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
}
pub fn get_max_i_neg(&mut self, channel: usize) -> (ElectricCurrent, ElectricCurrent) {
let duty = self.get_pwm(channel, PwmPin::MaxINeg);
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, MAX_TEC_I)
} }
// Get current passing through TEC // Get current passing through TEC
@ -434,22 +404,28 @@ impl Channels {
pub fn set_max_v(&mut self, channel: usize, max_v: ElectricPotential) -> (ElectricPotential, ElectricPotential) { pub fn set_max_v(&mut self, channel: usize, max_v: ElectricPotential) -> (ElectricPotential, ElectricPotential) {
let max = 4.0 * ElectricPotential::new::<volt>(3.3); let max = 4.0 * ElectricPotential::new::<volt>(3.3);
let duty = (max_v.min(MAX_TEC_V).max(ElectricPotential::zero()) / max).get::<ratio>(); let max_v = max_v.min(MAX_TEC_V).max(ElectricPotential::zero());
let duty = (max_v / max).get::<ratio>();
let duty = self.set_pwm(channel, PwmPin::MaxV, duty); let duty = self.set_pwm(channel, PwmPin::MaxV, duty);
self.channel_state(channel).pwm_limits.max_v = max_v.get::<volt>();
(duty * max, max) (duty * max, max)
} }
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 max_i_pos = max_i_pos.min(MAX_TEC_I).max(ElectricCurrent::zero());
let duty = (max_i_pos / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty); let duty = self.set_pwm(channel, PwmPin::MaxIPos, duty);
self.channel_state(channel).pwm_limits.max_i_pos = max_i_pos.get::<ampere>();
(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 max_i_neg = max_i_neg.min(MAX_TEC_I).max(ElectricCurrent::zero());
let duty = (max_i_neg / MAX_TEC_I_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty); let duty = self.set_pwm(channel, PwmPin::MaxINeg, duty);
self.channel_state(channel).pwm_limits.max_i_neg = max_i_neg.get::<ampere>();
(duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, max) (duty * MAX_TEC_I_DUTY_TO_CURRENT_RATE, max)
} }
@ -508,10 +484,10 @@ impl Channels {
PwmSummary { PwmSummary {
channel, channel,
center: CenterPointJson(self.channel_state(channel).center.clone()), center: CenterPointJson(self.channel_state(channel).center.clone()),
i_set: (self.get_i(channel), MAX_TEC_I).into(), i_set: self.get_i(channel),
max_v: self.get_max_v(channel).into(), max_v: self.get_max_v(channel),
max_i_pos: self.get_max_i_pos(channel).into(), max_i_pos: self.get_max_i_pos(channel),
max_i_neg: self.get_max_i_neg(channel).into(), max_i_neg: self.get_max_i_neg(channel),
} }
} }
@ -593,26 +569,14 @@ impl Serialize for CenterPointJson {
} }
} }
#[derive(Serialize)]
pub struct PwmSummaryField<T: Serialize> {
value: T,
max: T,
}
impl<T: Serialize> From<(T, T)> for PwmSummaryField<T> {
fn from((value, max): (T, T)) -> Self {
PwmSummaryField { value, max }
}
}
#[derive(Serialize)] #[derive(Serialize)]
pub struct PwmSummary { pub struct PwmSummary {
channel: usize, channel: usize,
center: CenterPointJson, center: CenterPointJson,
i_set: PwmSummaryField<ElectricCurrent>, i_set: ElectricCurrent,
max_v: PwmSummaryField<ElectricPotential>, max_v: ElectricPotential,
max_i_pos: PwmSummaryField<ElectricCurrent>, max_i_pos: ElectricCurrent,
max_i_neg: PwmSummaryField<ElectricCurrent>, max_i_neg: ElectricCurrent,
} }
#[derive(Serialize)] #[derive(Serialize)]

View File

@ -72,17 +72,17 @@ impl ChannelConfig {
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct PwmLimits { pub struct PwmLimits {
max_v: f64, pub max_v: f64,
max_i_pos: f64, pub max_i_pos: f64,
max_i_neg: f64, pub max_i_neg: f64,
} }
impl PwmLimits { impl PwmLimits {
pub fn new(channels: &mut Channels, channel: usize) -> Self { pub fn new(channels: &mut Channels, channel: usize) -> Self {
let (max_v, _) = channels.get_max_v(channel); let max_v = channels.get_max_v(channel);
let (max_i_pos, _) = channels.get_max_i_pos(channel); let max_i_pos = channels.get_max_i_pos(channel);
let (max_i_neg, _) = channels.get_max_i_neg(channel); let max_i_neg = channels.get_max_i_neg(channel);
PwmLimits { PwmLimits {
max_v: max_v.get::<volt>(), max_v: max_v.get::<volt>(),
max_i_pos: max_i_pos.get::<ampere>(), max_i_pos: max_i_pos.get::<ampere>(),