Skip to content

Commit b72255c

Browse files
bors[bot]eldruin
andauthored
Merge #296
296: Add error traits for communication interfaces r=therealprof a=eldruin There has been a lengthy discussion about this in #229. This implements proposal 3 by `@TeXitoi` which was the latest for I2C, SPI and Serial interfaces. See [here for the analysis](#229 (comment)) I included a minimal number of variants for each protocol, mostly borrowed from `embedded-error`. This raises the MSRV to Rust 1.40.0 as well in order to mark the `ErrorKind`s as `#[non_exhaustive]` so that adding a new variant is not a breaking change. I am also fine with _not_ marking the `ErrorKind`s as `#[non_exhaustive]`. Closes #229 Co-authored-by: Diego Barrios Romero <[email protected]>
2 parents d6b1c65 + d870969 commit b72255c

File tree

14 files changed

+209
-50
lines changed

14 files changed

+209
-50
lines changed

.github/bors.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ status = [
55
"ci-linux (stable, x86_64-unknown-linux-gnu)",
66
"ci-linux (stable, thumbv6m-none-eabi)",
77
"ci-linux (stable, thumbv7m-none-eabi)",
8-
"ci-linux (1.35.0, x86_64-unknown-linux-gnu)",
8+
"ci-linux (1.40.0, x86_64-unknown-linux-gnu)",
99
"ci-linux-test (stable)",
10-
"ci-linux-test (1.36.0, x86_64-unknown-linux-gnu)",
10+
"ci-linux-test (1.40.0, x86_64-unknown-linux-gnu)",
1111
"fmt",
1212
]

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121

2222
include:
2323
# Test MSRV
24-
- rust: 1.35.0
24+
- rust: 1.40.0
2525
TARGET: x86_64-unknown-linux-gnu
2626

2727
# Test nightly but don't fail

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
rust: [stable]
1717

1818
include:
19-
- rust: 1.36.0 # Higher than the MSRV due to dependencies.
19+
- rust: 1.40.0
2020
TARGET: x86_64-unknown-linux-gnu
2121

2222
# Test nightly but don't fail

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1717
### Added
1818
- Added `IoPin` trait for pins that can change between being inputs or outputs
1919
dynamically.
20+
- `Error` traits for SPI, I2C and Serial traits. The error types used in those must
21+
implement these `Error` traits, which implies providing a conversion to a common
22+
set of error kinds. Generic drivers using these interfaces can then convert the errors
23+
to this common set to act upon them.
2024
- Added `Debug` to all spi mode types.
2125
- Add impls of all traits for references (`&T` or `&mut T` depending on the trait) when `T` implements the trait.
2226

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[![crates.io](https://img.shields.io/crates/d/embedded-hal.svg)](https://crates.io/crates/embedded-hal)
22
[![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal)
33
[![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal)
4-
![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.35+-blue.svg)
4+
![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.40+-blue.svg)
55

66
# `embedded-hal`
77

@@ -108,7 +108,7 @@ As stated before, `embedded-hal` `-alpha` versions are _not guaranteed_ to be co
108108

109109
## Minimum Supported Rust Version (MSRV)
110110

111-
This crate is guaranteed to compile on stable Rust 1.35 and up. It *might*
111+
This crate is guaranteed to compile on stable Rust 1.40 and up. It *might*
112112
compile with older versions but that may change in any new patch release.
113113

114114
## License

src/fmt.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
//! TODO write example of usage
44
use core::fmt::{Result, Write};
55

6-
impl<Word, Error: core::fmt::Debug> Write for dyn crate::serial::nb::Write<Word, Error = Error> + '_
6+
impl<Word, Error: crate::serial::Error> Write
7+
for dyn crate::serial::nb::Write<Word, Error = Error> + '_
78
where
89
Word: From<u8>,
910
{

src/i2c.rs

+69-15
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
//! Here is an example of an embedded-hal implementation of the `Write` trait
2727
//! for both modes:
2828
//! ```
29-
//! # use embedded_hal::i2c::{SevenBitAddress, TenBitAddress, blocking::Write};
29+
//! # use embedded_hal::i2c::{ErrorKind, SevenBitAddress, TenBitAddress, blocking::Write};
3030
//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing.
3131
//! pub struct I2c0;
3232
//!
3333
//! impl Write<SevenBitAddress> for I2c0
3434
//! {
35-
//! # type Error = ();
35+
//! # type Error = ErrorKind;
3636
//! #
3737
//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> {
3838
//! // ...
@@ -42,7 +42,7 @@
4242
//!
4343
//! impl Write<TenBitAddress> for I2c0
4444
//! {
45-
//! # type Error = ();
45+
//! # type Error = ErrorKind;
4646
//! #
4747
//! fn write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> {
4848
//! // ...
@@ -56,14 +56,14 @@
5656
//! For demonstration purposes the address mode parameter has been omitted in this example.
5757
//!
5858
//! ```
59-
//! # use embedded_hal::i2c::blocking::WriteRead;
59+
//! # use embedded_hal::i2c::{blocking::WriteRead, Error};
6060
//! const ADDR: u8 = 0x15;
6161
//! # const TEMP_REGISTER: u8 = 0x1;
6262
//! pub struct TemperatureSensorDriver<I2C> {
6363
//! i2c: I2C,
6464
//! }
6565
//!
66-
//! impl<I2C, E: core::fmt::Debug> TemperatureSensorDriver<I2C>
66+
//! impl<I2C, E: Error> TemperatureSensorDriver<I2C>
6767
//! where
6868
//! I2C: WriteRead<Error = E>,
6969
//! {
@@ -79,14 +79,14 @@
7979
//! ### Device driver compatible only with 10-bit addresses
8080
//!
8181
//! ```
82-
//! # use embedded_hal::i2c::{TenBitAddress, blocking::WriteRead};
82+
//! # use embedded_hal::i2c::{Error, TenBitAddress, blocking::WriteRead};
8383
//! const ADDR: u16 = 0x158;
8484
//! # const TEMP_REGISTER: u8 = 0x1;
8585
//! pub struct TemperatureSensorDriver<I2C> {
8686
//! i2c: I2C,
8787
//! }
8888
//!
89-
//! impl<I2C, E: core::fmt::Debug> TemperatureSensorDriver<I2C>
89+
//! impl<I2C, E: Error> TemperatureSensorDriver<I2C>
9090
//! where
9191
//! I2C: WriteRead<TenBitAddress, Error = E>,
9292
//! {
@@ -101,6 +101,60 @@
101101
102102
use crate::private;
103103

104+
/// I2C error
105+
pub trait Error: core::fmt::Debug {
106+
/// Convert error to a generic I2C error kind
107+
///
108+
/// By using this method, I2C errors freely defined by HAL implementations
109+
/// can be converted to a set of generic I2C errors upon which generic
110+
/// code can act.
111+
fn kind(&self) -> ErrorKind;
112+
}
113+
114+
/// I2C error kind
115+
///
116+
/// This represents a common set of I2C operation errors. HAL implementations are
117+
/// free to define more specific or additional error types. However, by providing
118+
/// a mapping to these common I2C errors, generic code can still react to them.
119+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
120+
#[non_exhaustive]
121+
pub enum ErrorKind {
122+
/// Bus error occurred. e.g. A START or a STOP condition is detected and is not located after a multiple of 9 SCL clock pulses.
123+
Bus,
124+
/// The arbitration was lost, e.g. electrical problems with the clock signal
125+
ArbitrationLoss,
126+
/// The device did not acknowledge its address. The device may be missing.
127+
NoAcknowledgeAddress,
128+
/// The device did not acknowled the data. It may not be ready to process requests at the moment.
129+
NoAcknowledgeData,
130+
/// The peripheral receive buffer was overrun
131+
Overrun,
132+
/// A different error occurred. The original error may contain more information.
133+
Other,
134+
}
135+
136+
impl Error for ErrorKind {
137+
fn kind(&self) -> ErrorKind {
138+
*self
139+
}
140+
}
141+
142+
impl core::fmt::Display for ErrorKind {
143+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
144+
match self {
145+
Self::Bus => write!(f, "Bus error occurred"),
146+
Self::ArbitrationLoss => write!(f, "The arbitration was lost"),
147+
Self::NoAcknowledgeAddress => write!(f, "The device did not acknowledge its address"),
148+
Self::NoAcknowledgeData => write!(f, "The device did not acknowledge the data"),
149+
Self::Overrun => write!(f, "The peripheral receive buffer was overrun"),
150+
Self::Other => write!(
151+
f,
152+
"A different error occurred. The original error may contain more information"
153+
),
154+
}
155+
}
156+
}
157+
104158
/// Address mode (7-bit / 10-bit)
105159
///
106160
/// Note: This trait is sealed and should not be implemented outside of this crate.
@@ -119,12 +173,12 @@ impl AddressMode for TenBitAddress {}
119173
/// Blocking I2C traits
120174
pub mod blocking {
121175

122-
use super::{AddressMode, SevenBitAddress};
176+
use super::{AddressMode, Error, SevenBitAddress};
123177

124178
/// Blocking read
125179
pub trait Read<A: AddressMode = SevenBitAddress> {
126180
/// Error type
127-
type Error: core::fmt::Debug;
181+
type Error: Error;
128182

129183
/// Reads enough bytes from slave with `address` to fill `buffer`
130184
///
@@ -158,7 +212,7 @@ pub mod blocking {
158212
/// Blocking write
159213
pub trait Write<A: AddressMode = SevenBitAddress> {
160214
/// Error type
161-
type Error: core::fmt::Debug;
215+
type Error: Error;
162216

163217
/// Writes bytes to slave with address `address`
164218
///
@@ -190,7 +244,7 @@ pub mod blocking {
190244
/// Blocking write (iterator version)
191245
pub trait WriteIter<A: AddressMode = SevenBitAddress> {
192246
/// Error type
193-
type Error: core::fmt::Debug;
247+
type Error: Error;
194248

195249
/// Writes bytes to slave with address `address`
196250
///
@@ -216,7 +270,7 @@ pub mod blocking {
216270
/// Blocking write + read
217271
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
218272
/// Error type
219-
type Error: core::fmt::Debug;
273+
type Error: Error;
220274

221275
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
222276
/// single transaction*
@@ -264,7 +318,7 @@ pub mod blocking {
264318
/// Blocking write (iterator version) + read
265319
pub trait WriteIterRead<A: AddressMode = SevenBitAddress> {
266320
/// Error type
267-
type Error: core::fmt::Debug;
321+
type Error: Error;
268322

269323
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
270324
/// single transaction*
@@ -314,7 +368,7 @@ pub mod blocking {
314368
/// This allows combining operations within an I2C transaction.
315369
pub trait Transactional<A: AddressMode = SevenBitAddress> {
316370
/// Error type
317-
type Error: core::fmt::Debug;
371+
type Error: Error;
318372

319373
/// Execute the provided operations on the I2C bus.
320374
///
@@ -353,7 +407,7 @@ pub mod blocking {
353407
/// This allows combining operation within an I2C transaction.
354408
pub trait TransactionalIter<A: AddressMode = SevenBitAddress> {
355409
/// Error type
356-
type Error: core::fmt::Debug;
410+
type Error: Error;
357411

358412
/// Execute the provided operations on the I2C bus (iterator version).
359413
///

src/lib.rs

+11-20
Original file line numberDiff line numberDiff line change
@@ -143,24 +143,16 @@
143143
//! // convenience type alias
144144
//! pub type Serial1 = Serial<USART1>;
145145
//!
146-
//! /// Serial interface error
147-
//! #[derive(Debug)]
148-
//! pub enum Error {
149-
//! /// Buffer overrun
150-
//! Overrun,
151-
//! // omitted: other error variants
152-
//! }
153-
//!
154146
//! impl hal::serial::nb::Read<u8> for Serial<USART1> {
155-
//! type Error = Error;
147+
//! type Error = hal::serial::ErrorKind;
156148
//!
157-
//! fn read(&mut self) -> nb::Result<u8, Error> {
149+
//! fn read(&mut self) -> nb::Result<u8, Self::Error> {
158150
//! // read the status register
159151
//! let isr = self.usart.sr.read();
160152
//!
161153
//! if isr.ore().bit_is_set() {
162154
//! // Error: Buffer overrun
163-
//! Err(nb::Error::Other(Error::Overrun))
155+
//! Err(nb::Error::Other(Self::Error::Overrun))
164156
//! }
165157
//! // omitted: checks for other errors
166158
//! else if isr.rxne().bit_is_set() {
@@ -174,14 +166,14 @@
174166
//! }
175167
//!
176168
//! impl hal::serial::nb::Write<u8> for Serial<USART1> {
177-
//! type Error = Error;
169+
//! type Error = hal::serial::ErrorKind;
178170
//!
179-
//! fn write(&mut self, byte: u8) -> nb::Result<(), Error> {
171+
//! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
180172
//! // Similar to the `read` implementation
181173
//! # Ok(())
182174
//! }
183175
//!
184-
//! fn flush(&mut self) -> nb::Result<(), Error> {
176+
//! fn flush(&mut self) -> nb::Result<(), Self::Error> {
185177
//! // Similar to the `read` implementation
186178
//! # Ok(())
187179
//! }
@@ -327,12 +319,11 @@
327319
//! use embedded_hal as hal;
328320
//! use hal::nb;
329321
//!
330-
//! use hal::serial::nb::Write;
331-
//! use ::core::convert::Infallible;
322+
//! use hal::serial::{ErrorKind, nb::Write};
332323
//!
333324
//! fn flush<S>(serial: &mut S, cb: &mut CircularBuffer)
334325
//! where
335-
//! S: hal::serial::nb::Write<u8, Error = Infallible>,
326+
//! S: hal::serial::nb::Write<u8, Error = ErrorKind>,
336327
//! {
337328
//! loop {
338329
//! if let Some(byte) = cb.peek() {
@@ -397,9 +388,9 @@
397388
//! # }
398389
//! # struct Serial1;
399390
//! # impl hal::serial::nb::Write<u8> for Serial1 {
400-
//! # type Error = Infallible;
401-
//! # fn write(&mut self, _: u8) -> nb::Result<(), Infallible> { Err(::nb::Error::WouldBlock) }
402-
//! # fn flush(&mut self) -> nb::Result<(), Infallible> { Err(::nb::Error::WouldBlock) }
391+
//! # type Error = ErrorKind;
392+
//! # fn write(&mut self, _: u8) -> nb::Result<(), Self::Error> { Err(::nb::Error::WouldBlock) }
393+
//! # fn flush(&mut self) -> nb::Result<(), Self::Error> { Err(::nb::Error::WouldBlock) }
403394
//! # }
404395
//! # struct CircularBuffer;
405396
//! # impl CircularBuffer {

src/serial/blocking.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/// Write half of a serial interface (blocking variant)
88
pub trait Write<Word> {
99
/// The type of error that can occur when writing
10-
type Error: core::fmt::Debug;
10+
type Error: crate::serial::Error;
1111

1212
/// Writes a slice, blocking until everything has been written
1313
///

src/serial/mod.rs

+55
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,58 @@
22
33
pub mod blocking;
44
pub mod nb;
5+
6+
/// Serial error
7+
pub trait Error: core::fmt::Debug {
8+
/// Convert error to a generic serial error kind
9+
///
10+
/// By using this method, serial errors freely defined by HAL implementations
11+
/// can be converted to a set of generic serial errors upon which generic
12+
/// code can act.
13+
fn kind(&self) -> ErrorKind;
14+
}
15+
16+
/// Serial error kind
17+
///
18+
/// This represents a common set of serial operation errors. HAL implementations are
19+
/// free to define more specific or additional error types. However, by providing
20+
/// a mapping to these common serial errors, generic code can still react to them.
21+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
22+
#[non_exhaustive]
23+
pub enum ErrorKind {
24+
/// The peripheral receive buffer was overrun.
25+
Overrun,
26+
/// Received data does not conform to the peripheral configuration.
27+
/// Can be caused by a misconfigured device on either end of the serial line.
28+
FrameFormat,
29+
/// Parity check failed.
30+
Parity,
31+
/// Serial line is too noisy to read valid data.
32+
Noise,
33+
/// A different error occurred. The original error may contain more information.
34+
Other,
35+
}
36+
37+
impl Error for ErrorKind {
38+
fn kind(&self) -> ErrorKind {
39+
*self
40+
}
41+
}
42+
43+
impl core::fmt::Display for ErrorKind {
44+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45+
match self {
46+
Self::Overrun => write!(f, "The peripheral receive buffer was overrun"),
47+
Self::Parity => write!(f, "Parity check failed"),
48+
Self::Noise => write!(f, "Serial line is too noisy to read valid data"),
49+
Self::FrameFormat => write!(
50+
f,
51+
"Received data does not conform to the peripheral configuration"
52+
),
53+
Self::Other => write!(
54+
f,
55+
"A different error occurred. The original error may contain more information"
56+
),
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)