Compare commits

...

5 Commits

Author SHA1 Message Date
2e9c1d1467 Average out reads instead of waiting for stability
Reading until the error falls into a certain tolerance might cause
bootlooping as the ADC values just might never settle. Average it out
instead.
2024-02-21 14:16:57 +08:00
2591bff0e2 Give time for dac value to settle when calibrating 2024-02-21 14:16:57 +08:00
d470653385 Make set_i use get_center again
This brings back the ability to override the center point for the
current setpoint.
2024-02-21 14:16:55 +08:00
70d20f3deb Add calibration VREF option and set as default 2024-02-21 14:16:36 +08:00
5aaa1edad7 calibration: read vref direct 2024-02-21 14:16:36 +08:00
5 changed files with 38 additions and 30 deletions

View File

@ -107,6 +107,7 @@ formatted as line-delimited JSON.
| `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 |
| `center <0/1> vref_calib` | Set the MAX1968 0A-centerpoint to a stable calibrated VREF |
| `pid` | Show PID configuration | | `pid` | Show PID configuration |
| `pid <0/1> target <deg_celsius>` | Set the PID controller target temperature | | `pid <0/1> target <deg_celsius>` | Set the PID controller target temperature |
| `pid <0/1> kp <value>` | Set proportional gain | | `pid <0/1> kp <value>` | Set proportional gain |

View File

@ -20,8 +20,8 @@ pub struct Channel<C: ChannelPins> {
pub state: ChannelState, pub state: ChannelState,
/// for `i_set` /// for `i_set`
pub dac: ad5680::Dac<C::DacSpi, C::DacSync>, pub dac: ad5680::Dac<C::DacSpi, C::DacSync>,
/// Measured vref of MAX driver chip /// Calibrated vref of MAX driver chip
pub vref_meas: ElectricPotential, pub vref_calib: ElectricPotential,
pub shdn: C::Shdn, pub shdn: C::Shdn,
pub vref_pin: C::VRefPin, pub vref_pin: C::VRefPin,
pub itec_pin: C::ItecPin, pub itec_pin: C::ItecPin,
@ -36,11 +36,11 @@ impl<C: ChannelPins> Channel<C> {
let mut dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync); let mut dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync);
let _ = dac.set(0); let _ = dac.set(0);
// sensible dummy preset taken from datasheet. calibrate_dac_value() should be used to override this value. // sensible dummy preset taken from datasheet. calibrate_dac_value() should be used to override this value.
let vref_meas = ElectricPotential::new::<volt>(1.5); let vref_calib = ElectricPotential::new::<volt>(1.5);
Channel { Channel {
state, state,
dac, vref_meas, dac, vref_calib,
shdn: pins.shdn, shdn: pins.shdn,
vref_pin: pins.vref_pin, vref_pin: pins.vref_pin,
itec_pin: pins.itec_pin, itec_pin: pins.itec_pin,

View File

@ -45,7 +45,7 @@ impl ChannelState {
adc_time: Instant::from_secs(0), adc_time: Instant::from_secs(0),
// default: 10 Hz // default: 10 Hz
adc_interval: Duration::from_millis(100), adc_interval: Duration::from_millis(100),
center: CenterPoint::Vref, center: CenterPoint::VrefCalib,
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),
pid_engaged: false, pid_engaged: false,

View File

@ -21,6 +21,7 @@ use crate::{
pins, pins,
steinhart_hart, steinhart_hart,
hw_rev, hw_rev,
timer,
}; };
pub const CHANNELS: usize = 2; pub const CHANNELS: usize = 2;
@ -101,6 +102,13 @@ impl<'a> Channels<'a> {
match self.channel_state(channel).center { match self.channel_state(channel).center {
CenterPoint::Vref => CenterPoint::Vref =>
self.read_vref(channel), self.read_vref(channel),
CenterPoint::VrefCalib => {
match channel {
0 => self.channel0.vref_calib,
1 => self.channel1.vref_calib,
_ => unreachable!(),
}
},
CenterPoint::Override(center_point) => CenterPoint::Override(center_point) =>
ElectricPotential::new::<volt>(center_point.into()), ElectricPotential::new::<volt>(center_point.into()),
} }
@ -130,12 +138,7 @@ impl<'a> Channels<'a> {
} }
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 vref_meas = match channel.into() { let center_point = self.get_center(channel);
0 => self.channel0.vref_meas,
1 => self.channel1.vref_meas,
_ => unreachable!(),
};
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 = i_set * 10.0 * r_sense + center_point;
let voltage = self.set_dac(channel, voltage); let voltage = self.set_dac(channel, voltage);
@ -166,17 +169,6 @@ impl<'a> Channels<'a> {
} }
} }
pub fn read_dac_feedback_until_stable(&mut self, channel: usize, tolerance: ElectricPotential) -> ElectricPotential {
let mut prev = self.read_dac_feedback(channel);
loop {
let current = self.read_dac_feedback(channel);
if (current - prev).abs() < tolerance {
return current;
}
prev = current;
}
}
pub fn read_itec(&mut self, channel: usize) -> ElectricPotential { pub fn read_itec(&mut self, channel: usize) -> ElectricPotential {
match channel { match channel {
0 => { 0 => {
@ -262,15 +254,19 @@ impl<'a> Channels<'a> {
/// thermostat. /// thermostat.
pub fn calibrate_dac_value(&mut self, channel: usize) { pub fn calibrate_dac_value(&mut self, channel: usize) {
let samples = 50; let samples = 50;
let mut target_voltage = ElectricPotential::new::<volt>(0.0); let target_voltage = {
for _ in 0..samples { let mut target_voltage = ElectricPotential::new::<volt>(0.0);
target_voltage = target_voltage + self.get_center(channel); for _ in 0..samples {
} target_voltage += self.read_vref(channel);
target_voltage = target_voltage / samples as f64; }
target_voltage /= samples as f64;
target_voltage
};
let mut start_value = 1; let mut start_value = 1;
let mut best_error = ElectricPotential::new::<volt>(100.0); let mut best_error = ElectricPotential::new::<volt>(100.0);
for step in (0..18).rev() { for step in (0..18).rev() {
timer::sleep(5);
let mut prev_value = start_value; let mut prev_value = start_value;
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) { for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
match channel { match channel {
@ -283,7 +279,14 @@ impl<'a> Channels<'a> {
_ => unreachable!(), _ => unreachable!(),
} }
let dac_feedback = self.read_dac_feedback_until_stable(channel, ElectricPotential::new::<volt>(0.001)); let dac_feedback = {
let mut dac_feedback = ElectricPotential::new::<volt>(0.0);
for _ in 0..samples {
dac_feedback += self.read_dac_feedback(channel);
}
dac_feedback /= samples as f64;
dac_feedback
};
let error = target_voltage - dac_feedback; let error = target_voltage - dac_feedback;
if error < ElectricPotential::new::<volt>(0.0) { if error < ElectricPotential::new::<volt>(0.0) {
break; break;
@ -293,8 +296,8 @@ impl<'a> Channels<'a> {
let vref = (value as f64 / ad5680::MAX_VALUE as f64) * ElectricPotential::new::<volt>(DAC_OUT_V_MAX); let vref = (value as f64 / ad5680::MAX_VALUE as f64) * ElectricPotential::new::<volt>(DAC_OUT_V_MAX);
match channel { match channel {
0 => self.channel0.vref_meas = vref, 0 => self.channel0.vref_calib = vref,
1 => self.channel1.vref_meas = vref, 1 => self.channel1.vref_calib = vref,
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -560,6 +563,8 @@ impl Serialize for CenterPointJson {
match self.0 { match self.0 {
CenterPoint::Vref => CenterPoint::Vref =>
serializer.serialize_str("vref"), serializer.serialize_str("vref"),
CenterPoint::VrefCalib =>
serializer.serialize_str("vref_calib"),
CenterPoint::Override(vref) => CenterPoint::Override(vref) =>
serializer.serialize_f32(vref), serializer.serialize_f32(vref),
} }

View File

@ -133,6 +133,7 @@ pub enum PwmPin {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CenterPoint { pub enum CenterPoint {
Vref, Vref,
VrefCalib,
Override(f32), Override(f32),
} }
@ -357,6 +358,7 @@ fn center_point(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = whitespace(input)?; let (input, _) = whitespace(input)?;
let (input, center) = alt(( let (input, center) = alt((
value(Ok(CenterPoint::Vref), tag("vref")), value(Ok(CenterPoint::Vref), tag("vref")),
value(Ok(CenterPoint::VrefCalib), tag("vref_calib")),
|input| { |input| {
let (input, value) = float(input)?; let (input, value) = float(input)?;
Ok((input, value.map(|value| CenterPoint::Override(value as f32)))) Ok((input, value.map(|value| CenterPoint::Override(value as f32))))