use core::u16; use crate::thermostat::ad5680; use fugit::RateExtU32; use stm32f4xx_hal::{ adc::{ config::{self, AdcConfig}, Adc, }, gpio::{gpioa::*, gpiob::*, gpioc::*, Alternate, Analog, Output, PushPull}, hal, pac::{ADC1, SPI1, TIM4}, rcc::Clocks, spi::{NoMiso, Spi, TransferModeNormal}, timer::pwm::{PwmChannel, PwmExt}, }; use uom::si::{ electric_current::ampere, electric_potential::{millivolt, volt}, electrical_resistance::ohm, f64::{ElectricCurrent, ElectricPotential, ElectricalResistance}, ratio::ratio, }; pub const PWM_FREQ_KHZ: u32 = 20; pub const R_SENSE: f64 = 0.05; // 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; const TEC_VSEC_BIAS_V: f64 = 1.65; const MAX_V_DUTY_MAX: f64 = 1.5 / 3.3; const MAX_I_POS_DUTY_MAX: f64 = 1.5 / 3.3; const MAX_I_NEG_DUTY_MAX: f64 = 1.5 / 3.3; pub struct MAX1968PinSet { pub dac_spi: DacSpi, pub dac_sync: DacSync, pub shdn: PA5>, pub dac_vfb: PC0, pub vref: PA6, pub vtec: PB0, pub itec: PB1, pub max_v0: PwmChannel, pub max_i_pos0: PwmChannel, pub max_i_neg0: PwmChannel, } type DacSpi = Spi>, NoMiso, PB5>), TransferModeNormal>; type DacSync = PB4>; pub struct MaxAdcPins { pub dac_vfb: PC0, pub vref: PA6, pub itec: PB1, pub vtec: PB0, } pub struct MAX1968 { pub center_pt: ElectricPotential, // To be moved to a miniconf crate's struct pub adc: Adc, pub dac: ad5680::Dac, pub shdn: PA5>, pub adc_pins: MaxAdcPins, pub pwm_pins: PwmPins, } pub struct PwmPins { pub max_v0: PwmChannel, pub max_i_pos0: PwmChannel, pub max_i_neg0: PwmChannel, } enum PwmPinsEnum { MaxV, MaxPosI, MaxNegI, } pub enum AdcReadTarget { VREF, DacVfb, ITec, VTec, } impl PwmPins { fn setup(clocks: Clocks, tim4: TIM4, max_v0: PB7, max_i_pos0: PB8, max_i_neg0: PB6) -> PwmPins { let freq = 20.kHz(); fn init_pwm_pin>(pin: &mut P) { pin.set_duty(0); pin.enable(); } let channels = ( max_i_neg0.into_alternate::<2>(), max_v0.into_alternate::<2>(), max_i_pos0.into_alternate::<2>(), ); let (mut max_i_neg0, mut max_v0, mut max_i_pos0) = tim4.pwm_hz(channels, freq, &clocks).split(); init_pwm_pin(&mut max_v0); init_pwm_pin(&mut max_i_neg0); init_pwm_pin(&mut max_i_pos0); PwmPins { max_v0, max_i_pos0, max_i_neg0, } } } impl MAX1968 { pub fn new(pins: MAX1968PinSet, adc1: ADC1) -> Self { let dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync); let config = AdcConfig::default() .clock(config::Clock::Pclk2_div_2) .default_sample_time(config::SampleTime::Cycles_480); let pins_adc = Adc::adc1(adc1, true, config); MAX1968 { center_pt: ElectricPotential::new::(1500.0), adc: pins_adc, dac: dac, shdn: pins.shdn, adc_pins: MaxAdcPins { dac_vfb: pins.dac_vfb, vref: pins.vref, itec: pins.itec, vtec: pins.vtec, }, pwm_pins: PwmPins { max_v0: pins.max_v0, max_i_pos0: pins.max_i_pos0, max_i_neg0: pins.max_i_neg0, }, } } pub fn power_down(&mut self) { let _ = self.shdn.set_high(); } pub fn power_up(&mut self) { let _ = self.shdn.set_high(); } pub fn set_center_point(&mut self, value: ElectricPotential) { self.center_pt = value; } fn set_dac(&mut self, voltage: ElectricPotential) -> ElectricPotential { let value = ((voltage / ElectricPotential::new::(DAC_OUT_V_MAX)).get::() * (ad5680::MAX_VALUE as f64)) as u32; self.dac.set(value).unwrap(); // TODO: Store the set-ed DAC Voltage Value voltage } pub fn set_i(&mut self, i_tec: ElectricCurrent) -> ElectricCurrent { let center_point = self.center_pt; let r_sense = ElectricalResistance::new::(R_SENSE); let voltage = i_tec * 10.0 * r_sense + center_point; let voltage = self.set_dac(voltage); let i_tec = (voltage - center_point) / (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 { let mut sample: u32 = 0; sample = match adc_read_target { AdcReadTarget::VREF => { for _ in (0..avg_pt).rev() { sample += self.adc.convert( &self.adc_pins.vref, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } AdcReadTarget::DacVfb => { for _ in (0..avg_pt).rev() { sample += self.adc.convert( &self.adc_pins.dac_vfb, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } AdcReadTarget::ITec => { for _ in (0..avg_pt).rev() { sample += self.adc.convert( &self.adc_pins.itec, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } AdcReadTarget::VTec => { for _ in (0..avg_pt).rev() { sample += self.adc.convert( &self.adc_pins.vtec, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } }; let mv = self.adc.sample_to_millivolts(sample as u16); ElectricPotential::new::(mv as f64) } pub fn get_vref(&mut self) -> ElectricPotential { self.adc_read(AdcReadTarget::VREF, 16) } pub fn get_dac_vfb(&mut self) -> ElectricPotential { // Fixme: Rev0_2 does not have this feature unimplemented!() //self.adc_read(AdcReadTarget:: DacVfb, 1) } pub fn get_tec_i(&mut self) -> ElectricCurrent { (self.adc_read(AdcReadTarget::ITec, 1) - self.center_pt) / ElectricalResistance::new::(0.4) } pub fn get_tec_v(&mut self) -> ElectricPotential { // Fixme: Rev0_2 has Analog Input Polarity Reversed // Remove the -ve sign for Rev0_3 -(self.adc_read(AdcReadTarget::VTec, 1) - ElectricPotential::new::(TEC_VSEC_BIAS_V)) * 4.0 } fn set_pwm(&mut self, pwm_pin: PwmPinsEnum, duty: f64, max_duty: f64) -> f64 { fn set>(pin: &mut P, duty: f64) -> f64 { let max = pin.get_max_duty(); let value = ((duty * (max as f64)) as u16).min(max); pin.set_duty(value); pin.enable(); value as f64 / (max as f64) } let duty = duty.min(max_duty); match pwm_pin { PwmPinsEnum::MaxV => set(&mut self.pwm_pins.max_v0, duty), PwmPinsEnum::MaxPosI => set(&mut self.pwm_pins.max_i_pos0, duty), PwmPinsEnum::MaxNegI => set(&mut self.pwm_pins.max_i_neg0, duty), } } pub fn set_max_v(&mut self, max_v: ElectricPotential) -> (ElectricPotential, ElectricPotential) { let max = ElectricPotential::new::(6.0); let v = max_v / 4.0; let duty = (v / ElectricPotential::new::(3.3)).get::(); let duty = self.set_pwm(PwmPinsEnum::MaxV, duty, MAX_V_DUTY_MAX); (duty * max, max) } pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) { let max = ElectricCurrent::new::(3.0); let v = 10.0 * max_i_pos * ElectricalResistance::new::(R_SENSE); let duty = (v / ElectricPotential::new::(3.3)).get::(); let duty = self.set_pwm(PwmPinsEnum::MaxPosI, duty, MAX_I_POS_DUTY_MAX); (duty * max * 3.3 / 1.5, max) } pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> (ElectricCurrent, ElectricCurrent) { let max = ElectricCurrent::new::(3.0); let v = 10.0 * max_i_neg * ElectricalResistance::new::(R_SENSE); let duty = (v / ElectricPotential::new::(3.3)).get::(); let duty = self.set_pwm(PwmPinsEnum::MaxNegI, duty, MAX_I_NEG_DUTY_MAX); (duty * max * 3.3 / 1.5, max) } }