diff --git a/src/device/boot.rs b/src/device/boot.rs index 8867363..7cecd00 100644 --- a/src/device/boot.rs +++ b/src/device/boot.rs @@ -1,6 +1,6 @@ use super::{gpio, sys_timer, usb}; -use crate::{laser_diode::current_sources::*}; -use crate::{thermostat::max1968::MAX1968} +use crate::laser_diode::current_sources::*; +use crate::thermostat::max1968::MAX1968; use fugit::ExtU32; use log::info; use stm32f4xx_hal::{ @@ -10,17 +10,17 @@ use stm32f4xx_hal::{ watchdog::IndependentWatchdog, }; -use uom::si::{ - electric_current::ampere, - f64::ElectricCurrent, -}; +use uom::si::{electric_current::ampere, f64::ElectricCurrent}; #[cfg(not(feature = "semihosting"))] const WATCHDOG_PERIOD: u32 = 1000; #[cfg(feature = "semihosting")] const WATCHDOG_PERIOD: u32 = 30000; -pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> IndependentWatchdog { +pub fn bootup( + mut core_perif: CorePeripherals, + perif: Peripherals, +) -> (IndependentWatchdog, MAX1968) { core_perif.SCB.enable_icache(); core_perif.SCB.enable_dcache(&mut core_perif.CPUID); @@ -65,6 +65,7 @@ pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> Independen laser.set_current(0.1).unwrap(); let mut tec_driver = MAX1968::new(max1968_phy, perif.ADC1); + tec_driver.setup(); tec_driver.set_i(ElectricCurrent::new::(1.0)); @@ -77,5 +78,5 @@ pub fn bootup(mut core_perif: CorePeripherals, perif: Peripherals) -> Independen info!("Kirdy setup complete"); - wd + (wd, tec_driver) } diff --git a/src/device/gpio.rs b/src/device/gpio.rs index 575d650..49b73a9 100644 --- a/src/device/gpio.rs +++ b/src/device/gpio.rs @@ -1,6 +1,6 @@ use crate::laser_diode::current_sources::*; use crate::thermostat::ad5680; -use crate::thermostat::max1968::{MAX1968PinSet, PWM_FREQ_KHZ}; +use crate::thermostat::max1968::{Channel0, MAX1968PinSet, MAX1968Phy, PWM_FREQ_KHZ}; use fugit::RateExtU32; use stm32_eth::EthPins; use stm32f4xx_hal::{ @@ -36,7 +36,7 @@ pub fn setup( EthernetPins, USB, CurrentSourcePhyConstruct, - MAX1968PinSet, + MAX1968Phy, // photo_diode_phy, // thermostat_phy ) { @@ -92,30 +92,32 @@ pub fn setup( gpiob.pb8.into_alternate(), ); let (max_i_neg0, max_v0, max_i_pos0) = - tim4.pwm_hz(pwm_chs, PWM_FREQ_KHZ.kHz(), &clocks).split(); + tim4.pwm_hz(pwm_chs, PWM_FREQ_KHZ.convert(), &clocks).split(); - let max1968_phy = MAX1968PinSet { - dac_spi: Spi::new( - spi1, - ( - gpiob.pb3.into_alternate(), - NoMiso {}, - gpiob.pb5.into_alternate(), + let max1968_phy = MAX1968Phy::new(MAX1968PinSet { + dac: ad5680::Dac::new( + Spi::new( + spi1, + ( + gpiob.pb3.into_alternate(), + NoMiso {}, + gpiob.pb5.into_alternate(), + ), + ad5680::SPI_MODE, + ad5680::SPI_CLOCK_MHZ.convert(), + &clocks, ), - ad5680::SPI_MODE, - ad5680::SPI_CLOCK_MHZ.MHz(), - &clocks, + gpiob.pb4.into_push_pull_output(), ), - dac_sync: gpiob.pb4.into_push_pull_output(), - dac_vfb: gpioc.pc0.into_analog(), shdn: gpioa.pa5.into_push_pull_output(), - vref: gpioa.pa6.into_analog(), - vtec: gpiob.pb0.into_analog(), - itec: gpiob.pb1.into_analog(), - max_v0: max_v0, - max_i_pos0: max_i_pos0, - max_i_neg0: max_i_neg0, - }; + vref_pin: gpioa.pa6.into_analog(), + itec_pin: gpiob.pb1.into_analog(), + dac_feedback_pin: gpioc.pc0.into_analog(), + vtec_pin: gpiob.pb0.into_analog(), + max_v: max_v0, + max_i_pos: max_i_pos0, + max_i_neg: max_i_neg0, + }); (eth_pins, usb, current_source_phy, max1968_phy) } diff --git a/src/main.rs b/src/main.rs index fa74079..da2b913 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ fn main() -> ! { let core_perif = CorePeripherals::take().unwrap(); let perif = Peripherals::take().unwrap(); - let mut wd = bootup(core_perif, perif); + let (mut wd, mut tec_driver) = bootup(core_perif, perif); loop { wd.feed(); diff --git a/src/thermostat/ad5680.rs b/src/thermostat/ad5680.rs index 6afca8c..bc09518 100644 --- a/src/thermostat/ad5680.rs +++ b/src/thermostat/ad5680.rs @@ -1,4 +1,5 @@ use crate::device::sys_timer::sleep; +use fugit::MegahertzU32; use stm32f4xx_hal::{ hal::{blocking::spi::Transfer, digital::v2::OutputPin}, spi, @@ -9,7 +10,7 @@ pub const SPI_MODE: spi::Mode = spi::Mode { polarity: spi::Polarity::IdleLow, phase: spi::Phase::CaptureOnSecondTransition, }; -pub const SPI_CLOCK_MHZ: u32 = 30; +pub const SPI_CLOCK_MHZ: MegahertzU32 = MegahertzU32::from_raw(30); pub const MAX_VALUE: u32 = 0x3FFFF; diff --git a/src/thermostat/max1968.rs b/src/thermostat/max1968.rs index 2b268fa..85d7bea 100644 --- a/src/thermostat/max1968.rs +++ b/src/thermostat/max1968.rs @@ -1,15 +1,16 @@ +use core::marker::PhantomData; use core::u16; use crate::thermostat::ad5680; -use fugit::RateExtU32; +use fugit::KilohertzU32; use stm32f4xx_hal::{ adc::{ config::{self, AdcConfig}, Adc, }, gpio::{gpioa::*, gpiob::*, gpioc::*, Alternate, Analog, Output, PushPull}, - hal, + hal::{self, blocking::spi::Transfer, digital::v2::OutputPin}, pac::{ADC1, SPI1, TIM4}, rcc::Clocks, spi::{NoMiso, Spi, TransferModeNormal}, @@ -24,29 +25,108 @@ use uom::si::{ ratio::ratio, }; -pub const PWM_FREQ_KHZ: u32 = 20; -pub const R_SENSE: f64 = 0.05; +pub const PWM_FREQ_KHZ: KilohertzU32 = KilohertzU32::from_raw(20); +pub const R_SENSE: ElectricalResistance = ElectricalResistance { + dimension: PhantomData, + units: PhantomData, + value: 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; +const TEC_VSEC_BIAS_V: ElectricPotential = ElectricPotential { + dimension: PhantomData, + units: PhantomData, + value: 1.65, +}; -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, +// Kirdy Design Specs: +// MaxV = 5.0V +// MAX Current = +- 1.0A +const MAX_V_DUTY_TO_CURRENT_RATE: ElectricPotential = ElectricPotential { + dimension: PhantomData, + units: PhantomData, + value: 4.0 * 3.3, +}; +pub const MAX_V_MAX: ElectricPotential = ElectricPotential { + dimension: PhantomData, + units: PhantomData, + value: 5.0, +}; +const MAX_V_DUTY_MAX: f64 = MAX_V_MAX.value / MAX_V_DUTY_TO_CURRENT_RATE.value; +const MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE: ElectricCurrent = ElectricCurrent { + dimension: PhantomData, + units: PhantomData, + value: 1.0 / (10.0 * R_SENSE.value / 3.3), +}; +pub const MAX_I_POS_CURRENT: ElectricCurrent = ElectricCurrent { + dimension: PhantomData, + units: PhantomData, + value: 1.0, +}; +pub const MAX_I_NEG_CURRENT: ElectricCurrent = ElectricCurrent { + dimension: PhantomData, + units: PhantomData, + value: 1.0, +}; +// .get::() is not implemented for const +const MAX_I_POS_DUTY_MAX: f64 = MAX_I_POS_CURRENT.value / MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value; +const MAX_I_NEG_DUTY_MAX: f64 = MAX_I_NEG_CURRENT.value / MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE.value; + +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 { + // state + pub center_pt: ElectricPotential, + 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>; @@ -59,13 +139,11 @@ pub struct MaxAdcPins { } 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, + // settings + pub phy: MAX1968Phy, + pub pins_adc: Adc, } + pub struct PwmPins { pub max_v0: PwmChannel, pub max_i_pos0: PwmChannel, @@ -86,8 +164,6 @@ pub enum AdcReadTarget { 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(); @@ -100,7 +176,7 @@ impl PwmPins { ); let (mut max_i_neg0, mut max_v0, mut max_i_pos0) = - tim4.pwm_hz(channels, freq, &clocks).split(); + tim4.pwm_hz(channels, PWM_FREQ_KHZ.convert(), &clocks).split(); init_pwm_pin(&mut max_v0); init_pwm_pin(&mut max_i_neg0); @@ -114,10 +190,25 @@ impl PwmPins { } } -impl MAX1968 { - pub fn new(pins: MAX1968PinSet, adc1: ADC1) -> Self { - let dac = ad5680::Dac::new(pins.dac_spi, pins.dac_sync); +impl MAX1968Phy { + pub fn new(pins: MAX1968PinSet) -> Self { + MAX1968Phy { + center_pt: ElectricPotential::new::(1.5), + 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_2) .default_sample_time(config::SampleTime::Cycles_480); @@ -125,25 +216,12 @@ impl MAX1968 { 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, - }, + phy: phy_ch0, + pins_adc: pins_adc, } } - pub fn setup(&mut self){ + pub fn setup(&mut self) { self.power_down(); let vref = self.adc_read(AdcReadTarget::VREF, 2048); @@ -158,28 +236,28 @@ impl MAX1968 { } pub fn power_down(&mut self) { - let _ = self.shdn.set_low(); + let _ = self.phy.shdn.set_low(); } pub fn power_up(&mut self) { - let _ = self.shdn.set_high(); + let _ = self.phy.shdn.set_high(); } pub fn set_center_point(&mut self, value: ElectricPotential) { - self.center_pt = value; + self.phy.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(); + self.phy.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 center_point = self.phy.center_pt; + let r_sense = 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); @@ -193,8 +271,8 @@ impl MAX1968 { sample = match adc_read_target { AdcReadTarget::VREF => { for _ in (0..avg_pt).rev() { - sample += self.adc.convert( - &self.adc_pins.vref, + sample += self.pins_adc.convert( + &self.phy.vref_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } @@ -202,8 +280,8 @@ impl MAX1968 { } AdcReadTarget::DacVfb => { for _ in (0..avg_pt).rev() { - sample += self.adc.convert( - &self.adc_pins.dac_vfb, + sample += self.pins_adc.convert( + &self.phy.dac_feedback_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } @@ -211,8 +289,8 @@ impl MAX1968 { } AdcReadTarget::ITec => { for _ in (0..avg_pt).rev() { - sample += self.adc.convert( - &self.adc_pins.itec, + sample += self.pins_adc.convert( + &self.phy.itec_pin, stm32f4xx_hal::adc::config::SampleTime::Cycles_480, ) as u32; } @@ -220,15 +298,15 @@ impl MAX1968 { } AdcReadTarget::VTec => { for _ in (0..avg_pt).rev() { - sample += self.adc.convert( - &self.adc_pins.vtec, + 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.adc.sample_to_millivolts(sample as u16); + let mv = self.pins_adc.sample_to_millivolts(sample as u16); ElectricPotential::new::(mv as f64) } @@ -243,15 +321,14 @@ impl MAX1968 { } pub fn get_tec_i(&mut self) -> ElectricCurrent { - (self.adc_read(AdcReadTarget::ITec, 1) - self.center_pt) + (self.adc_read(AdcReadTarget::ITec, 1) - self.phy.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 + -(self.adc_read(AdcReadTarget::VTec, 1) - TEC_VSEC_BIAS_V) * 4.0 } fn set_pwm(&mut self, pwm_pin: PwmPinsEnum, duty: f64, max_duty: f64) -> f64 { @@ -266,33 +343,27 @@ impl MAX1968 { 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), + 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), } } - 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::(); + pub fn set_max_v(&mut self, max_v: ElectricPotential) -> ElectricPotential { + let duty = (max_v / MAX_V_DUTY_TO_CURRENT_RATE).get::(); let duty = self.set_pwm(PwmPinsEnum::MaxV, duty, MAX_V_DUTY_MAX); - (duty * max, max) + duty * MAX_V_DUTY_TO_CURRENT_RATE } - 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::(); + pub fn set_max_i_pos(&mut self, max_i_pos: ElectricCurrent) -> ElectricCurrent { + let duty = (max_i_pos / MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::(); let duty = self.set_pwm(PwmPinsEnum::MaxPosI, duty, MAX_I_POS_DUTY_MAX); - (duty * max * 3.3 / 1.5, max) + duty * MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE } - 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::(); + pub fn set_max_i_neg(&mut self, max_i_neg: ElectricCurrent) -> ElectricCurrent { + let duty = (max_i_neg / MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE).get::(); let duty = self.set_pwm(PwmPinsEnum::MaxNegI, duty, MAX_I_NEG_DUTY_MAX); - (duty * max * 3.3 / 1.5, max) + duty * MAX_I_POS_NEG_DUTY_TO_CURRENT_RATE } } diff --git a/src/thermostat/thermostat.rs b/src/thermostat/thermostat.rs index e69de29..832ad9a 100644 --- a/src/thermostat/thermostat.rs +++ b/src/thermostat/thermostat.rs @@ -0,0 +1,18 @@ +use crate::thermostat::ad5680; +use crate::thermostat::MAX1968; + +pub struct Thermostat { + max1968: MAX1968, + // TADC +} + +impl Thermostat{ + pub fn new (max1968: MAX1968) -> Self { + Thermostat{ + max1968 + } + } + pub fn setup(&mut self){ + self.max1968.setup(); + } +} \ No newline at end of file