Skip to content

[Discussion] Non-blocking I2C Trait #50

Open
@austinglaser

Description

@austinglaser

It's unfortunate that there's no nonblocking variant of the i2c HAL traits -- but that could easily change.

My first impression is that it should look something like:

//! Inter-integrated Circuit Interface

use nb;

/// Possible I2C errors
pub enum Error<E> {
    /// An implementation-specific error ocurred
    Other(E),

    /// Another master owns the bus
    ArbitrationLost,

    /// The slave did not acknowledge an address or data byte
    ByteNacked,

    /// An operation was attempted at an invalid time
    ///
    /// For example, an attempt to `send()` a byte before `send_address()` is called will fail with
    /// this error
    InvalidOperation,

    #[doc(hidden)]
    _Extensible,
}

/// Read or write access to the peripheral
pub enum AccessType {
    Read,
    Write,
}

pub trait Master {
    /// An enumeration of implementation-specific I2C errors
    type ImplError;

    /// Drive a start condition on the bus
    ///
    /// Returns an `Error` if the system is in an invalid state.
    ///
    /// May be used to generate a repeated start condition -- however, at minimum `send_address()`
    /// must be called between the start conditions.
    ///
    /// TODO: Does I2C require at least byte transferred before the repeated start? This is by far
    /// the most common use-case
    fn send_start(&mut self) -> nb::Result<(), Error<Self::ImplError>>;

    /// Drive a start condition on the bus
    ///
    /// Returns an `Error` if the system is in an invalid state
    fn send_stop(&mut self) -> nb::Result<(), Error<Self::ImplError>>;

    /// Send a peripheral address
    ///
    /// Must be the first operation after generating a start condition.
    fn send_address(
        &mut self,
        address: u8,
        access: AccessType,
    ) -> nb::Result<(), Error<Self::ImplError>>;

    /// Send a byte to the peripheral
    ///
    /// May be called several times after sending an address with `AccessType::Write`. May not be
    /// called after sending an address with `AccessType::Read`
    fn send(&mut self, byte: u8) -> nb::Result<(), Error<Self::ImplError>>;

    /// Send a byte to the peripheral
    ///
    /// May be called several times after sending an address with `AccessType::Read`. May not be
    /// called after sending an address with `AccessType::Write`
    fn read(&mut self, send_ack: bool) -> nb::Result<u8, Error<Self::ImplError>>;
}

If this is stabilized, there could be default implementations of blocking::i2c in terms of it, simplifying driver writers' lives.

Alternatives

The nonblocking trait could operate at a higher-level (basically just mirroring the traits exposed by blocking::i2c except with a nb::Result. The above trait has lots of potential for runtime errors, which may be undesirable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions