Skip to content

Commit f7ab7b1

Browse files
committed
Add error traits for communication interfaces
1 parent febd786 commit f7ab7b1

File tree

10 files changed

+207
-44
lines changed

10 files changed

+207
-44
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1111
- Added `IoPin` trait for pins that can change between being inputs or outputs
1212
dynamically.
1313
- Added `Debug` to all spi mode types.
14+
- `Error` traits for SPI, I2C and Serial traits. The error types used in those must
15+
implement these `Error` traits, which implies providing a conversion to a common
16+
set of error kinds. Generic drivers using these interfaces can then convert the errors
17+
to this common set to act upon them.
1418

1519
### Changed
1620
- Swap PWM channel arguments to references

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

+67-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,58 @@
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+
/// An unspecific bus error occurred
123+
Bus,
124+
/// The arbitration was lost, e.g. electrical problems with the clock signal
125+
ArbitrationLoss,
126+
/// A bus operation was not acknowledged, e.g. due to the addressed device not being available on
127+
/// the bus or the device not being ready to process requests at the moment
128+
NoAcknowledge,
129+
/// The peripheral receive buffer was overrun
130+
Overrun,
131+
/// A different error occurred. The original error may contain more information.
132+
Other,
133+
}
134+
135+
impl Error for ErrorKind {
136+
fn kind(&self) -> ErrorKind {
137+
*self
138+
}
139+
}
140+
141+
impl core::fmt::Display for ErrorKind {
142+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
143+
match self {
144+
Self::Bus => write!(f, "An unspecific bus error occurred"),
145+
Self::ArbitrationLoss => write!(f, "The arbitration was lost"),
146+
Self::NoAcknowledge => write!(f, "A bus operation was not acknowledged"),
147+
Self::Overrun => write!(f, "The peripheral receive buffer was overrun"),
148+
Self::Other => write!(
149+
f,
150+
"A different error occurred. The original error may contain more information"
151+
),
152+
}
153+
}
154+
}
155+
104156
/// Address mode (7-bit / 10-bit)
105157
///
106158
/// Note: This trait is sealed and should not be implemented outside of this crate.
@@ -119,12 +171,12 @@ impl AddressMode for TenBitAddress {}
119171
/// Blocking I2C traits
120172
pub mod blocking {
121173

122-
use super::{AddressMode, SevenBitAddress};
174+
use super::{AddressMode, Error, SevenBitAddress};
123175

124176
/// Blocking read
125177
pub trait Read<A: AddressMode = SevenBitAddress> {
126178
/// Error type
127-
type Error: core::fmt::Debug;
179+
type Error: Error;
128180

129181
/// Reads enough bytes from slave with `address` to fill `buffer`
130182
///
@@ -150,7 +202,7 @@ pub mod blocking {
150202
/// Blocking write
151203
pub trait Write<A: AddressMode = SevenBitAddress> {
152204
/// Error type
153-
type Error: core::fmt::Debug;
205+
type Error: Error;
154206

155207
/// Writes bytes to slave with address `address`
156208
///
@@ -174,7 +226,7 @@ pub mod blocking {
174226
/// Blocking write (iterator version)
175227
pub trait WriteIter<A: AddressMode = SevenBitAddress> {
176228
/// Error type
177-
type Error: core::fmt::Debug;
229+
type Error: Error;
178230

179231
/// Writes bytes to slave with address `address`
180232
///
@@ -189,7 +241,7 @@ pub mod blocking {
189241
/// Blocking write + read
190242
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
191243
/// Error type
192-
type Error: core::fmt::Debug;
244+
type Error: Error;
193245

194246
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
195247
/// single transaction*
@@ -224,7 +276,7 @@ pub mod blocking {
224276
/// Blocking write (iterator version) + read
225277
pub trait WriteIterRead<A: AddressMode = SevenBitAddress> {
226278
/// Error type
227-
type Error: core::fmt::Debug;
279+
type Error: Error;
228280

229281
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
230282
/// single transaction*
@@ -258,7 +310,7 @@ pub mod blocking {
258310
/// This allows combining operations within an I2C transaction.
259311
pub trait Transactional<A: AddressMode = SevenBitAddress> {
260312
/// Error type
261-
type Error: core::fmt::Debug;
313+
type Error: Error;
262314

263315
/// Execute the provided operations on the I2C bus.
264316
///
@@ -285,7 +337,7 @@ pub mod blocking {
285337
/// This allows combining operation within an I2C transaction.
286338
pub trait TransactionalIter<A: AddressMode = SevenBitAddress> {
287339
/// Error type
288-
type Error: core::fmt::Debug;
340+
type Error: Error;
289341

290342
/// Execute the provided operations on the I2C bus (iterator version).
291343
///

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+
}

src/serial/nb.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/// This can be encoded in this trait via the `Word` type parameter.
77
pub trait Read<Word> {
88
/// Read error
9-
type Error: core::fmt::Debug;
9+
type Error: crate::serial::Error;
1010

1111
/// Reads a single word from the serial interface
1212
fn read(&mut self) -> nb::Result<Word, Self::Error>;
@@ -15,7 +15,7 @@ pub trait Read<Word> {
1515
/// Write half of a serial interface
1616
pub trait Write<Word> {
1717
/// Write error
18-
type Error: core::fmt::Debug;
18+
type Error: crate::serial::Error;
1919

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

0 commit comments

Comments
 (0)