diff --git a/src/laser_diode/analog_wdg.rs b/src/laser_diode/analog_wdg.rs new file mode 100644 index 0000000..95c3c5d --- /dev/null +++ b/src/laser_diode/analog_wdg.rs @@ -0,0 +1,208 @@ +// stm32f4xx_hal does not provide config and driver for analog watchdog yet +use stm32f4xx_hal::{ + pac::{ADC2, NVIC}, + gpio::{Analog, Output, PushPull, gpioa::PA3, gpiod::PD9}, + interrupt, +}; +use uom::si::electric_potential::millivolt; +use uom::si::f64::ElectricPotential; + +// 12 bit Resolution +const MAX_SAMPLE: u16 = 4095; + +pub type LdPwrEnPinType = PD9>; +pub type PdMonAdcPinType = PA3; + +static mut ANALOG_WDG: Option = None; + +pub struct LdAnalogWdgPhy { + // To make sure PA3 is configured to Analog mode + pub _pd_mon_ch0: PdMonAdcPinType, + pub pwr_en_ch0: LdPwrEnPinType, +} + + +#[derive(Clone)] +pub struct AlarmStatus { + alarm: bool, + val: u16, +} + +pub struct LdAnalogWdg { + pac: ADC2, + phy: LdAnalogWdgPhy, + alarm_status: AlarmStatus, + //Calibrated VDDA in millivolt from Adc.calibrate() + calibrated_vdda: u32, +} + +impl LdAnalogWdg { + /// ADC interrupt is disabled and continuous conversion is started by default + pub fn setup(pac: ADC2, phy: LdAnalogWdgPhy){ + unsafe { ANALOG_WDG = Some(LdAnalogWdg{ + pac: pac, + phy: phy, + alarm_status: AlarmStatus { + alarm: false, + val: 0x0000, + }, + calibrated_vdda: 3300, + }); + } + + unsafe { + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.pac.cr1.write(|w| w + // 12 Bit Resolution + .res().twelve_bit() + // Enable Analog Watchdog on Single Regular Channel + .awden().enabled() + .awdsgl().single_channel() + .jawden().disabled() + // Disable Analog Watchdog Interrupt + .awdie().disabled() + // Select Analog Watchdog Channel 3 (PA3) on ADC2 + .awdch().bits(0x03) + ); + wdg.pac.cr2.write(|w| w + // Continous Conversion mode + .cont().set_bit() + // Enable ADC + .adon().set_bit() + // Start ADC Conversion + .swstart().set_bit() + ); + // Set Sampling Time for Channel 3 to 480 Cycle + wdg.pac.smpr2.write(|w| w + .smp3().cycles480() + ); + // Set the high threshold to be max value initially + wdg.pac.htr.write(|w| w.bits(0xFFFF_FFFF)); + // Set the high threshold to be min value initially + wdg.pac.ltr.write(|w| w.bits(0x0000_0000)); + // Set the Conversion Sequence to only have Channel 3 (PA3) + wdg.pac.sqr3.write(|w| w + .sq1().bits(0x03) + ); + } + } + } + + fn get() -> Option<&'static mut Self> { + unsafe { ANALOG_WDG.as_mut() } + } + + /// This fn accepts the calibrated vdda value from Adc.calibrate() + pub fn set_calibrated_vdda(val: u32) { + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.calibrated_vdda = val; + } + } + + pub fn get_pd_v() -> ElectricPotential { + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + let sample = wdg.pac.dr.read().data().bits(); + + return ElectricPotential::new::(((u32::from(sample) * wdg.calibrated_vdda) / u32::from(MAX_SAMPLE)) as f64) + } + ElectricPotential::new::(0.0) + } + + fn set_alarm(){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.alarm_status = AlarmStatus { + alarm: true, + val: wdg.pac.dr.read().data().bits(), + }; + } + } + + pub fn get_alarm_status() -> AlarmStatus { + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + return wdg.alarm_status.clone() + } + AlarmStatus { + alarm: false, + val: 0x0000, + } + } + + pub fn clear_alarm_status(){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.alarm_status = AlarmStatus { + alarm: false, + val: 0x0000, + }; + } + } + + /// Set ADC Watchdog Higher threshold register + /// Interrupt is triggered when ADC value is ABOVE the value set + pub fn set_htr(htr: u32){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.pac.htr.write(|w| unsafe {w.bits(htr)}); + } + } + + /// Set ADC Watchdog Lower threshold register + /// Interrupt is triggered when ADC value is BELOW the value set + #[allow(unused)] + pub fn set_ltr(ltr:u32){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.pac.ltr.write(|w| unsafe {w.bits(ltr)}); + } + } + + pub fn enable_watchdog_interrupt(){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.pac.cr1.modify(|_, w| w + .awdie().set_bit() + ); + } + } + + pub fn disable_watchdog_interrupt(){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.pac.cr1.modify(|_, w| w + .awdie().clear_bit() + ); + } + } + + fn clear_interrupt_bit(){ + unsafe{ + NVIC::unmask(interrupt::ADC); + } + } + + pub fn is_pwr_engaged() -> bool{ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + return wdg.phy.pwr_en_ch0.is_set_high() + } + false + } + + pub fn pwr_engage(){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.phy.pwr_en_ch0.set_high() + } + } + + pub fn pwr_disengage(){ + if let Some(ref mut wdg ) = LdAnalogWdg::get() { + wdg.phy.pwr_en_ch0.set_low() + } + } +} + +#[interrupt] +fn ADC(){ + cortex_m::interrupt::free(|_| { + LdAnalogWdg::set_alarm(); + LdAnalogWdg::pwr_disengage(); + // Disable interrupt to avoid getting stuck in infinite interrupt loop + LdAnalogWdg::disable_watchdog_interrupt(); + LdAnalogWdg::clear_interrupt_bit(); + } + ) +} diff --git a/src/laser_diode/mod.rs b/src/laser_diode/mod.rs index 8972fe7..59f9016 100644 --- a/src/laser_diode/mod.rs +++ b/src/laser_diode/mod.rs @@ -1,5 +1,5 @@ pub mod ld_ctrl; pub mod max5719; pub mod laser_diode; -pub mod pid_state; -pub mod pd_mon; \ No newline at end of file +pub mod pd_mon; +pub mod analog_wdg; \ No newline at end of file