diff --git a/Software/MainBoard/rust/Cargo.lock b/Software/MainBoard/rust/Cargo.lock index cfaea11..098ebf3 100644 --- a/Software/MainBoard/rust/Cargo.lock +++ b/Software/MainBoard/rust/Cargo.lock @@ -1901,6 +1901,7 @@ dependencies = [ "chrono", "chrono-tz", "crc", + "critical-section", "deranged", "ds323x", "edge-dhcp", diff --git a/Software/MainBoard/rust/Cargo.toml b/Software/MainBoard/rust/Cargo.toml index bd3cbeb..27bf35e 100644 --- a/Software/MainBoard/rust/Cargo.toml +++ b/Software/MainBoard/rust/Cargo.toml @@ -101,6 +101,7 @@ chrono-tz = { version = "0.10.4", default-features = false, features = ["filter- heapless = { version = "0.7.17", features = ["serde"] } # stay in sync with mcutie version static_cell = "2.1.1" portable-atomic = "1.11.1" +critical-section = "1" crc = "3.3.0" bytemuck = { version = "1.24.0", features = ["derive", "min_const_generics", "pod_saturating", "extern_crate_alloc"] } deranged = "0.5.5" diff --git a/Software/MainBoard/rust/src/hal/water.rs b/Software/MainBoard/rust/src/hal/water.rs index cdc642d..44bec81 100644 --- a/Software/MainBoard/rust/src/hal/water.rs +++ b/Software/MainBoard/rust/src/hal/water.rs @@ -1,8 +1,6 @@ use crate::bail; use crate::fat_error::FatError; use crate::hal::{ADC1, TANK_MULTI_SAMPLE}; -use core::cell::RefCell; -use embassy_sync::blocking_mutex::CriticalSectionMutex; use embassy_time::Timer; use esp_hal::analog::adc::{Adc, AdcCalLine, AdcConfig, AdcPin, Attenuation}; use esp_hal::delay::Delay; @@ -19,14 +17,13 @@ use portable_atomic::{AtomicUsize, Ordering}; unsafe impl Send for TankSensor<'_> {} static FLOW_OVERFLOW_COUNTER: AtomicUsize = AtomicUsize::new(0); -static FLOW_UNIT: CriticalSectionMutex>>> = - CriticalSectionMutex::new(RefCell::new(None)); pub struct TankSensor<'a> { one_wire_bus: OneWire>, tank_channel: Adc<'a, ADC1<'a>, Async>, tank_power: Output<'a>, tank_pin: AdcPin, ADC1<'a>, AdcCalLine>>, + flow_unit: Unit<'static, 1>, } impl<'a> TankSensor<'a> { @@ -64,61 +61,47 @@ impl<'a> TankSensor<'a> { pcnt1.listen(); - FLOW_UNIT.lock(|refcell| { - refcell.borrow_mut().replace(pcnt1); - }); - Ok(TankSensor { one_wire_bus, tank_channel, tank_power, tank_pin, + flow_unit: pcnt1, }) } pub fn reset_flow_meter(&mut self) { - FLOW_OVERFLOW_COUNTER.store(0, Ordering::SeqCst); - FLOW_UNIT.lock(|refcell| { - if let Some(unit) = refcell.borrow_mut().as_mut() { - unit.pause(); - unit.clear(); - } + // Pause, clear counter, clear any pending interrupt, then reset the overflow counter — + // all inside a single critical section to prevent a race where the interrupt fires + // between the overflow reset and the pause. + critical_section::with(|_| { + self.flow_unit.pause(); + self.flow_unit.clear(); + self.flow_unit.reset_interrupt(); + FLOW_OVERFLOW_COUNTER.store(0, Ordering::SeqCst); }); } pub fn start_flow_meter(&mut self) { - FLOW_UNIT.lock(|refcell| { - if let Some(unit) = refcell.borrow_mut().as_mut() { - unit.resume(); - } - }); - } - - fn get_flow_meter_value(&mut self) -> i16 { - FLOW_UNIT.lock(|refcell| { - refcell.borrow_mut().as_mut().map_or(0, |unit| unit.value()) - }) + self.flow_unit.resume(); } pub fn stop_flow_meter(&mut self) -> i16 { - FLOW_UNIT.lock(|refcell| { - let mut borrowed = refcell.borrow_mut(); - if let Some(unit) = borrowed.as_mut() { - let val = unit.value(); - unit.pause(); - val - } else { - 0 - } + critical_section::with(|_| { + let val = self.flow_unit.value(); + self.flow_unit.pause(); + val }) } pub fn get_full_flow_count(&self) -> u32 { - let current = FLOW_UNIT.lock(|refcell| { - refcell.borrow().as_ref().map_or(0, |unit| unit.value() as u32) - }); - let overflowed = FLOW_OVERFLOW_COUNTER.load(Ordering::SeqCst) as u32; - overflowed * (i16::MAX.wrapping_add(1) as u32) + current + // Read both values inside a single critical section so an overflow interrupt cannot + // fire between the two reads and produce an inconsistent result. + critical_section::with(|_| { + let overflowed = FLOW_OVERFLOW_COUNTER.load(Ordering::SeqCst) as u32; + let current = self.flow_unit.value() as u32; + overflowed * (i16::MAX as u32 + 1) + current + }) } pub async fn water_temperature_c(&mut self) -> Result { @@ -218,15 +201,12 @@ impl<'a> TankSensor<'a> { #[esp_hal::handler] pub fn flow_interrupt_handler() { - FLOW_UNIT.lock(|refcell| { - if let Some(unit) = refcell.borrow_mut().as_mut() { - if unit.interrupt_is_set() { - let events = unit.events(); - if events.high_limit { - FLOW_OVERFLOW_COUNTER.fetch_add(1, Ordering::SeqCst); - } - unit.reset_interrupt(); - } + use esp_hal::peripherals::PCNT; + let pcnt = PCNT::regs(); + if pcnt.int_raw().read().cnt_thr_event_u(1).bit() { + if pcnt.u_status(1).read().h_lim().bit() { + FLOW_OVERFLOW_COUNTER.fetch_add(1, Ordering::SeqCst); } - }); + pcnt.int_clr().write(|w| w.cnt_thr_event_u(1).set_bit()); + } }