1
0
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:
linuswck 2024-01-18 15:14:24 +08:00
parent ff3d9b790a
commit 2f7ca2a706
4 changed files with 110 additions and 35 deletions

View File

@ -7,6 +7,7 @@ use stm32f4xx_hal::pac::{CorePeripherals, Peripherals};
mod device;
mod laser_diode;
mod thermostat;
mod pid;
use device::{boot::bootup, log_setup, sys_timer};
use uom::fmt::DisplayStyle::Abbreviation;

View File

@ -2,7 +2,7 @@
use miniconf::Miniconf;
use miniconf::serde::{Serialize, Deserialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Miniconf)]
pub struct Parameters {
/// Gain coefficient for proportional term
pub kp: f32,
@ -28,8 +28,9 @@ impl Default for Parameters {
}
}
#[derive(Clone)]
#[derive(Clone, Debug, PartialEq, Miniconf)]
pub struct Controller {
#[miniconf(defer)]
pub parameters: Parameters,
pub target : f64,
u1 : f64,
@ -88,6 +89,7 @@ impl Controller {
#[derive(Clone, Debug, Miniconf)]
pub struct Summary {
#[miniconf(defer)]
parameters: Parameters,
target: f64,
}

View File

@ -23,8 +23,8 @@ const VREF_SENS: f64 = 3.3 / 2.0;
pub struct PidState {
pub adc_data: Option<u32>,
pub adc_calibration: ad7172::ChannelCalibration,
pub sample_ts: Instant,
pub sampling_interval: Duration,
pub update_ts: Time,
pub update_interval: Time,
/// i_set 0A center point
pub center_point: ElectricPotential,
pub dac_volt: ElectricPotential,
@ -45,9 +45,9 @@ impl Default for PidState {
PidState {
adc_data: None,
adc_calibration: ad7172::ChannelCalibration::default(),
sample_ts: Instant::from_secs(0),
update_ts: Time::new::<millisecond>(0.0),
// default: 10 Hz
sampling_interval: Duration::from_millis(100),
update_interval: Time::new::<millisecond>(100.0),
center_point: ElectricPotential::new::<volt>(1.5),
dac_volt: ElectricPotential::new::<volt>(0.0),
pid_engaged: false,
@ -69,8 +69,8 @@ impl PidState {
} else {
Some(adc_data)
};
self.sampling_interval = now - self.sample_ts;
self.sample_ts = now;
self.update_interval = Time::new::<millisecond>(now.millis() as f64) - self.update_ts;
self.update_ts = Time::new::<millisecond>(now.millis() as f64);
}
/// Update PID state on ADC input, calculate new DAC output
@ -81,12 +81,12 @@ impl PidState {
Some(pid_output)
}
pub fn get_sample_ts(&self) -> Time {
Time::new::<millisecond>(self.sample_ts.total_millis() as f64)
pub fn get_update_ts(&self) -> Time {
self.update_ts
}
pub fn get_sampling_interval(&self) -> Time {
Time::new::<millisecond>(self.sampling_interval.total_millis() as f64)
pub fn get_update_interval(&self) -> Time {
self.update_interval
}
pub fn get_adc(&self) -> Option<ElectricPotential> {

View File

@ -1,6 +1,6 @@
use core::marker::PhantomData;
use smoltcp::time::Instant;
use crate::sys_timer;
use crate::{sys_timer, pid::pid};
use crate::thermostat::ad5680;
use crate::thermostat::max1968::{MAX1968, AdcReadTarget, PwmPinsEnum};
use crate::thermostat::ad7172;
@ -11,7 +11,7 @@ use uom::si::{
electric_current::ampere,
electric_potential::volt,
electrical_resistance::ohm,
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time},
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance, Time, ThermodynamicTemperature},
ratio::ratio,
};
use miniconf::Miniconf;
@ -23,7 +23,7 @@ pub const R_SENSE: ElectricalResistance = ElectricalResistance {
};
#[derive(Clone, Debug, Miniconf)]
pub struct Settings {
pub struct TecSettings {
pub center_pt: ElectricPotential,
pub max_v_set: ElectricPotential,
pub max_i_pos_set: ElectricCurrent,
@ -31,7 +31,7 @@ pub struct Settings {
pub i_set: ElectricCurrent,
}
impl Settings{
impl TecSettings{
// FixMe: Rev0_2 is 3.3V max while Rev0_3 is 3.0V max
pub const DAC_OUT_V_MAX: ElectricPotential = ElectricPotential {
dimension: PhantomData,
@ -47,6 +47,11 @@ impl Settings{
// Kirdy Design Specs:
// MaxV = 5.0V
// 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 {
dimension: PhantomData,
units: PhantomData,
@ -57,7 +62,7 @@ impl Settings{
units: PhantomData,
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 {
dimension: PhantomData,
units: PhantomData,
@ -74,11 +79,11 @@ impl Settings{
value: 1.0,
};
// .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_NEG_DUTY_MAX: f64 = Settings::MAX_I_NEG_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 = 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 {
Self {
center_pt: ElectricPotential::new::<volt>(1.5),
@ -93,7 +98,7 @@ impl Default for Settings {
pub struct Thermostat {
max1968: MAX1968,
ad7172: ad7172::AdcPhy,
pub tec_setting: Settings,
pub tec_setting: TecSettings,
pid_ctrl_ch0: PidState,
}
@ -102,7 +107,7 @@ impl Thermostat{
Thermostat{
max1968: max1968,
ad7172: ad7172,
tec_setting: Settings::default(),
tec_setting: TecSettings::default(),
pid_ctrl_ch0: PidState::default(),
}
}
@ -115,7 +120,7 @@ impl Thermostat{
fn tec_setup(&mut self) {
self.power_down();
self.tec_setting = Settings::default();
self.tec_setting = TecSettings::default();
self.set_i(self.tec_setting.i_set);
@ -169,29 +174,29 @@ impl Thermostat{
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 = 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
}
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 = self.max1968.set_pwm(PwmPinsEnum::MaxV, duty, Settings::MAX_V_DUTY_MAX);
self.tec_setting.max_v_set = duty * Settings::MAX_V_DUTY_TO_CURRENT_RATE;
let duty = (max_v / TecSettings::MAX_V_DUTY_TO_CURRENT_RATE).get::<ratio>();
let duty = self.max1968.set_pwm(PwmPinsEnum::MaxV, duty, TecSettings::MAX_V_DUTY_MAX);
self.tec_setting.max_v_set = duty * TecSettings::MAX_V_DUTY_TO_CURRENT_RATE;
self.tec_setting.max_v_set
}
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 = self.max1968.set_pwm(PwmPinsEnum::MaxPosI, duty, Settings::MAX_I_POS_DUTY_MAX);
self.tec_setting.max_i_pos_set = duty * Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
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, TecSettings::MAX_I_POS_DUTY_MAX);
self.tec_setting.max_i_pos_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_setting.max_i_pos_set
}
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 = self.max1968.set_pwm(PwmPinsEnum::MaxNegI, duty, Settings::MAX_I_NEG_DUTY_MAX);
self.tec_setting.max_i_neg_set = duty * Settings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
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, TecSettings::MAX_I_NEG_DUTY_MAX);
self.tec_setting.max_i_neg_set = duty * TecSettings::MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE;
self.tec_setting.max_i_neg_set
}
@ -211,7 +216,7 @@ impl Thermostat{
pub fn get_tec_v(&mut self) -> ElectricPotential {
// Fixme: Rev0_2 has Analog Input Polarity Reversed
// 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.
@ -250,7 +255,7 @@ impl Thermostat{
best_error = error;
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);
}
prev_value = value;
@ -266,4 +271,71 @@ impl Thermostat{
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,
}