From 63c7a51dad22aef5a30ed3170204174efa85ae01 Mon Sep 17 00:00:00 2001 From: Rahix Date: Mon, 1 Apr 2024 02:15:08 +0200 Subject: [PATCH] [WIP] Sketch out alternative interrupts API --- src/common.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +++ src/pin.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/src/common.rs b/src/common.rs index 6147bfe..7e71ddf 100644 --- a/src/common.rs +++ b/src/common.rs @@ -60,6 +60,44 @@ pub trait PortDriverPullUp: PortDriver { fn set_pull_up(&mut self, mask: u32, enable: bool) -> Result<(), Self::Error>; } +pub trait PortDriverInterrupts: PortDriver { + /// Fetch the interrupt status of pins from the port expander. + /// + /// This method should fetch the interrupt information from the port expander and clear the + /// remote registers. The values need to be stored locally as part of the port driver. + /// + /// The local values should be amended by new interrupt information instead of overwriting. + fn fetch_interrupt_state(&mut self) -> Result<(), Self::Error>; + + /// Read whether pins changed state since the last interrupt. + /// + /// This method should only query the locally cached values that were retrieved by + /// `fetch_interrupt_state()`. + /// + /// This method should reset the locally cached pin-change status for pins from the mask. + fn query_pin_change(&mut self, mask: u32) -> u32; +} + +pub trait PortDriverIrqMask: PortDriver { + /// Set/clear the interrupt mask of the port expander. + fn set_interrupt_mask(&mut self, mask_set: u32, mask_clear: u32) -> Result<(), Self::Error>; +} + +pub trait PortDriverIrqState: PortDriver { + /// Read the state of pins from the last interrupt. + /// + /// This method returns a tuple: + /// 1. The mask of pins that actually changed state. Value must be the same that would have + /// been returned by `query_pin_change()`. + /// 2. The state of each of the pins in the changed mask. + /// + /// This method should only query the locally cached values that were retrieved by + /// `fetch_interrupt_state()`. + /// + /// This method should reset the locally cached pin-change status for pins from the mask. + fn query_interrupt_state(&mut self, mask: u32) -> (u32, u32); +} + /// Pin Modes pub mod mode { /// Trait for pin-modes which can be used to set a logic level. diff --git a/src/lib.rs b/src/lib.rs index 6f78906..6bdcf15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,9 @@ pub(crate) use bus::I2cExt; pub(crate) use bus::SpiBus; pub(crate) use common::Direction; pub(crate) use common::PortDriver; +pub(crate) use common::PortDriverInterrupts; +pub(crate) use common::PortDriverIrqMask; +pub(crate) use common::PortDriverIrqState; pub(crate) use common::PortDriverPolarity; pub(crate) use common::PortDriverPullDown; pub(crate) use common::PortDriverPullUp; diff --git a/src/pin.rs b/src/pin.rs index f6a1a00..f34fcc4 100644 --- a/src/pin.rs +++ b/src/pin.rs @@ -251,3 +251,53 @@ where Pin::toggle(self) } } + +impl<'a, MODE: crate::mode::HasInput, MUTEX, PD> Pin<'a, MODE, MUTEX> +where + PD: crate::PortDriver + crate::PortDriverInterrupts, + MUTEX: crate::PortMutex, +{ + pub fn fetch_all_interrupt_state(&self) -> Result<(), PD::Error> { + self.port_driver.lock(|drv| drv.fetch_interrupt_state()) + } + + pub fn query_pin_change(&mut self) -> bool { + self.port_driver + .lock(|drv| drv.query_pin_change(self.pin_mask) & self.pin_mask != 0) + } +} + +impl<'a, MODE: crate::mode::HasInput, MUTEX, PD> Pin<'a, MODE, MUTEX> +where + PD: crate::PortDriver + crate::PortDriverIrqState, + MUTEX: crate::PortMutex, +{ + pub fn query_interrupt_state(&mut self) -> Option { + self.port_driver.lock(|drv| { + let (mask_changed, mask_state) = drv.query_interrupt_state(self.pin_mask); + if mask_changed & self.pin_mask != 0 { + Some(mask_state & self.pin_mask != 0) + } else { + None + } + }) + } +} + +impl<'a, MODE: crate::mode::HasInput, MUTEX, PD> Pin<'a, MODE, MUTEX> +where + PD: crate::PortDriver + crate::PortDriverIrqMask, + MUTEX: crate::PortMutex, +{ + /// Add this pin to the interrupt mask of the port expander. + pub fn listen(&mut self) -> Result<(), PD::Error> { + self.port_driver + .lock(|drv| drv.set_interrupt_mask(self.pin_mask, 0)) + } + + /// Remove this pin from the interrupt mask of the port expander. + pub fn unlisten(&mut self) -> Result<(), PD::Error> { + self.port_driver + .lock(|drv| drv.set_interrupt_mask(0, self.pin_mask)) + } +}