forked from M-Labs/kirdy
Thermostat: Add fns to report status & settings
- Report all system status, PID Settings, NTC parameter, TEC Settings
This commit is contained in:
parent
ff3d9b790a
commit
2f7ca2a706
|
@ -7,6 +7,7 @@ use stm32f4xx_hal::pac::{CorePeripherals, Peripherals};
|
||||||
mod device;
|
mod device;
|
||||||
mod laser_diode;
|
mod laser_diode;
|
||||||
mod thermostat;
|
mod thermostat;
|
||||||
|
mod pid;
|
||||||
|
|
||||||
use device::{boot::bootup, log_setup, sys_timer};
|
use device::{boot::bootup, log_setup, sys_timer};
|
||||||
use uom::fmt::DisplayStyle::Abbreviation;
|
use uom::fmt::DisplayStyle::Abbreviation;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
use miniconf::Miniconf;
|
use miniconf::Miniconf;
|
||||||
use miniconf::serde::{Serialize, Deserialize};
|
use miniconf::serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Miniconf)]
|
||||||
pub struct Parameters {
|
pub struct Parameters {
|
||||||
/// Gain coefficient for proportional term
|
/// Gain coefficient for proportional term
|
||||||
pub kp: f32,
|
pub kp: f32,
|
||||||
|
@ -28,8 +28,9 @@ impl Default for Parameters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug, PartialEq, Miniconf)]
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
|
#[miniconf(defer)]
|
||||||
pub parameters: Parameters,
|
pub parameters: Parameters,
|
||||||
pub target : f64,
|
pub target : f64,
|
||||||
u1 : f64,
|
u1 : f64,
|
||||||
|
@ -88,6 +89,7 @@ impl Controller {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Miniconf)]
|
#[derive(Clone, Debug, Miniconf)]
|
||||||
pub struct Summary {
|
pub struct Summary {
|
||||||
|
#[miniconf(defer)]
|
||||||
parameters: Parameters,
|
parameters: Parameters,
|
||||||
target: f64,
|
target: f64,
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ const VREF_SENS: f64 = 3.3 / 2.0;
|
||||||
pub struct PidState {
|
pub struct PidState {
|
||||||
pub adc_data: Option<u32>,
|
pub adc_data: Option<u32>,
|
||||||
pub adc_calibration: ad7172::ChannelCalibration,
|
pub adc_calibration: ad7172::ChannelCalibration,
|
||||||
pub sample_ts: Instant,
|
pub update_ts: Time,
|
||||||
pub sampling_interval: Duration,
|
pub update_interval: Time,
|
||||||
/// i_set 0A center point
|
/// i_set 0A center point
|
||||||
pub center_point: ElectricPotential,
|
pub center_point: ElectricPotential,
|
||||||
pub dac_volt: ElectricPotential,
|
pub dac_volt: ElectricPotential,
|
||||||
|
@ -45,9 +45,9 @@ impl Default for PidState {
|
||||||
PidState {
|
PidState {
|
||||||
adc_data: None,
|
adc_data: None,
|
||||||
adc_calibration: ad7172::ChannelCalibration::default(),
|
adc_calibration: ad7172::ChannelCalibration::default(),
|
||||||
sample_ts: Instant::from_secs(0),
|
update_ts: Time::new::<millisecond>(0.0),
|
||||||
// default: 10 Hz
|
// default: 10 Hz
|
||||||
sampling_interval: Duration::from_millis(100),
|
update_interval: Time::new::<millisecond>(100.0),
|
||||||
center_point: ElectricPotential::new::<volt>(1.5),
|
center_point: ElectricPotential::new::<volt>(1.5),
|
||||||
dac_volt: ElectricPotential::new::<volt>(0.0),
|
dac_volt: ElectricPotential::new::<volt>(0.0),
|
||||||
pid_engaged: false,
|
pid_engaged: false,
|
||||||
|
@ -69,8 +69,8 @@ impl PidState {
|
||||||
} else {
|
} else {
|
||||||
Some(adc_data)
|
Some(adc_data)
|
||||||
};
|
};
|
||||||
self.sampling_interval = now - self.sample_ts;
|
self.update_interval = Time::new::<millisecond>(now.millis() as f64) - self.update_ts;
|
||||||
self.sample_ts = now;
|
self.update_ts = Time::new::<millisecond>(now.millis() as f64);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update PID state on ADC input, calculate new DAC output
|
/// Update PID state on ADC input, calculate new DAC output
|
||||||
|
@ -81,12 +81,12 @@ impl PidState {
|
||||||
Some(pid_output)
|
Some(pid_output)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sample_ts(&self) -> Time {
|
pub fn get_update_ts(&self) -> Time {
|
||||||
Time::new::<millisecond>(self.sample_ts.total_millis() as f64)
|
self.update_ts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sampling_interval(&self) -> Time {
|
pub fn get_update_interval(&self) -> Time {
|
||||||
Time::new::<millisecond>(self.sampling_interval.total_millis() as f64)
|
self.update_interval
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_adc(&self) -> Option<ElectricPotential> {
|
pub fn get_adc(&self) -> Option<ElectricPotential> {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use crate::sys_timer;
|
use crate::{sys_timer, pid::pid};
|
||||||
use crate::thermostat::ad5680;
|
use crate::thermostat::ad5680;
|
||||||
use crate::thermostat::max1968::{MAX1968, AdcReadTarget, PwmPinsEnum};
|
use crate::thermostat::max1968::{MAX1968, AdcReadTarget, PwmPinsEnum};
|
||||||
use crate::thermostat::ad7172;
|
use crate::thermostat::ad7172;
|
||||||
|
@ -11,7 +11,7 @@ use uom::si::{
|
||||||
electric_current::ampere,
|
electric_current::ampere,
|
||||||
electric_potential::volt,
|
electric_potential::volt,
|
||||||
electrical_resistance::ohm,
|
electrical_resistance::ohm,
|
||||||
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time},
|
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time, ThermodynamicTemperature},
|
||||||
ratio::ratio,
|
ratio::ratio,
|
||||||
};
|
};
|
||||||
use miniconf::Miniconf;
|
use miniconf::Miniconf;
|
||||||
|
@ -23,7 +23,7 @@ pub const R_SENSE: ElectricalResistance = ElectricalResistance {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Miniconf)]
|
#[derive(Clone, Debug, Miniconf)]
|
||||||
pub struct Settings {
|
pub struct TecSettings {
|
||||||
pub center_pt: ElectricPotential,
|
pub center_pt: ElectricPotential,
|
||||||
pub max_v_set: ElectricPotential,
|
pub max_v_set: ElectricPotential,
|
||||||
pub max_i_pos_set: ElectricCurrent,
|
pub max_i_pos_set: ElectricCurrent,
|
||||||
|
@ -31,7 +31,7 @@ pub struct Settings {
|
||||||
pub i_set: ElectricCurrent,
|
pub i_set: ElectricCurrent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings{
|
impl TecSettings{
|
||||||
// FixMe: Rev0_2 is 3.3V max while Rev0_3 is 3.0V max
|
// FixMe: Rev0_2 is 3.3V max while Rev0_3 is 3.0V max
|
||||||
pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
|
pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
|
||||||
dimension: PhantomData,
|
dimension: PhantomData,
|
||||||
|
@ -47,6 +47,11 @@ impl Settings{
|
||||||
// Kirdy Design Specs:
|
// Kirdy Design Specs:
|
||||||
// MaxV = 5.0V
|
// MaxV = 5.0V
|
||||||
// MAX Current = +- 1.0A
|
// MAX Current = +- 1.0A
|
||||||
|
const MAX_I_SET : ElectricCurrent = ElectricCurrent {
|
||||||
|
dimension: PhantomData,
|
||||||
|
units: PhantomData,
|
||||||
|
value: 1.0,
|
||||||
|
};
|
||||||
const MAX_V_DUTY_TO_CURRENT_RATE: ElectricPotential = ElectricPotential {
|
const MAX_V_DUTY_TO_CURRENT_RATE: ElectricPotential = ElectricPotential {
|
||||||
dimension: PhantomData,
|
dimension: PhantomData,
|
||||||
units: PhantomData,
|
units: PhantomData,
|
||||||
|
@ -57,7 +62,7 @@ impl Settings{
|
||||||
units: PhantomData,
|
units: PhantomData,
|
||||||
value: 5.0,
|
value: 5.0,
|
||||||
};
|
};
|
||||||
const MAX_V_DUTY_MAX: f64 = Settings::MAX_V_MAX.value / Settings::MAX_V_DUTY_TO_CURRENT_RATE.value;
|
const MAX_V_DUTY_MAX: f64 = TecSettings::MAX_V_MAX.value / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE.value;
|
||||||
const MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE: ElectricCurrent = ElectricCurrent {
|
const MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE: ElectricCurrent = ElectricCurrent {
|
||||||
dimension: PhantomData,
|
dimension: PhantomData,
|
||||||
units: PhantomData,
|
units: PhantomData,
|
||||||
|
@ -74,11 +79,11 @@ impl Settings{
|
||||||
value: 1.0,
|
value: 1.0,
|
||||||
};
|
};
|
||||||
// .get::<ratio>() is not implemented for const
|
// .get::<ratio>() is not implemented for const
|
||||||
const MAX_I_POS_DUTY_MAX: f64 = Settings::MAX_I_POS_CURRENT.value / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value;
|
const MAX_I_POS_DUTY_MAX: f64 = TecSettings::MAX_I_POS_CURRENT.value / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value;
|
||||||
const MAX_I_NEG_DUTY_MAX: f64 = Settings::MAX_I_NEG_CURRENT.value / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value;
|
const MAX_I_NEG_DUTY_MAX: f64 = TecSettings::MAX_I_NEG_CURRENT.value / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for TecSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
center_pt: ElectricPotential::new::<volt>(1.5),
|
center_pt: ElectricPotential::new::<volt>(1.5),
|
||||||
|
@ -93,7 +98,7 @@ impl Default for Settings {
|
||||||
pub struct Thermostat {
|
pub struct Thermostat {
|
||||||
max1968: MAX1968,
|
max1968: MAX1968,
|
||||||
ad7172: ad7172::AdcPhy,
|
ad7172: ad7172::AdcPhy,
|
||||||
pub tec_setting: Settings,
|
pub tec_setting: TecSettings,
|
||||||
pid_ctrl_ch0: PidState,
|
pid_ctrl_ch0: PidState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +107,7 @@ impl Thermostat{
|
||||||
Thermostat{
|
Thermostat{
|
||||||
max1968: max1968,
|
max1968: max1968,
|
||||||
ad7172: ad7172,
|
ad7172: ad7172,
|
||||||
tec_setting: Settings::default(),
|
tec_setting: TecSettings::default(),
|
||||||
pid_ctrl_ch0: PidState::default(),
|
pid_ctrl_ch0: PidState::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +120,7 @@ impl Thermostat{
|
||||||
fn tec_setup(&mut self) {
|
fn tec_setup(&mut self) {
|
||||||
self.power_down();
|
self.power_down();
|
||||||
|
|
||||||
self.tec_setting = Settings::default();
|
self.tec_setting = TecSettings::default();
|
||||||
|
|
||||||
self.set_i(self.tec_setting.i_set);
|
self.set_i(self.tec_setting.i_set);
|
||||||
|
|
||||||
|
@ -169,29 +174,29 @@ impl Thermostat{
|
||||||
|
|
||||||
pub fn set_i(&mut self, i_tec: ElectricCurrent) -> ElectricCurrent {
|
pub fn set_i(&mut self, i_tec: ElectricCurrent) -> ElectricCurrent {
|
||||||
let voltage = i_tec * 10.0 * R_SENSE + self.tec_setting.center_pt;
|
let voltage = i_tec * 10.0 * R_SENSE + self.tec_setting.center_pt;
|
||||||
let voltage = self.max1968.set_dac(voltage, Settings::DAC_OUT_V_MAX);
|
let voltage = self.max1968.set_dac(voltage, TecSettings::DAC_OUT_V_MAX);
|
||||||
self.tec_setting.i_set = (voltage - self.tec_setting.center_pt) / (10.0 * R_SENSE);
|
self.tec_setting.i_set = (voltage - self.tec_setting.center_pt) / (10.0 * R_SENSE);
|
||||||
self.tec_setting.i_set
|
self.tec_setting.i_set
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_v(&mut self, max_v: ElectricPotential) -> ElectricPotential {
|
pub fn set_max_v(&mut self, max_v: ElectricPotential) -> ElectricPotential {
|
||||||
let duty = (max_v / Settings::MAX_V_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
let duty = (max_v / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
||||||
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxV, duty, Settings::MAX_V_DUTY_MAX);
|
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxV, duty, TecSettings::MAX_V_DUTY_MAX);
|
||||||
self.tec_setting.max_v_set = duty * Settings::MAX_V_DUTY_TO_CURRENT_RATE;
|
self.tec_setting.max_v_set = duty * TecSettings::MAX_V_DUTY_TO_CURRENT_RATE;
|
||||||
self.tec_setting.max_v_set
|
self.tec_setting.max_v_set
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> ElectricCurrent {
|
pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> ElectricCurrent {
|
||||||
let duty = (max_i_pos / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
let duty = (max_i_pos / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
||||||
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxPosI, duty, Settings::MAX_I_POS_DUTY_MAX);
|
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxPosI, duty, TecSettings::MAX_I_POS_DUTY_MAX);
|
||||||
self.tec_setting.max_i_pos_set = duty * Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
|
self.tec_setting.max_i_pos_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
|
||||||
self.tec_setting.max_i_pos_set
|
self.tec_setting.max_i_pos_set
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> ElectricCurrent {
|
pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> ElectricCurrent {
|
||||||
let duty = (max_i_neg / Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
let duty = (max_i_neg / TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::<ratio>();
|
||||||
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxNegI, duty, Settings::MAX_I_NEG_DUTY_MAX);
|
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxNegI, duty, TecSettings::MAX_I_NEG_DUTY_MAX);
|
||||||
self.tec_setting.max_i_neg_set = duty * Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
|
self.tec_setting.max_i_neg_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
|
||||||
self.tec_setting.max_i_neg_set
|
self.tec_setting.max_i_neg_set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +216,7 @@ impl Thermostat{
|
||||||
pub fn get_tec_v(&mut self) -> ElectricPotential {
|
pub fn get_tec_v(&mut self) -> ElectricPotential {
|
||||||
// Fixme: Rev0_2 has Analog Input Polarity Reversed
|
// Fixme: Rev0_2 has Analog Input Polarity Reversed
|
||||||
// Remove the -ve sign for Rev0_3
|
// Remove the -ve sign for Rev0_3
|
||||||
-(self.max1968.adc_read(AdcReadTarget::VTec, 16) - Settings::TEC_VSEC_BIAS_V) * 4.0
|
-(self.max1968.adc_read(AdcReadTarget::VTec, 16) - TecSettings::TEC_VSEC_BIAS_V) * 4.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output.
|
/// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output.
|
||||||
|
@ -250,7 +255,7 @@ impl Thermostat{
|
||||||
best_error = error;
|
best_error = error;
|
||||||
start_value = prev_value;
|
start_value = prev_value;
|
||||||
|
|
||||||
let vref = (value as f64 / ad5680::MAX_VALUE as f64) * Settings::DAC_OUT_V_MAX;
|
let vref = (value as f64 / ad5680::MAX_VALUE as f64) * TecSettings::DAC_OUT_V_MAX;
|
||||||
self.set_center_pt(vref);
|
self.set_center_pt(vref);
|
||||||
}
|
}
|
||||||
prev_value = value;
|
prev_value = value;
|
||||||
|
@ -266,4 +271,71 @@ impl Thermostat{
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_status_report(&mut self) -> StatusReport {
|
||||||
|
StatusReport {
|
||||||
|
pid_update_ts: self.pid_ctrl_ch0.get_update_ts(),
|
||||||
|
pid_update_interval: self.pid_ctrl_ch0.get_update_interval(),
|
||||||
|
pid_engaged: self.pid_engaged(),
|
||||||
|
temperature: self.pid_ctrl_ch0.get_temperature(),
|
||||||
|
i_set: self.tec_setting.i_set,
|
||||||
|
tec_i: self.get_tec_i(),
|
||||||
|
tec_v: self.get_tec_v(),
|
||||||
|
tec_vref: self.get_vref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_pid_settings(&mut self) -> pid::Controller {
|
||||||
|
self.pid_ctrl_ch0.pid.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_steinhart_hart(&mut self) -> steinhart_hart::Parameters {
|
||||||
|
self.pid_ctrl_ch0.sh.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tec_settings(&mut self) -> TecSettingSummary {
|
||||||
|
TecSettingSummary {
|
||||||
|
center_point: self.tec_setting.center_pt,
|
||||||
|
i_set: TecSettingsSummaryField { value: self.tec_setting.i_set, max: TecSettings::MAX_I_SET },
|
||||||
|
max_v: TecSettingsSummaryField { value: self.tec_setting.max_v_set, max: TecSettings::MAX_V_MAX },
|
||||||
|
max_i_pos: TecSettingsSummaryField { value: self.tec_setting.max_i_pos_set, max: TecSettings::MAX_I_POS_CURRENT },
|
||||||
|
max_i_neg: TecSettingsSummaryField { value: self.tec_setting.max_i_neg_set, max: TecSettings::MAX_I_NEG_CURRENT },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Miniconf)]
|
||||||
|
pub struct StatusReport {
|
||||||
|
pid_update_ts: Time,
|
||||||
|
pid_update_interval: Time,
|
||||||
|
pid_engaged: bool,
|
||||||
|
temperature: Option<ThermodynamicTemperature>,
|
||||||
|
i_set: ElectricCurrent,
|
||||||
|
tec_i: ElectricCurrent,
|
||||||
|
tec_v: ElectricPotential,
|
||||||
|
tec_vref: ElectricPotential,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Miniconf)]
|
||||||
|
pub struct TecSettingsSummaryField<T> {
|
||||||
|
value: T,
|
||||||
|
max: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Miniconf)]
|
||||||
|
pub struct TecSettingSummary {
|
||||||
|
center_point: ElectricPotential,
|
||||||
|
#[miniconf(defer)]
|
||||||
|
i_set: TecSettingsSummaryField<ElectricCurrent>,
|
||||||
|
#[miniconf(defer)]
|
||||||
|
max_v: TecSettingsSummaryField<ElectricPotential>,
|
||||||
|
#[miniconf(defer)]
|
||||||
|
max_i_pos: TecSettingsSummaryField<ElectricCurrent>,
|
||||||
|
#[miniconf(defer)]
|
||||||
|
max_i_neg: TecSettingsSummaryField<ElectricCurrent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Miniconf)]
|
||||||
|
pub struct SteinhartHartSummary {
|
||||||
|
#[miniconf(defer)]
|
||||||
|
params: steinhart_hart::Parameters,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue