Skip to content

Commit

Permalink
Add error traits for communication interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
eldruin committed Aug 31, 2021
1 parent 47354c8 commit 4e8dbb3
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 42 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `IoPin` trait for pins that can change between being inputs or outputs
dynamically.
- Added `Debug` to all spi mode types.
- `Error` traits for SPI, I2C and Serial traits. The error types used in those must
implement these `Error` traits, which implies providing a conversion to a common
set of error kinds. Generic drivers using these interfaces can then convert the errors
to this common set to act upon them.

### Changed
- Swap PWM channel arguments to references
Expand Down
3 changes: 2 additions & 1 deletion src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
//! TODO write example of usage
use core::fmt::{Result, Write};

impl<Word, Error> Write for dyn crate::serial::nb::Write<Word, Error = Error> + '_
impl<Word, Error: crate::serial::Error> Write
for dyn crate::serial::nb::Write<Word, Error = Error> + '_
where
Word: From<u8>,
{
Expand Down
82 changes: 67 additions & 15 deletions src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
//! Here is an example of an embedded-hal implementation of the `Write` trait
//! for both modes:
//! ```
//! # use embedded_hal::i2c::{SevenBitAddress, TenBitAddress, blocking::Write};
//! # use embedded_hal::i2c::{ErrorKind, SevenBitAddress, TenBitAddress, blocking::Write};
//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing.
//! pub struct I2c0;
//!
//! impl Write<SevenBitAddress> for I2c0
//! {
//! # type Error = ();
//! # type Error = ErrorKind;
//! #
//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> {
//! // ...
Expand All @@ -42,7 +42,7 @@
//!
//! impl Write<TenBitAddress> for I2c0
//! {
//! # type Error = ();
//! # type Error = ErrorKind;
//! #
//! fn write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> {
//! // ...
Expand All @@ -56,14 +56,14 @@
//! For demonstration purposes the address mode parameter has been omitted in this example.
//!
//! ```
//! # use embedded_hal::i2c::blocking::WriteRead;
//! # use embedded_hal::i2c::{blocking::WriteRead, Error};
//! const ADDR: u8 = 0x15;
//! # const TEMP_REGISTER: u8 = 0x1;
//! pub struct TemperatureSensorDriver<I2C> {
//! i2c: I2C,
//! }
//!
//! impl<I2C, E> TemperatureSensorDriver<I2C>
//! impl<I2C, E: Error> TemperatureSensorDriver<I2C>
//! where
//! I2C: WriteRead<Error = E>,
//! {
Expand All @@ -79,14 +79,14 @@
//! ### Device driver compatible only with 10-bit addresses
//!
//! ```
//! # use embedded_hal::i2c::{TenBitAddress, blocking::WriteRead};
//! # use embedded_hal::i2c::{Error, TenBitAddress, blocking::WriteRead};
//! const ADDR: u16 = 0x158;
//! # const TEMP_REGISTER: u8 = 0x1;
//! pub struct TemperatureSensorDriver<I2C> {
//! i2c: I2C,
//! }
//!
//! impl<I2C, E> TemperatureSensorDriver<I2C>
//! impl<I2C, E: Error> TemperatureSensorDriver<I2C>
//! where
//! I2C: WriteRead<TenBitAddress, Error = E>,
//! {
Expand All @@ -101,6 +101,58 @@
use crate::private;

/// I2C error
pub trait Error: core::fmt::Debug {
/// Convert error to a generic I2C error kind
///
/// By using this method, I2C errors freely defined by HAL implementations
/// can be converted to a set of generic I2C errors upon which generic
/// code can act.
fn kind(&self) -> ErrorKind;
}

/// I2C error kind
///
/// This represents a common set of I2C operation errors. HAL implementations are
/// free to define more specific or additional error types. However, by providing
/// a mapping to these common I2C errors, generic code can still react to them.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
/// An unspecific bus error occurred
Bus,
/// The arbitration was lost, e.g. electrical problems with the clock signal
ArbitrationLoss,
/// A bus operation was not acknowledged, e.g. due to the addressed device not being available on
/// the bus or the device not being ready to process requests at the moment
NoAcknowledge,
/// The peripheral receive buffer was overrun
Overrun,
/// A different error occurred. The original error may contain more information.
Other,
}

