use core::u16; use crate::thermostat::ad5680; use fugit::KilohertzU32; use stm32f4xx_hal::{ adc::{ config::{self, AdcConfig}, Adc, }, gpio::{gpioa::*, gpiob::*, gpioc::*, Alternate, Analog, Output, PushPull}, hal::{self, blocking::spi::Transfer, digital::v2::OutputPin}, pac::{ADC1, SPI1, TIM4}, spi::{NoMiso, Spi, TransferModeNormal}, timer::pwm::PwmChannel, }; use uom::si::{ electric_potential::millivolt, f64::ElectricPotential, ratio::ratio, }; pub const PWM_FREQ_KHZ: KilohertzU32 = KilohertzU32::from_raw(20); pub trait ChannelPins { type DacSpi: Transfer; type DacSync: OutputPin; type ShdnPin: OutputPin; type VRefPin; type ItecPin; type DacFeedbackPin; type VTecPin; type MaxVPin; type MaxIPosPin; type MAXINegPin; } pub struct Channel0; impl ChannelPins for Channel0 { type DacSpi = DacSpi; type DacSync = DacSync; type ShdnPin = PA5>; type VRefPin = PA6; type ItecPin = PB1; type DacFeedbackPin = PC0; type VTecPin = PB0; type MaxVPin = PwmChannel; type MaxIPosPin = PwmChannel; type MAXINegPin = PwmChannel; } pub struct MAX1968Phy { pub dac: ad5680::Dac, pub shdn: C::ShdnPin, pub vref_pin: C::VRefPin, pub itec_pin: C::ItecPin, pub dac_feedback_pin: C::DacFeedbackPin, pub vtec_pin: C::VTecPin, pub max_v: C::MaxVPin, pub max_i_pos: C::MaxIPosPin, pub max_i_neg: C::MAXINegPin, } pub struct MAX1968PinSet { pub dac: ad5680::Dac, pub shdn: C::ShdnPin, pub vref_pin: C::VRefPin, pub itec_pin: C::ItecPin, pub dac_feedback_pin: C::DacFeedbackPin, pub vtec_pin: C::VTecPin, pub max_v: C::MaxVPin, pub max_i_pos: C::MaxIPosPin, pub max_i_neg: C::MAXINegPin, } type DacSpi = Spi>, NoMiso, PB5>), TransferModeNormal>; type DacSync = PB4>; pub struct MAX1968 { // settings pub phy: MAX1968Phy, pub pins_adc: Adc, } pub enum PwmPinsEnum { MaxV, MaxPosI, MaxNegI, } pub enum AdcReadTarget { VREF, DacVfb, ITec, VTec, } impl MAX1968Phy { pub fn new(pins: MAX1968PinSet) -> Self { MAX1968Phy { dac: pins.dac, shdn: pins.shdn, vref_pin: pins.vref_pin, itec_pin: pins.itec_pin, dac_feedback_pin: pins.dac_feedback_pin, vtec_pin: pins.vtec_pin, max_v: pins.max_v, max_i_pos: pins.max_i_pos, max_i_neg: pins.max_i_neg, } } } impl MAX1968 { pub fn new(phy_ch0: MAX1968Phy, adc1: ADC1) -> Self { let config = AdcConfig::default() .clock(config::Clock::Pclk2_div_8) .default_sample_time(config::SampleTime::Cycles_480); // Do not set reset RCCs as it causes other ADCs' clock to be disabled let mut pins_adc = Adc::adc1(adc1, false, config); pins_adc.calibrate(); MAX1968 { phy: phy_ch0, pins_adc: pins_adc, } } // Return the calibrated VDDA Voltage // Can be used to set reference voltage for other ADC pub fn get_calibrated_vdda(&mut self) -> u32 { self.pins_adc.reference_voltage() } pub fn power_down(&mut self) { self.phy.shdn.set_low(); } pub fn power_up(&mut self) { self.phy.shdn.set_high(); } pub fn set_dac(&mut self, voltage: ElectricPotential, dac_out_v_max: ElectricPotential) -> ElectricPotential { let value = ((voltage / dac_out_v_max).get::() * (ad5680::MAX_VALUE as f64)) as u32; self.phy.dac.set(value).unwrap(); // TODO: Store the set-ed DAC Voltage Value voltage } // AN4073: ADC Reading Dispersion can be reduced through Averaging // Upon test, 16 Point Averaging = +-3 LSB Dispersion 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 => { for _ in (0..avg_pt).rev() { sample += self.pins_adc.convert( &self.phy.vref_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } AdcReadTarget::DacVfb => { for _ in (0..avg_pt).rev() { sample += self.pins_adc.convert( &self.phy.dac_feedback_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } AdcReadTarget::ITec => { for _ in (0..avg_pt).rev() { sample += self.pins_adc.convert( &self.phy.itec_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } AdcReadTarget::VTec => { for _ in (0..avg_pt).rev() { sample += self.pins_adc.convert( &self.phy.vtec_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } sample / avg_pt as u32 } }; let mv = self.pins_adc.sample_to_millivolts(sample as u16); ElectricPotential::new::(mv as f64) } pub 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.phy.max_v, duty), PwmPinsEnum::MaxPosI => set(&mut self.phy.max_i_pos, duty), PwmPinsEnum::MaxNegI => set(&mut self.phy.max_i_neg, duty), } } }