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 f16b10c
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 12 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
52 changes: 52 additions & 0 deletions src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 Down
18 changes: 6 additions & 12 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
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"
),
}
}
}
60 changes: 60 additions & 0 deletions src/spi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,63 @@ pub const MODE_3: Mode = Mode {
polarity: Polarity::IdleHigh,
phase: Phase::CaptureOnSecondTransition,
};

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

/// SPI error kind
///
/// This represents a common set of SPI operation errors. HAL implementations are
/// free to define more specific or additional error types. However, by providing
/// a mapping to these common SPI 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 peripheral receive buffer was overrun
Overrun,
/// Multiple devices on the SPI bus are trying across each other, e.g. in a multi-master setup
ModeFault,
/// CRC does not match the received data
Crc,
/// Received data does not conform to the peripheral configuration
FrameFormat,
/// 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::Overrun => write!(f, "The peripheral receive buffer was overrun"),
Self::ModeFault => write!(
f,
"Multiple devices on the SPI bus are trying across each other"
),
Self::Crc => write!(f, "CRC does not match the received 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"
),
}
}
}

0 comments on commit f16b10c

Please sign in to comment.