impl Error for ErrorKind {
fn kind(&self) -> ErrorKind {
*self
}
}

impl core::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Bus => write!(f, "An unspecific bus error occurred"),
Self::ArbitrationLoss => write!(f, "The arbitration was lost"),
Self::NoAcknowledge => write!(f, "A bus operation was not acknowledged"),
Self::Overrun => write!(f, "The peripheral receive buffer was overrun"),
Self::Other => write!(
f,
"A different error occurred. The original error may contain more information"
),
}
}
}

/// Address mode (7-bit / 10-bit)
///
/// Note: This trait is sealed and should not be implemented outside of this crate.
Expand All @@ -119,12 +171,12 @@ impl AddressMode for TenBitAddress {}
/// Blocking I2C traits
pub mod blocking {

use super::{AddressMode, SevenBitAddress};
use super::{AddressMode, Error, SevenBitAddress};

/// Blocking read
pub trait Read<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
type Error: Error;

/// Reads enough bytes from slave with `address` to fill `buffer`
///
Expand All @@ -150,7 +202,7 @@ pub mod blocking {
/// Blocking write
pub trait Write<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
type Error: Error;

/// Writes bytes to slave with address `address`
///
Expand All @@ -174,7 +226,7 @@ pub mod blocking {
/// Blocking write (iterator version)
pub trait WriteIter<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
type Error: Error;

/// Writes bytes to slave with address `address`
///
Expand All @@ -189,7 +241,7 @@ pub mod blocking {
/// Blocking write + read
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
type Error: Error;

/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
/// single transaction*
Expand Down Expand Up @@ -224,7 +276,7 @@ pub mod blocking {
/// Blocking write (iterator version) + read
pub trait WriteIterRead<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
type Error: Error;

/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
/// single transaction*
Expand Down Expand Up @@ -258,7 +310,7 @@ pub mod blocking {
/// This allows combining operations within an I2C transaction.
pub trait Transactional<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
type Error: Error;

/// Execute the provided operations on the I2C bus.
///
Expand All @@ -285,7 +337,7 @@ pub mod blocking {
/// This allows combining operation within an I2C transaction.
pub trait TransactionalIter<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
type Error: Error;

/// Execute the provided operations on the I2C bus (iterator version).
///
Expand Down
29 changes: 11 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,23 +143,17 @@
//! // convenience type alias
//! pub type Serial1 = Serial<USART1>;
//!
//! /// Serial interface error
//! pub enum Error {
//! /// Buffer overrun
//! Overrun,
//! // omitted: other error variants
//! }
//!
//! impl hal::serial::nb::Read<u8> for Serial<USART1> {
//! type Error = Error;
//! type Error = hal::serial::ErrorKind;
//!
//! fn read(&mut self) -> nb::Result<u8, Error> {
//! fn read(&mut self) -> nb::Result<u8, Self::Error> {
//! // read the status register
//! let isr = self.usart.sr.read();
//!
//! if isr.ore().bit_is_set() {
//! // Error: Buffer overrun
//! Err(nb::Error::Other(Error::Overrun))
//! Err(nb::Error::Other(Self::Error::Overrun))
//! }
//! // omitted: checks for other errors
//! else if isr.rxne().bit_is_set() {
Expand All @@ -173,14 +167,14 @@
//! }
//!
//! impl hal::serial::nb::Write<u8> for Serial<USART1> {
//! type Error = Error;
//! type Error = hal::serial::ErrorKind;
//!
//! fn write(&mut self, byte: u8) -> nb::Result<(), Error> {
//! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
//! // Similar to the `read` implementation
//! # Ok(())
//! }
//!
//! fn flush(&mut self) -> nb::Result<(), Error> {
//! fn flush(&mut self) -> nb::Result<(), Self::Error> {
//! // Similar to the `read` implementation
//! # Ok(())
//! }
Expand Down Expand Up @@ -326,12 +320,11 @@
//! use embedded_hal as hal;
//! use hal::nb;
//!
//! use hal::serial::nb::Write;
//! use ::core::convert::Infallible;
//! use hal::serial::{ErrorKind, nb::Write};
//!
//! fn flush<S>(serial: &mut S, cb: &mut CircularBuffer)
//! where
//! S: hal::serial::nb::Write<u8, Error = Infallible>,
//! S: hal::serial::nb::Write<u8, Error = ErrorKind>,
//! {
//! loop {
//! if let Some(byte) = cb.peek() {
Expand Down Expand Up @@ -396,9 +389,9 @@
//! # }
//! # struct Serial1;
//! # impl hal::serial::nb::Write<u8> for Serial1 {
//! # type Error = Infallible;
//! # fn write(&mut self, _: u8) -> nb::Result<(), Infallible> { Err(::nb::Error::WouldBlock) }
//! # fn flush(&mut self) -> nb::Result<(), Infallible> { Err(::nb::Error::WouldBlock) }
//! # type Error = ErrorKind;
//! # fn write(&mut self, _: u8) -> nb::Result<(), Self::Error> { Err(::nb::Error::WouldBlock) }
//! # fn flush(&mut self) -> nb::Result<(), Self::Error> { Err(::nb::Error::WouldBlock) }
//! # }
//! # struct CircularBuffer;
//! # impl CircularBuffer {
Expand Down
2 changes: 1 addition & 1 deletion src/serial/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/// Write half of a serial interface (blocking variant)
pub trait Write<Word> {
/// The type of error that can occur when writing
type Error;
type Error: crate::serial::Error;

/// Writes a slice, blocking until everything has been written
///
Expand Down
55 changes: 55 additions & 0 deletions src/serial/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,58 @@
pub mod blocking;
pub mod nb;

/// Serial error
pub trait Error: core::fmt::Debug {
/// Convert error to a generic serial error kind
///
/// By using this method, serial errors freely defined by HAL implementations
/// can be converted to a set of generic serial errors upon which generic
/// code can act.
fn kind(&self) -> ErrorKind;
}

/// Serial error kind
///
/// This represents a common set of serial operation errors. HAL implementations are
/// free to define more specific or additional error types. However, by providing
/// a mapping to these common serial errors, generic code can still react to them.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
/// The peripheral receive buffer was overrun.
Overrun,
/// Received data does not conform to the peripheral configuration.
/// Can be caused by a misconfigured device on either end of the serial line.
FrameFormat,
/// Parity check failed.
Parity,
/// Serial line is too noisy to read valid data.
Noise,
/// A different error occurred. The original error may contain more information.
Other,
}

impl Error for ErrorKind {
fn kind(&self) -> ErrorKind {
*self
}
}

impl core::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Overrun => write!(f, "The peripheral receive buffer was overrun"),
Self::Parity => write!(f, "Parity check failed"),
Self::Noise => write!(f, "Serial line is too noisy to read valid data"),
Self::FrameFormat => write!(
f,
"Received data does not conform to the peripheral configuration"
),
Self::Other => write!(
f,
"A different error occurred. The original error may contain more information"
),
}
}
}
4 changes: 2 additions & 2 deletions src/serial/nb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/// This can be encoded in this trait via the `Word` type parameter.
pub trait Read<Word> {
/// Read error
type Error;
type Error: crate::serial::Error;

/// Reads a single word from the serial interface
fn read(&mut self) -> nb::Result<Word, Self::Error>;
Expand All @@ -15,7 +15,7 @@ pub trait Read<Word> {
/// Write half of a serial interface
pub trait Write<Word> {
/// Write error
type Error;
type Error: crate::serial::Error;

/// Writes a single word to the serial interface
fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>;
Expand Down
Loading

0 comments on commit 4e8dbb3

Please sign in to comment.