Port TEC DAC calibration procedure from Thermostat
- Needs clean up - To be evaluated and rethink the calibration procedure
This commit is contained in:
parent
d3f3608136
commit
23ee568ea7
@ -35,7 +35,7 @@ pub const R_SENSE: ElectricalResistance = ElectricalResistance {
|
||||
// Rev 0_2: DAC Chip connects 3V3 reference voltage and thus provide 0-3.3V output range
|
||||
// TODO: Rev 0_3: DAC Chip connects 3V3 reference voltage,
|
||||
// which is then passed through a resistor divider to provide 0-3V output range
|
||||
const DAC_OUT_V_MAX: f64 = 3.3;
|
||||
pub const DAC_OUT_V_MAX: f64 = 3.3;
|
||||
const TEC_VSEC_BIAS_V: ElectricPotential = ElectricPotential {
|
||||
dimension: PhantomData,
|
||||
units: PhantomData,
|
||||
@ -258,17 +258,15 @@ impl MAX1968 {
|
||||
}
|
||||
|
||||
pub fn set_i(&mut self, i_tec: ElectricCurrent) -> ElectricCurrent {
|
||||
let center_point = self.phy.center_pt;
|
||||
let r_sense = R_SENSE;
|
||||
let voltage = i_tec * 10.0 * r_sense + center_point;
|
||||
let voltage = i_tec * 10.0 * R_SENSE + self.phy.center_pt;
|
||||
let voltage = self.set_dac(voltage);
|
||||
let i_tec = (voltage - center_point) / (10.0 * r_sense);
|
||||
let i_tec = (voltage - self.phy.center_pt) / (10.0 * R_SENSE);
|
||||
i_tec
|
||||
}
|
||||
|
||||
// AN4073: ADC Reading Dispersion can be reduced through Averaging
|
||||
// Upon test, 16 Point Averaging = +-3 LSB Dispersion
|
||||
fn adc_read(&mut self, adc_read_target: AdcReadTarget, avg_pt: u16) -> ElectricPotential {
|
||||
pub fn adc_read(&mut self, adc_read_target: AdcReadTarget, avg_pt: u16) -> ElectricPotential {
|
||||
let mut sample: u32 = 0;
|
||||
sample = match adc_read_target {
|
||||
AdcReadTarget::VREF => {
|
||||
@ -317,7 +315,7 @@ impl MAX1968 {
|
||||
}
|
||||
|
||||
pub fn get_dac_vfb(&mut self) -> ElectricPotential {
|
||||
self.adc_read(AdcReadTarget:: DacVfb, 1)
|
||||
self.adc_read(AdcReadTarget:: DacVfb, 16)
|
||||
}
|
||||
|
||||
pub fn get_tec_i(&mut self) -> ElectricCurrent {
|
||||
|
@ -1,2 +1,3 @@
|
||||
pub mod ad5680;
|
||||
pub mod max1968;
|
||||
pub mod thermostat;
|
@ -1,5 +1,14 @@
|
||||
use crate::sys_timer;
|
||||
use crate::thermostat::ad5680;
|
||||
use crate::thermostat::MAX1968;
|
||||
use crate::thermostat::max1968::{MAX1968, AdcReadTarget, DAC_OUT_V_MAX};
|
||||
use log::info;
|
||||
use uom::si::{
|
||||
electric_current::ampere,
|
||||
electric_potential::{millivolt, volt},
|
||||
electrical_resistance::ohm,
|
||||
f64::{ElectricCurrent, ElectricPotential, ElectricalResistance},
|
||||
ratio::ratio,
|
||||
};
|
||||
|
||||
pub struct Thermostat {
|
||||
max1968: MAX1968,
|
||||
@ -15,4 +24,65 @@ impl Thermostat{
|
||||
pub fn setup(&mut self){
|
||||
self.max1968.setup();
|
||||
}
|
||||
|
||||
/// Calibrates the DAC output to match vref of the MAX driver to reduce zero-current offset of the MAX driver output.
|
||||
///
|
||||
/// The thermostat DAC applies a control voltage signal to the CTLI pin of MAX driver chip to control its output current.
|
||||
/// The CTLI input signal is centered around VREF of the MAX chip. Applying VREF to CTLI sets the output current to 0.
|
||||
///
|
||||
/// This calibration routine measures the VREF voltage and the DAC output with the STM32 ADC, and uses a breadth-first
|
||||
/// search to find the DAC setting that will produce a DAC output voltage closest to VREF. This DAC output voltage will
|
||||
/// be stored and used in subsequent i_set routines to bias the current control signal to the measured VREF, reducing
|
||||
/// the offset error of the current control signal.
|
||||
///
|
||||
/// The input offset of the STM32 ADC is eliminated by using the same ADC for the measurements, and by only using the
|
||||
/// difference in VREF and DAC output for the calibration.
|
||||
///
|
||||
/// This routine should be called only once after boot, repeated reading of the vref signal and changing of the stored
|
||||
/// VREF measurement can introduce significant noise at the current output, degrading the stabilily performance of the
|
||||
/// thermostat.
|
||||
pub fn calibrate_dac_value(&mut self) {
|
||||
let samples = 50;
|
||||
let mut target_voltage = ElectricPotential::new::<volt>(0.0);
|
||||
for _ in 0..samples {
|
||||
target_voltage = target_voltage + self.max1968.adc_read(AdcReadTarget::VREF, 1);
|
||||
}
|
||||
target_voltage = target_voltage / samples as f64;
|
||||
let mut start_value = 1;
|
||||
let mut best_error = ElectricPotential::new::<volt>(100.0);
|
||||
let before_cal = self.max1968.phy.center_pt;
|
||||
for step in (0..18).rev() {
|
||||
info!("Step: {} Calibrating", step);
|
||||
let mut prev_value = start_value;
|
||||
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
|
||||
//info!("Calibrating");
|
||||
self.max1968.phy.dac.set(value).unwrap();
|
||||
sys_timer::sleep(5);
|
||||
|
||||
let dac_feedback = self.max1968.adc_read(AdcReadTarget::DacVfb, 64);
|
||||
let error = target_voltage - dac_feedback;
|
||||
if error < ElectricPotential::new::<volt>(0.0) {
|
||||
break;
|
||||
} else if error < best_error {
|
||||
best_error = error;
|
||||
start_value = prev_value;
|
||||
|
||||
let vref = (value as f64 / ad5680::MAX_VALUE as f64) * ElectricPotential::new::<volt>(DAC_OUT_V_MAX);
|
||||
self.max1968.set_center_point(vref);
|
||||
|
||||
}
|
||||
prev_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
info!("Before Calibration, VREF = {:?}", before_cal);
|
||||
info!("After Calibration, VREF = {:?}", self.max1968.phy.center_pt);
|
||||
self.max1968.set_i(ElectricCurrent::new::<ampere>(0.0));
|
||||
info!("VREF Value {:?}", self.max1968.adc_read(AdcReadTarget::VREF, 64));
|
||||
info!("DAC VFB Value {:?}", self.max1968.adc_read(AdcReadTarget::DacVfb, 64));
|
||||
sys_timer::sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user