// 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(); } ) }