Refactor flow meter logic: replace global mutex with per-instance flow_unit and use critical-section for thread safety.
This commit is contained in:
1
Software/MainBoard/rust/Cargo.lock
generated
1
Software/MainBoard/rust/Cargo.lock
generated
@@ -1901,6 +1901,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
"crc",
|
"crc",
|
||||||
|
"critical-section",
|
||||||
"deranged",
|
"deranged",
|
||||||
"ds323x",
|
"ds323x",
|
||||||
"edge-dhcp",
|
"edge-dhcp",
|
||||||
|
|||||||
@@ -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
|
heapless = { version = "0.7.17", features = ["serde"] } # stay in sync with mcutie version
|
||||||
static_cell = "2.1.1"
|
static_cell = "2.1.1"
|
||||||
portable-atomic = "1.11.1"
|
portable-atomic = "1.11.1"
|
||||||
|
critical-section = "1"
|
||||||
crc = "3.3.0"
|
crc = "3.3.0"
|
||||||
bytemuck = { version = "1.24.0", features = ["derive", "min_const_generics", "pod_saturating", "extern_crate_alloc"] }
|
bytemuck = { version = "1.24.0", features = ["derive", "min_const_generics", "pod_saturating", "extern_crate_alloc"] }
|
||||||
deranged = "0.5.5"
|
deranged = "0.5.5"
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use crate::bail;
|
use crate::bail;
|
||||||
use crate::fat_error::FatError;
|
use crate::fat_error::FatError;
|
||||||
use crate::hal::{ADC1, TANK_MULTI_SAMPLE};
|
use crate::hal::{ADC1, TANK_MULTI_SAMPLE};
|
||||||
use core::cell::RefCell;
|
|
||||||
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use esp_hal::analog::adc::{Adc, AdcCalLine, AdcConfig, AdcPin, Attenuation};
|
use esp_hal::analog::adc::{Adc, AdcCalLine, AdcConfig, AdcPin, Attenuation};
|
||||||
use esp_hal::delay::Delay;
|
use esp_hal::delay::Delay;
|
||||||
@@ -19,14 +17,13 @@ use portable_atomic::{AtomicUsize, Ordering};
|
|||||||
unsafe impl Send for TankSensor<'_> {}
|
unsafe impl Send for TankSensor<'_> {}
|
||||||
|
|
||||||
static FLOW_OVERFLOW_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
static FLOW_OVERFLOW_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
static FLOW_UNIT: CriticalSectionMutex<RefCell<Option<Unit<'static, 1>>>> =
|
|
||||||
CriticalSectionMutex::new(RefCell::new(None));
|
|
||||||
|
|
||||||
pub struct TankSensor<'a> {
|
pub struct TankSensor<'a> {
|
||||||
one_wire_bus: OneWire<Flex<'a>>,
|
one_wire_bus: OneWire<Flex<'a>>,
|
||||||
tank_channel: Adc<'a, ADC1<'a>, Async>,
|
tank_channel: Adc<'a, ADC1<'a>, Async>,
|
||||||
tank_power: Output<'a>,
|
tank_power: Output<'a>,
|
||||||
tank_pin: AdcPin<GPIO5<'a>, ADC1<'a>, AdcCalLine<ADC1<'a>>>,
|
tank_pin: AdcPin<GPIO5<'a>, ADC1<'a>, AdcCalLine<ADC1<'a>>>,
|
||||||
|
flow_unit: Unit<'static, 1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TankSensor<'a> {
|
impl<'a> TankSensor<'a> {
|
||||||
@@ -64,61 +61,47 @@ impl<'a> TankSensor<'a> {
|
|||||||
|
|
||||||
pcnt1.listen();
|
pcnt1.listen();
|
||||||
|
|
||||||
FLOW_UNIT.lock(|refcell| {
|
|
||||||
refcell.borrow_mut().replace(pcnt1);
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(TankSensor {
|
Ok(TankSensor {
|
||||||
one_wire_bus,
|
one_wire_bus,
|
||||||
tank_channel,
|
tank_channel,
|
||||||
tank_power,
|
tank_power,
|
||||||
tank_pin,
|
tank_pin,
|
||||||
|
flow_unit: pcnt1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_flow_meter(&mut self) {
|
pub fn reset_flow_meter(&mut self) {
|
||||||
|
// 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);
|
FLOW_OVERFLOW_COUNTER.store(0, Ordering::SeqCst);
|
||||||
FLOW_UNIT.lock(|refcell| {
|
|
||||||
if let Some(unit) = refcell.borrow_mut().as_mut() {
|
|
||||||
unit.pause();
|
|
||||||
unit.clear();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_flow_meter(&mut self) {
|
pub fn start_flow_meter(&mut self) {
|
||||||
FLOW_UNIT.lock(|refcell| {
|
self.flow_unit.resume();
|
||||||
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())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_flow_meter(&mut self) -> i16 {
|
pub fn stop_flow_meter(&mut self) -> i16 {
|
||||||
FLOW_UNIT.lock(|refcell| {
|
critical_section::with(|_| {
|
||||||
let mut borrowed = refcell.borrow_mut();
|
let val = self.flow_unit.value();
|
||||||
if let Some(unit) = borrowed.as_mut() {
|
self.flow_unit.pause();
|
||||||
let val = unit.value();
|
|
||||||
unit.pause();
|
|
||||||
val
|
val
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_full_flow_count(&self) -> u32 {
|
pub fn get_full_flow_count(&self) -> u32 {
|
||||||
let current = FLOW_UNIT.lock(|refcell| {
|
// Read both values inside a single critical section so an overflow interrupt cannot
|
||||||
refcell.borrow().as_ref().map_or(0, |unit| unit.value() as u32)
|
// fire between the two reads and produce an inconsistent result.
|
||||||
});
|
critical_section::with(|_| {
|
||||||
let overflowed = FLOW_OVERFLOW_COUNTER.load(Ordering::SeqCst) as u32;
|
let overflowed = FLOW_OVERFLOW_COUNTER.load(Ordering::SeqCst) as u32;
|
||||||
overflowed * (i16::MAX.wrapping_add(1) as u32) + current
|
let current = self.flow_unit.value() as u32;
|
||||||
|
overflowed * (i16::MAX as u32 + 1) + current
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn water_temperature_c(&mut self) -> Result<f32, FatError> {
|
pub async fn water_temperature_c(&mut self) -> Result<f32, FatError> {
|
||||||
@@ -218,15 +201,12 @@ impl<'a> TankSensor<'a> {
|
|||||||
|
|
||||||
#[esp_hal::handler]
|
#[esp_hal::handler]
|
||||||
pub fn flow_interrupt_handler() {
|
pub fn flow_interrupt_handler() {
|
||||||
FLOW_UNIT.lock(|refcell| {
|
use esp_hal::peripherals::PCNT;
|
||||||
if let Some(unit) = refcell.borrow_mut().as_mut() {
|
let pcnt = PCNT::regs();
|
||||||
if unit.interrupt_is_set() {
|
if pcnt.int_raw().read().cnt_thr_event_u(1).bit() {
|
||||||
let events = unit.events();
|
if pcnt.u_status(1).read().h_lim().bit() {
|
||||||
if events.high_limit {
|
|
||||||
FLOW_OVERFLOW_COUNTER.fetch_add(1, Ordering::SeqCst);
|
FLOW_OVERFLOW_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
unit.reset_interrupt();
|
pcnt.int_clr().write(|w| w.cnt_thr_event_u(1).set_bit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user