Skip to content

Commit 74f9b9b

Browse files
committed
Add overview and removed traits section + Update to current state
1 parent d80c464 commit 74f9b9b

File tree

1 file changed

+151
-49
lines changed

1 file changed

+151
-49
lines changed

MIGRATING-0.2-1.0.md

Lines changed: 151 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,123 @@
44

55
- [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100)
66
- [Table of contents](#table-of-contents)
7+
- [Overview and reasoning](#overview-and-reasoning)
78
- [Trait organization](#trait-organization)
9+
- [Trait unification](#trait-unification)
10+
- [Removed traits](#removed-traits)
11+
- [Unconstrained associated types](#unconstrained-associated-types)
12+
- [Impractical traits](#impractical-traits)
13+
- [Delay traits](#delay-traits)
14+
- [Bus/device separation](#busdevice-separation)
815
- [Fallibility](#fallibility)
9-
- [Method renaming](#method-renaming)
1016
- [SPI transfer return type](#spi-transfer-return-type)
1117
- [Error type bounds](#error-type-bounds)
12-
- [`nb` dependency](#nb-dependency)
1318
- [Prelude](#prelude)
1419
- [`rng` module](#rng-module)
1520
- [Removed blanket implementations](#removed-blanket-implementations)
1621
- [Features](#features)
22+
- [Companion crates](#companion-crates)
1723
- [Use-case-specific help](#use-case-specific-help)
1824
- [For driver authors](#for-driver-authors)
1925
- [I2C traits](#i2c-traits)
2026
- [SPI traits](#spi-traits)
2127
- [For HAL authors](#for-hal-authors)
2228

29+
## Overview and reasoning
30+
31+
There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and 1.0.0.
32+
We understand the significance of `embedded-hal` in the Rust embedded
33+
ecosystem and thus intend to release a version that stays compatible for a long time.
34+
35+
In this version, among many other changes, we have addressed several big topics that have emerged over the years:
36+
- [Associated type compatibiilty](#removed-traits)
37+
- [Trait fragmentation](#trait-organization)
38+
- [Bus/device separation](#bus-device-separation)
39+
- [Fallibility](#fallibility)
40+
- [Execution model support](#trait-organization)
41+
2342
## Trait organization
2443

25-
All traits have been organized in modules for each feature, each of these containing sub-modules depending on their execution model. That includes `blocking` and `nb` for
26-
non-blocking. In the future when we add asynchronous traits, we envision adding a `futures` (or similarly-named) module.
44+
All traits have been organized in modules for each feature. For example `embedded_hal::spi` and `embedded_hal::i2c`.
45+
We only foresee having blocking traits in `embedded-hal`. We have put the traits for different execution models
46+
into separate crates. Notably `embedded-hal-async` and `embedded-hal-nb`. See [companion crates](#companion-crates).
47+
This allows for a separate and more tailored evolution.
48+
49+
<!-- TODO assumes nb separation merged -->
50+
51+
Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`.
52+
53+
### Trait unification
54+
55+
Previously, there were multiple traits for the same feature. In order to avoid fragmentation and ensure
56+
interoperability for generic code, these have now been united.
57+
58+
For example, most generic code should simply use the `SpiDevice` trait instead of
59+
choosing from `Transactional`, `Transfer`, `Write` and `WriteIter`.
60+
61+
For HAL implementations and some specialized use-cases there are still a few traits to implement for SPI
62+
but the number has been reduced.
63+
64+
Please see more about this separation [below](#bus-device-separation).
65+
66+
## Removed traits
67+
68+
These traits have been removed in the 1.0.0 release:
69+
70+
- [`adc::OneShot`][adc]
71+
- [`adc::Channel`][adc]
72+
- [`capture::Capture`][capture]
73+
- `delay::DelayMs` (replaced by `DelayUs`)
74+
- [`digital::IoPin`][iopin]
75+
- [`pwm::Pwm`][pwm]
76+
- [`pwm::PwmPin`][pwm]
77+
- [`qei::Qei`][qei]
78+
- [`timer::Cancel`][timer]
79+
- [`timer::CountDown`][timer]
80+
- [`timer::Periodic`][timer]
81+
- [`watchdog::Disable`][watchdog]
82+
- [`watchdog::Enable`][watchdog]
83+
- [`watchdog::Watchdog`][watchdog]
84+
85+
Please find a general [roadmap with further guidance here][roadmap-rm-traits] about
86+
how to get these traits back in a future release.
87+
If you need them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected.
88+
89+
[roadmap-rm-traits]: https://github.com/rust-embedded/embedded-hal/issues/357
90+
[adc]: https://github.com/rust-embedded/embedded-hal/issues/377
91+
[iopin]: https://github.com/rust-embedded/embedded-hal/issues/397
92+
[capture]: https://github.com/rust-embedded/embedded-hal/issues/361
93+
[pwm]: https://github.com/rust-embedded/embedded-hal/issues/358
94+
[qei]: https://github.com/rust-embedded/embedded-hal/issues/362
95+
[timer]: https://github.com/rust-embedded/embedded-hal/issues/359
96+
[watchdog]: https://github.com/rust-embedded/embedded-hal/issues/360
97+
98+
### Unconstrained associated types
99+
100+
Traits defined in `embedded-hal` pursue creating an interface for interoperability between generic code (be it generic user code, generic application code, generic device drivers, etc.).
101+
When a trait has an unconstrained associated type, it is not possible to write generic code around it. Each side (implementer and user) need to specify which type the associated type will be. If the types match, the both parts can work together, however, this is not truly generic code.
102+
103+
For example, if somebody creates a device driver that receives a `CountDown` struct, it needs to specify what its `Time` type should be. If they choose a type coming from `fugit`, somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides `CountDown` with `Time` types defined in `embedded-time`. It is also not possible for the user to implement `CountDown` for `Time` types defined by `fugit` in a straight-forward way due to the orphan rule.
104+
In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one.
105+
106+
At the moment no solution for this has been found so we have decided to remove such traits hoping that a solution may be found
107+
and we can add them back in a future 1.x release.
108+
109+
### Impractical traits
110+
111+
The [`digital::IoPin` trait][iopin] and the [`adc` traits][adc] have been deemed impractical for use and have thus been removed.
112+
Please feel free to comment on the appropriate issue if you need any of these crates and propose a solution.
113+
114+
### Delay traits
115+
116+
The `DelayMs` trait has been removed. The functionality provided by this trait should now be provided by the `DelayUs` trait,
117+
which also features a convenience `delay_ms()` method, so changes should be minimal.
27118

28-
Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules.
119+
This allowed us to reduce the API surface while still keeping the main functionality. We intend to add a generic `Delay` trait
120+
in the future, once the time representation issue has been resolved.
121+
122+
## Bus/device separation
123+
<!-- TODO assumes I2C bus/device merged -->
29124

30125
## Fallibility
31126

@@ -34,19 +129,19 @@ However, HAL implementations can also provide infallible versions of the methods
34129

35130
For example, an implementation similar to the one below would allow to use the GPIO pins as `OutputPin`s
36131
in any generic driver or implementation-agnostic code (by importing the `OutputPin` trait),
37-
as well as using the infallible methods in non-generic code, thus avoiding the need to use `unwrap()`
38-
the results in many cases and resulting in more succinct code.
132+
as well as using the infallible methods in non-generic code.
133+
This avoids the need to use `unwrap()` the results in many cases and results in more succinct code.
39134

40135
It should be noted that given this implementation, importing the `OutputPin` trait can result in
41136
ambiguous calls, so please remove the trait imports if you do not need them.
42137

43138
```rust
44139
use core::convert::Infallible;
45-
use embedded_hal::blocking::digital::OutputPin;
140+
use embedded_hal::digital::blocking::OutputPin;
46141

47-
struct GpioPin;
142+
struct HalImplGpioPin;
48143

49-
impl OutputPin for GpioPin {
144+
impl OutputPin for HalImplGpioPin {
50145
type Error = Infallible;
51146

52147
fn set_high(&mut self) -> Result<(), Self::Error> {
@@ -60,7 +155,7 @@ impl OutputPin for GpioPin {
60155
}
61156
}
62157

63-
impl GpioPin {
158+
impl HalImplGpioPin {
64159
fn set_high(&mut self) {
65160
// ...
66161
}
@@ -71,24 +166,15 @@ impl GpioPin {
71166
}
72167
```
73168

74-
## Method renaming
75-
76-
The methods in `SPI`, `I2C` and `Serial` traits for both `blocking` and `nb` execution models have been renamed
77-
to `write()`, `read()` and `flush()` for consistency.
78-
79-
In order to avoid method call ambiguity, only the traits from the corresponding execution model should be imported
80-
into the relevant scope. This is the reason why we have removed the prelude.
81-
82-
For more on this, see [Prelude](#prelude).
83-
84169
## SPI transfer return type
85170

86-
The `transfer()` method in the trait `spi::blocking::Transfer` previously returned
87-
a slice of the output data.
171+
Previously the `transfer()` method in SPI returned a slice of the output data.
88172
This slice is the same as the output buffer which is passed to the method, though, thus redundant and potentially confusing.
89173
The `transfer()` method now returns `Result<(), Self::Error>`.
90174
If you were using this return value, adapting the code should be straight forward by simply using the reception buffer which is passed.
175+
91176
See an example:
177+
92178
```rust
93179
let tx_data = [1, 2, 3, 4];
94180
let mut rx_data = [0; 4];
@@ -113,38 +199,52 @@ pub enum MyError {
113199
}
114200
```
115201

116-
## `nb` dependency
202+
Additionally, for the I2C, SPI and Serial communication interfaces we have added a dedicated mechanism
203+
which allows for two crucial requirements:
204+
1. Generic code like drivers can interpret and act on errors if they want to.
205+
2. HAL implementations can have arbitrarily-precise error types.
117206

118-
The `Result` type and `block!` macro from the [`nb`] crate are now reexported in `embeddeh_hal::nb`.
119-
This ensures there are no version mismatches.
120-
You should remove the `nb` crate dependency in your `Cargo.toml` in any version and use the reexported items.
207+
This works in the following way:
121208

122-
In your `Cargo.toml`:
123-
```diff
124-
- nb = "1"
125-
```
209+
For each interface, `embedded-hal` defines an `ErrorKind` `enum` type with all sensible error variants as well
210+
as an `Error` trait featuring a method that converts the type into that `ErrorKind`.
126211

127-
In your code:
128-
```diff
129-
- use nb;
130-
+ use embedded_hal::nb;
131-
```
132-
You can also drop `#[macro_use]` if you are using Rust edition 2018.
212+
`embedded-hal` still allows for implementation-defined error types associated to each trait, but requires these to
213+
implement the appropriate `Error` trait, thus providing a mapping to a defined set of error variants.
133214

134-
Alternatively (needs Rust edition 2018):
135-
```diff
136-
- use nb::{Result, block};
137-
+ use embedded_hal::nb::{Result, block};
215+
With this mechanism, HAL implementations can continue to define their own error types which can carry as much
216+
information as they want. On the other hand it is now possible for generic code to inspect those errors
217+
and act on common errors like I2Cs NACK.
218+
219+
Furthermore, implementation-specific code can access the original error type and retrieve any information contained.
220+
221+
An example of a driver which looks for I2C NACK errors and returns its own `DeviceBusy` or `Comm` error
222+
wrapping the original one could be as follows:
223+
224+
```rust
225+
const address = 0x1D;
226+
227+
fn set_some_parameter(&mut self) -> Result<(), Self::Error> {
228+
const data = [0, 1];
229+
match self.i2c.write(address, &data) {
230+
Err(e) => match e.kind() {
231+
ErrorKind::NoAcknowledge(_) => Err(Self::Error::DeviceBusy(e)),
232+
_ => Err(Self::Error::Comm(e)) // wrap and return any other error
233+
},
234+
Ok(_) => Ok(())
235+
}
236+
}
138237
```
139238

140239
## Prelude
141240

142241
The prelude has been removed because it could make method calls ambiguous, since the method names are now
143-
the same across execution models.
242+
the same across traits.
144243
To overcome this, please import the traits you wish to use individually.
145-
If you run into ambiguous method calls, you can disambiguate using fully-qualified syntax (the error message
244+
245+
If you run into ambiguous method calls, you can disambiguate using the fully-qualified syntax (the error message
146246
from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code
147-
to limit the scope of the trait imports and thus avoid ambiguity.
247+
to limit the scope of the trait imports and thus avoid the ambiguity.
148248
Please note that it is also possible to import traits *inside a function*.
149249

150250
## `rng` module
@@ -157,8 +257,11 @@ The `rng` module and its traits have been removed in favor of the [`rand_core`]
157257

158258
There were several blanket implementations of blocking traits using the non-blocking
159259
traits as a base.
160-
Due to their relative simplicity and some technical concerns, these have been removed.
161-
Implementing them yourself is now necessary. This should be straight-forward.
260+
261+
Since the non-blocking traits have been extracted into the separate crate `embedded-hal-nb`,
262+
these have been removed.
263+
264+
<!-- TODO assumes nb separation merged -->
162265

163266
## Features
164267

@@ -170,18 +273,17 @@ long time to stabilize.
170273
Instead, we would like to push experimentation OUT of the `embedded-hal` crate, allowing people to
171274
experiment externally, and merge when some kind of feasibility had been proven.
172275

276+
## Companion crates
277+
173278
## Use-case-specific help
174279

175280
### For driver authors
176281

177282
### I2C traits
178283

179-
Nothing changed.
180284

181285
#### SPI traits
182286

183-
For the blocking traits nothing changed.
184-
For the non-blocking traits, TODO
185287

186288
### For HAL authors
187289

0 commit comments

Comments
 (0)