Skip to content
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
51 changes: 36 additions & 15 deletions hal/blocking/src/i2c_hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,19 @@
//!
//! For non-blocking slave operations, see `openprot-hal-nb::i2c_hardware`.

use embedded_hal::i2c::{AddressMode, Operation, SevenBitAddress};
use embedded_hal::i2c::{AddressMode, ErrorType, Operation, SevenBitAddress};

/// Core I2C hardware interface providing basic operations
///
/// This is the foundation trait that all I2C hardware implementations must provide.
/// It contains only the most basic operations needed for any I2C controller.
pub trait I2cHardwareCore {
/// Hardware-specific error type that implements embedded-hal error traits
type Error: embedded_hal::i2c::Error + core::fmt::Debug;

///
/// The associated error type comes from [`embedded_hal::i2c::ErrorType`] — the
/// same minimal error-type base embedded-hal itself layers capability traits
/// on (and that `i2c_device::I2CCoreTarget` already uses). Capability traits
/// (`I2cSlaveCore`, …) depend only on `ErrorType`, **not** on this whole
/// init/timing/recover contract.
pub trait I2cHardwareCore: ErrorType {
/// Hardware-specific configuration type for I2C initialization and setup
type Config;

Expand Down Expand Up @@ -87,8 +90,20 @@ pub trait I2cHardwareCore {

/// Handle hardware interrupt events (called from ISR)
fn handle_interrupt(&mut self);
}

/// Attempt to recover the I2C bus from stuck conditions
/// Bus recovery — minimal seam for callers that need recovery without the full
/// `I2cHardwareCore` init/timing contract.
///
/// Implemented by hardware drivers that can toggle SCL to un-stick a held-SDA
/// condition. The server-runtime requires this bound so it can recover after
/// bus/arbitration errors without the client needing to know.
pub trait I2cBusRecovery: ErrorType {
/// Attempt to recover the bus from a stuck condition (held SDA/SCL).
///
/// On success the bus is idle and the next transaction can proceed.
/// On error the bus is unrecoverable by software; the caller should
/// propagate the original transfer error to its client.
fn recover_bus(&mut self) -> Result<(), Self::Error>;
}

Expand Down Expand Up @@ -121,9 +136,9 @@ pub trait I2cMaster<A: AddressMode = SevenBitAddress>: I2cHardwareCore {
pub mod slave {
use super::*;

/// I2C slave events that can occur during slave operations
/// I2C slave events raised by the hardware ISR.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum I2cSEvent {
pub enum I2cIsrEvent {
/// Master is requesting to read from slave
SlaveRdReq,
/// Master is requesting to write to slave
Expand All @@ -150,7 +165,7 @@ pub mod slave {
/// Number of bytes in transmit buffer
pub tx_buffer_count: usize,
/// Last slave event that occurred
pub last_event: Option<I2cSEvent>,
pub last_event: Option<I2cIsrEvent>,
/// Whether an error condition exists
pub error: bool,
}
Expand All @@ -160,7 +175,11 @@ pub mod slave {
/// This trait provides the fundamental slave operations that all slave
/// implementations need: setting slave address and enabling/disabling slave mode.
/// This is the minimal trait for any I2C slave implementation.
pub trait I2cSlaveCore<A: AddressMode = SevenBitAddress>: super::I2cHardwareCore {
// Slave operation needs only an error type, not the full
// init/timing/recover contract — mirrors embedded-hal's own
// `I2c: ErrorType` layering. (Previously `: super::I2cHardwareCore`,
// which wrongly forced every slave impl to also be a full controller.)
pub trait I2cSlaveCore<A: AddressMode = SevenBitAddress>: super::ErrorType {
/// Configure the slave address for this I2C controller
fn configure_slave_address(&mut self, addr: A) -> Result<(), Self::Error>;

Expand Down Expand Up @@ -253,7 +272,7 @@ pub mod slave {
/// Returns the most recent slave event, useful for debugging
/// and state tracking. May return None if no events have occurred
/// since reset or if the hardware doesn't track this information.
fn last_slave_event(&self) -> Option<I2cSEvent>;
fn last_slave_event(&self) -> Option<I2cIsrEvent>;
}

/// Blocking slave event handling (sync pattern)
Expand All @@ -270,7 +289,7 @@ pub mod slave {
/// with master transactions.
fn wait_for_slave_event(
&mut self,
expected_event: I2cSEvent,
expected_event: I2cIsrEvent,
timeout_ms: u32,
) -> Result<bool, Self::Error>;

Expand All @@ -279,15 +298,17 @@ pub mod slave {
/// Blocks until any slave event occurs or timeout expires.
/// Returns the event that occurred, or None if timeout expired.
/// Useful when any event needs to be processed synchronously.
fn wait_for_any_event(&mut self, timeout_ms: u32)
-> Result<Option<I2cSEvent>, Self::Error>;
fn wait_for_any_event(
&mut self,
timeout_ms: u32,
) -> Result<Option<I2cIsrEvent>, Self::Error>;

/// Handle a specific slave event with blocking semantics
///
/// Processes a slave event and may block if the event handling
/// requires waiting for hardware completion. This is different
/// from the polling version which always returns immediately.
fn handle_slave_event_blocking(&mut self, event: I2cSEvent) -> Result<(), Self::Error>;
fn handle_slave_event_blocking(&mut self, event: I2cIsrEvent) -> Result<(), Self::Error>;
}

/// Complete slave implementation combining core functionality
Expand Down
50 changes: 25 additions & 25 deletions hal/nb/src/i2c_hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@
//!
//! ```rust,no_run
//! use openprot_hal_nb::i2c_hardware::I2cSlaveEventPolling;
//! use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
//! use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
//!
//! fn poll_slave_events<T: I2cSlaveEventPolling>(slave: &mut T) -> Result<(), T::Error> {
//! // Non-blocking check for events in main loop
//! while let Some(event) = slave.poll_slave_events()? {
//! match event {
//! I2cSEvent::SlaveWrReq => {
//! I2cIsrEvent::SlaveWrReq => {
//! println!("Master wants to write to us");
//! slave.handle_slave_event(event)?;
//! },
//! I2cSEvent::SlaveRdReq => {
//! I2cIsrEvent::SlaveRdReq => {
//! println!("Master wants to read from us");
//! slave.handle_slave_event(event)?;
//! },
//! I2cSEvent::SlaveStop => {
//! I2cIsrEvent::SlaveStop => {
//! println!("Transaction complete");
//! slave.handle_slave_event(event)?;
//! },
Expand All @@ -46,17 +46,17 @@
//!
//! ```rust,no_run
//! use openprot_hal_nb::i2c_hardware::I2cSlaveEventPolling;
//! use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
//! use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
//!
//! // Called from interrupt service routine
//! fn i2c_slave_isr<T: I2cSlaveEventPolling>(slave: &mut T) {
//! // Check specific events without blocking
//! if slave.is_event_pending(I2cSEvent::SlaveWrReq).unwrap_or(false) {
//! let _ = slave.handle_slave_event(I2cSEvent::SlaveWrReq);
//! if slave.is_event_pending(I2cIsrEvent::SlaveWrReq).unwrap_or(false) {
//! let _ = slave.handle_slave_event(I2cIsrEvent::SlaveWrReq);
//! }
//!
//! if slave.is_event_pending(I2cSEvent::SlaveRdReq).unwrap_or(false) {
//! let _ = slave.handle_slave_event(I2cSEvent::SlaveRdReq);
//! if slave.is_event_pending(I2cIsrEvent::SlaveRdReq).unwrap_or(false) {
//! let _ = slave.handle_slave_event(I2cIsrEvent::SlaveRdReq);
//! }
//! }
//! ```
Expand Down Expand Up @@ -85,7 +85,7 @@

use embedded_hal::i2c::{AddressMode, SevenBitAddress};
use openprot_hal_blocking::i2c_hardware::slave::{
I2cSEvent, I2cSlaveBuffer, I2cSlaveCore, I2cSlaveInterrupts,
I2cIsrEvent, I2cSlaveBuffer, I2cSlaveCore, I2cSlaveInterrupts,
};

/// Non-blocking slave event handling (async/polling pattern)
Expand All @@ -100,17 +100,17 @@ use openprot_hal_blocking::i2c_hardware::slave::{
///
/// ```rust,no_run
/// use openprot_hal_nb::i2c_hardware::I2cSlaveEventPolling;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
///
/// fn handle_i2c_events<T: I2cSlaveEventPolling>(slave: &mut T) -> Result<(), T::Error> {
/// // Check for events without blocking
/// if let Some(event) = slave.poll_slave_events()? {
/// match event {
/// I2cSEvent::SlaveWrReq => {
/// I2cIsrEvent::SlaveWrReq => {
/// // Master wants to write - prepare to receive
/// slave.handle_slave_event(event)?;
/// },
/// I2cSEvent::SlaveRdReq => {
/// I2cIsrEvent::SlaveRdReq => {
/// // Master wants to read - prepare data
/// slave.handle_slave_event(event)?;
/// },
Expand All @@ -125,13 +125,13 @@ use openprot_hal_blocking::i2c_hardware::slave::{
///
/// ```rust,no_run
/// use openprot_hal_nb::i2c_hardware::I2cSlaveEventPolling;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
///
/// // Fast ISR that doesn't block
/// fn i2c_isr<T: I2cSlaveEventPolling>(slave: &mut T) {
/// // Quick event check
/// if slave.is_event_pending(I2cSEvent::SlaveWrReq).unwrap_or(false) {
/// let _ = slave.handle_slave_event(I2cSEvent::SlaveWrReq);
/// if slave.is_event_pending(I2cIsrEvent::SlaveWrReq).unwrap_or(false) {
/// let _ = slave.handle_slave_event(I2cIsrEvent::SlaveWrReq);
/// }
/// }
/// ```
Expand All @@ -152,7 +152,7 @@ pub trait I2cSlaveEventPolling<A: AddressMode = SevenBitAddress>: I2cSlaveInterr
///
/// ```rust,no_run
/// use openprot_hal_nb::i2c_hardware::I2cSlaveEventPolling;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
///
/// fn check_events<T: I2cSlaveEventPolling>(slave: &mut T) -> Result<(), T::Error> {
/// if let Some(event) = slave.poll_slave_events()? {
Expand All @@ -163,7 +163,7 @@ pub trait I2cSlaveEventPolling<A: AddressMode = SevenBitAddress>: I2cSlaveInterr
/// Ok(())
/// }
/// ```
fn poll_slave_events(&mut self) -> Result<Option<I2cSEvent>, Self::Error>;
fn poll_slave_events(&mut self) -> Result<Option<I2cIsrEvent>, Self::Error>;

/// Handle a specific slave event (called from ISR or event loop)
///
Expand All @@ -183,15 +183,15 @@ pub trait I2cSlaveEventPolling<A: AddressMode = SevenBitAddress>: I2cSlaveInterr
///
/// ```rust,no_run
/// use openprot_hal_nb::i2c_hardware::I2cSlaveEventPolling;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
///
/// fn handle_event<T: I2cSlaveEventPolling>(slave: &mut T) -> Result<(), T::Error> {
/// slave.handle_slave_event(I2cSEvent::SlaveWrReq)?;
/// slave.handle_slave_event(I2cIsrEvent::SlaveWrReq)?;
/// println!("Write request handled");
/// Ok(())
/// }
/// ```
fn handle_slave_event(&mut self, event: I2cSEvent) -> Result<(), Self::Error>;
fn handle_slave_event(&mut self, event: I2cIsrEvent) -> Result<(), Self::Error>;

/// Non-blocking check if a specific event is pending
///
Expand All @@ -212,16 +212,16 @@ pub trait I2cSlaveEventPolling<A: AddressMode = SevenBitAddress>: I2cSlaveInterr
///
/// ```rust,no_run
/// use openprot_hal_nb::i2c_hardware::I2cSlaveEventPolling;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
///
/// fn check_specific_event<T: I2cSlaveEventPolling>(slave: &T) -> Result<(), T::Error> {
/// if slave.is_event_pending(I2cSEvent::SlaveWrReq)? {
/// if slave.is_event_pending(I2cIsrEvent::SlaveWrReq)? {
/// println!("Write request is pending");
/// }
/// Ok(())
/// }
/// ```
fn is_event_pending(&self, event: I2cSEvent) -> Result<bool, Self::Error>;
fn is_event_pending(&self, event: I2cIsrEvent) -> Result<bool, Self::Error>;
}

/// Complete non-blocking slave implementation
Expand Down Expand Up @@ -265,7 +265,7 @@ pub trait I2cSlaveEventPolling<A: AddressMode = SevenBitAddress>: I2cSlaveInterr
///
/// ```rust,no_run
/// use openprot_hal_nb::i2c_hardware::I2cSlaveNonBlocking;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cSEvent;
/// use openprot_hal_blocking::i2c_hardware::slave::I2cIsrEvent;
///
/// fn main_loop<T: I2cSlaveNonBlocking>(mut slave: T) -> Result<(), T::Error> {
/// loop {
Expand Down
Loading