Disable feedback current readout on flawed HW Revs

Thermostats v2.2 and below have a noisy and offset feedback current
`tec_i` caused by missing hardware on 2 MAX1968 TEC driver pins:

1. A missing RC filter on the ITEC pin that would have isolated CPU
sampling pulses from the signal; and
2. Some missing buffering on the VREF pin that would have avoided
loading the VREF signal, preventing voltage drops from the nominal 1.5V.

Since the resulting signal `tec_i` derived from these two signals can
have an error of around +/- 100mA, and readback may affect the stability
performance of the Thermostat, disable current readback entirely on
affected hardware revisions for now.

See https://github.com/sinara-hw/Thermostat/issues/117 and
https://github.com/sinara-hw/Thermostat/issues/120.

On hardware revisions v3.x and above, this would be fixed.
This commit is contained in:
atse 2024-01-29 13:15:54 +08:00
parent 17edae44fb
commit ae3d8b51d4
3 changed files with 13 additions and 9 deletions

View File

@ -271,6 +271,8 @@ with the following keys.
| `tec_u_meas` | Volts | Measurement of the voltage across the TEC | | `tec_u_meas` | Volts | Measurement of the voltage across the TEC |
| `pid_output` | Amperes | PID control output | | `pid_output` | Amperes | PID control output |
Note: With Thermostat v2 and below, the voltage and current readouts `i_tec` and `tec_i` are disabled and null due to faulty hardware that introduces a lot of noise in the signal.
## PID Tuning ## PID Tuning
The thermostat implements a PID control loop for each of the TEC channels, more details on setting up the PID control loop can be found [here](./doc/PID%20tuning.md). The thermostat implements a PID control loop for each of the TEC channels, more details on setting up the PID control loop can be found [here](./doc/PID%20tuning.md).

View File

@ -20,6 +20,7 @@ use crate::{
command_handler::JsonBuffer, command_handler::JsonBuffer,
pins, pins,
steinhart_hart, steinhart_hart,
hw_rev,
}; };
pub const CHANNELS: usize = 2; pub const CHANNELS: usize = 2;
@ -28,17 +29,18 @@ pub const R_SENSE: f64 = 0.05;
const DAC_OUT_V_MAX: f64 = 3.0; const DAC_OUT_V_MAX: f64 = 3.0;
// TODO: -pub // TODO: -pub
pub struct Channels { pub struct Channels<'a> {
channel0: Channel<Channel0>, channel0: Channel<Channel0>,
channel1: Channel<Channel1>, channel1: Channel<Channel1>,
pub adc: ad7172::Adc<pins::AdcSpi, pins::AdcNss>, pub adc: ad7172::Adc<pins::AdcSpi, pins::AdcNss>,
/// stm32f4 integrated adc /// stm32f4 integrated adc
pins_adc: pins::PinsAdc, pins_adc: pins::PinsAdc,
pub pwm: pins::PwmPins, pub pwm: pins::PwmPins,
hwrev: &'a hw_rev::HWRev,
} }
impl Channels { impl<'a> Channels<'a> {
pub fn new(pins: pins::Pins) -> Self { pub fn new(pins: pins::Pins, hwrev: &'a hw_rev::HWRev) -> Self {
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap(); let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
// Feature not used // Feature not used
adc.set_sync_enable(false).unwrap(); adc.set_sync_enable(false).unwrap();
@ -56,7 +58,7 @@ impl Channels {
let channel1 = Channel::new(pins.channel1, adc_calibration1); let channel1 = Channel::new(pins.channel1, adc_calibration1);
let pins_adc = pins.pins_adc; let pins_adc = pins.pins_adc;
let pwm = pins.pwm; let pwm = pins.pwm;
let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm }; let mut channels = Channels { channel0, channel1, adc, pins_adc, pwm, hwrev };
for channel in 0..CHANNELS { for channel in 0..CHANNELS {
channels.calibrate_dac_value(channel); channels.calibrate_dac_value(channel);
channels.set_i(channel, ElectricCurrent::new::<ampere>(0.0)); channels.set_i(channel, ElectricCurrent::new::<ampere>(0.0));
@ -429,8 +431,8 @@ impl Channels {
fn report(&mut self, channel: usize) -> Report { fn report(&mut self, channel: usize) -> Report {
let i_set = self.get_i(channel); let i_set = self.get_i(channel);
let i_tec = self.read_itec(channel); let i_tec = if self.hwrev.major > 2 {Some(self.read_itec(channel))} else {None};
let tec_i = self.get_tec_i(channel); let tec_i = if self.hwrev.major > 2 {Some(self.get_tec_i(channel))} else {None};
let dac_value = self.get_dac(channel); let dac_value = self.get_dac(channel);
let state = self.channel_state(channel); let state = self.channel_state(channel);
let pid_output = ElectricCurrent::new::<ampere>(state.pid.y1); let pid_output = ElectricCurrent::new::<ampere>(state.pid.y1);
@ -543,8 +545,8 @@ pub struct Report {
i_set: ElectricCurrent, i_set: ElectricCurrent,
dac_value: ElectricPotential, dac_value: ElectricPotential,
dac_feedback: ElectricPotential, dac_feedback: ElectricPotential,
i_tec: ElectricPotential, i_tec: Option<ElectricPotential>,
tec_i: ElectricCurrent, tec_i: Option<ElectricCurrent>,
tec_u_meas: ElectricPotential, tec_u_meas: ElectricPotential,
pid_output: ElectricCurrent, pid_output: ElectricCurrent,
} }

View File

@ -138,7 +138,7 @@ fn main() -> ! {
let mut store = flash_store::store(dp.FLASH); let mut store = flash_store::store(dp.FLASH);
let mut channels = Channels::new(pins); let mut channels = Channels::new(pins, &hwrev);
for c in 0..CHANNELS { for c in 0..CHANNELS {
match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) { match store.read_value::<ChannelConfig>(CHANNEL_CONFIG_KEY[c]) {
Ok(Some(config)) => Ok(Some(config)) =>