Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Sketch out alternative interrupts API #29

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
50 changes: 50 additions & 0 deletions src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Port = PD>,
{
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<Port = PD>,
{
pub fn query_interrupt_state(&mut self) -> Option<bool> {
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<Port = PD>,
{
/// 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))
}
}
Loading