Skip to content

Commit c090f4d

Browse files
committed
Expand migration guide, update for the latest changes.
1 parent 1a0d60c commit c090f4d

File tree

1 file changed

+156
-75
lines changed

1 file changed

+156
-75
lines changed

MIGRATING-0.2-1.0.md

Lines changed: 156 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,45 @@
22

33
## Table of contents
44

5-
- [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100)
6-
- [Table of contents](#table-of-contents)
7-
- [Overview and reasoning](#overview-and-reasoning)
8-
- [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)
15-
- [Fallibility](#fallibility)
16-
- [SPI transfer return type](#spi-transfer-return-type)
17-
- [Error type bounds](#error-type-bounds)
18-
- [Prelude](#prelude)
19-
- [`rng` module](#rng-module)
20-
- [Removed blanket implementations](#removed-blanket-implementations)
21-
- [Features](#features)
22-
- [Companion crates](#companion-crates)
23-
- [Use-case-specific help](#use-case-specific-help)
24-
- [For driver authors](#for-driver-authors)
25-
- [I2C traits](#i2c-traits)
26-
- [SPI traits](#spi-traits)
27-
- [For HAL authors](#for-hal-authors)
5+
- [Overview and reasoning](#overview-and-reasoning)
6+
- [Trait organization](#trait-organization)
7+
- [Trait unification](#trait-unification)
8+
- [Removed traits](#removed-traits)
9+
- [Unconstrained associated types](#unconstrained-associated-types)
10+
- [Impractical traits](#impractical-traits)
11+
- [Serial traits](#serial-traits)
12+
- [RNG traits](#rng-traits)
13+
- [CAN traits](#can-traits)
14+
- [SPI Bus/device separation](#spi-busdevice-separation)
15+
- [Fallibility](#fallibility)
16+
- [SPI transfer return type](#spi-transfer-return-type)
17+
- [Error type bounds](#error-type-bounds)
18+
- [Prelude](#prelude)
19+
- [Removed blanket implementations](#removed-blanket-implementations)
20+
- [Cargo Features](#cargo-features)
21+
- [Companion crates](#companion-crates)
2822

2923
## Overview and reasoning
3024

3125
There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and 1.0.0.
3226
We understand the significance of `embedded-hal` in the Rust embedded
3327
ecosystem and thus intend to release a version that stays compatible for a long time.
3428

35-
In this version, among many other changes, we have addressed several big topics that have emerged over the years:
29+
The main difference betewen `embedded-hal` 0.2 and 1.0 is the project is now focused
30+
on a single goal: traits for writing drivers that work on any HAL.
31+
32+
In `embedded-hal` 0.2, the traits had dual goals:
33+
- Standardize the API of HAL crates, so HAL crate authors get guidance on how to design their APIs and
34+
end users writing code directly against one HAL get a familiar API.
35+
- Allowing writing generic drivers using the traits, so they work on top of any HAL crate.
36+
37+
For `embedded-hal` 1.0, we decided to drop the first goal, targeting only the second. The reasons are:
38+
39+
- Standardizing HAL APIs is difficult, because hardware out there has wildly different sets of capabilities. Modeling all capabilities required many different variants of the traits, and required "customization points" like associated types, significantly increasing complexity.
40+
- There is a tension between both goals. "Customization points" like associated types make the traits hard to use in generic HAL-independent drivers.
41+
- The second goal delivers much more value. Being able to use any driver together with any HAL crate, out of the box, and across the entire Rust Embedded ecosystem, is just plain awesome.
42+
43+
This refocusing on drivers is the root cause of many of the changes between `embedded-hal` 0.2 and 1.0:
3644
- [Associated type compatibiilty](#removed-traits)
3745
- [Trait fragmentation](#trait-organization)
3846
- [Bus/device separation](#bus-device-separation)
@@ -41,39 +49,39 @@ In this version, among many other changes, we have addressed several big topics
4149

4250
## Trait organization
4351

44-
All traits have been organized in modules for each feature. For example `embedded_hal::spi` and `embedded_hal::i2c`.
52+
All traits have been organized in modules for each peripheral. For example `embedded_hal::spi` and `embedded_hal::i2c`.
4553
We only foresee having blocking traits in `embedded-hal`. We have put the traits for different execution models
4654
into separate crates. Notably `embedded-hal-async` and `embedded-hal-nb`. See [companion crates](#companion-crates).
4755
This allows for a separate and more tailored evolution.
4856

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`.
57+
Execution-model-independent definitions have been moved into the peripheral's module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`.
5258

53-
### Trait unification
59+
## Trait unification
5460

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.
61+
Previously, there were multiple traits for the same peripheral, for different sets of capabilities. The reasoning
62+
was different hardware supports a different set of features, so by making the traits granular each HAL implementation
63+
can implement only the features supported by the hardware.
5764

58-
For example, most generic code should simply use the `SpiDevice` trait instead of
59-
choosing from `Transactional`, `Transfer`, `Write` and `WriteIter`.
65+
However, this has proven to be troublesome for generic drivers, in cases where a driver expects to use one
66+
trait, but the HAL crate implements only other traits. To avoid this fragmentation and ensure
67+
interoperability for generic code, these have now been unified.
6068

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.
69+
- I2C: `Read`, `Write`, `WriteIter`, `WriteIterRead`, `WriteRead`, `Transactional`, `TransactionalIter` have now been unified into a single `I2c` trait.
70+
- SPI: `Write` `WriteIter`, `Transfer`, `Transactional` have been unified into `SpiBus`.
71+
- GPIO: `ToggleableOutputPin` has been merged into `StatefulOutputPin`.
72+
- Delays: `DelayMs`, `DelayUs` has been unified into `DelayNs` (and precision extended to nanoseconds).
6373

64-
Please see more about this separation [below](#bus-device-separation).
74+
HAL implementation crates should implement the full functionality of the traits. If a feature is not supported natively by the hardware, it should be polyfilled/emulated in software. In no case should "not supported" errors be returned. This ensures maximum compatibility.
6575

6676
## Removed traits
6777

68-
These traits have been removed in the 1.0.0 release:
78+
These traits have been removed in the 1.0.0 release, with no replacement for now:
6979

7080
- [`adc::OneShot`][adc]
7181
- [`adc::Channel`][adc]
7282
- [`capture::Capture`][capture]
73-
- `delay::DelayMs` (replaced by `DelayUs`)
7483
- [`digital::IoPin`][iopin]
7584
- [`pwm::Pwm`][pwm]
76-
- [`pwm::PwmPin`][pwm]
7785
- [`qei::Qei`][qei]
7886
- [`timer::Cancel`][timer]
7987
- [`timer::CountDown`][timer]
@@ -83,8 +91,11 @@ These traits have been removed in the 1.0.0 release:
8391
- [`watchdog::Watchdog`][watchdog]
8492

8593
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.
94+
whether and how to get these traits back in a future release
95+
96+
If you are a generic driver author and need one of them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected.
97+
98+
HAL implementation crates are encouraged to provide their own APIs for functionality for the removed traits, and not implement any traits. This will allow the APIs to more closely match the hardware capabilities, and allow users to continue to use them.
8899

89100
[roadmap-rm-traits]: https://github.com/rust-embedded/embedded-hal/issues/357
90101
[adc]: https://github.com/rust-embedded/embedded-hal/issues/377
@@ -98,29 +109,110 @@ If you need them, we would like to hear from you. Please add your use case to th
98109
### Unconstrained associated types
99110

100111
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.
112+
When a trait has an unconstrained associated type (for example `type Time;`), 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.
102113

103114
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.
115+
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. This means all these traits don't fulfill the "allow writing generic drivers" goal.
105116

106117
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
107118
and we can add them back in a future 1.x release.
108119

109120
### Impractical traits
110121

111122
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.
123+
Please feel free to comment on the appropriate issue if you need any of these traitsk and propose a solution.
124+
125+
### Serial traits
126+
127+
The `blocking::serial::Write` trait has been removed in favor of the [`embedded-io`] traits, also maintained within the `embedded-hal` repository.
128+
129+
[`embedded-io`]: https://crates.io/crates/embedded-io
130+
131+
### RNG traits
132+
133+
The `rng` module and its traits have been removed in favor of the [`rand_core`] traits.
113134

114-
### Delay traits
135+
[`rand_core`]: https://crates.io/crates/rand_core
136+
137+
### CAN traits
138+
139+
The `can` module and its traits have been removed in favor of the [`embedded-can`] traits.
140+
141+
[`embedded-can`]: https://crates.io/crates/embedded-can
142+
143+
## SPI Bus/device separation
144+
145+
The SPI traits have been unified into a single `SpiBus` trait. However, to allow sharing an SPI bus, and hardware control of the CS pin, 1.0 adds the `SpiDevice` trait.
146+
147+
The short summary is:
148+
- `SpiBus` represents an entire SPI bus (with SCK, MOSI, MISO) pins
149+
- `SpiDevice` represents a single device on an SPI bus, selected by a CS pin.
115150

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.
151+
See the [SPI documentation](https://docs.rs/embedded-hal/1.0.0/embedded_hal/spi/index.html) for more details.
118152

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.
153+
When upgrading code to `embedded-hal` 1.0, it is critical to implement/use the right trait depending on the underlying situation.
121154

122-
## Bus/device separation
123-
<!-- TODO assumes I2C bus/device merged -->
155+
### For HAL implementation crates
156+
157+
- If you previously implemented the SPI traits, and did *not* manage a CS pin automatically, you should now implement `SpiBus`, which is the equivalent in 1.0.
158+
- Optionally, if the API *does* manage a CS pin automatically, you may implement `SpiDevice`.
159+
- This is required if the underlying API requires it to manage the CS pin, like `spidev` on Linux.
160+
161+
Do not implement `SpiBus` and `SpiDevice` on the same struct, since this is never correct. When there's no CS pin being controlled you must implement only `SpiBus`, and when there is, implement only `SpiDevice`. If you want to offer both APIs, implement them on separate structs so the user has to cohose one or the other.
162+
163+
### For driver crates
164+
165+
- If your device has SCK, MOSI, MISO, CS pins: use `SpiDevice`.
166+
- Do NOT take the CS pin as a separate `OutputPin`, the `SpiDevice` will manage it for you. Taking the CS pin separately will make your driver not work on shared buses.
167+
- If your device only has SCK, MOSI, MISO: use `SpiBus`.
168+
- This means bus sharing won't be supported, but there's no way to share without a CS pin anyway.
169+
- If you're using SPI to bitbang non-SPI protocols (for example, WS2812 smart LEDs), use `SpiBus`.
170+
171+
### For end users
172+
173+
You will most likely find the HAL crate you're using implements `SpiBus`, and the driver you want to use
174+
requires `SpiDevice`. To convert from `SpiBus` to `SpiDevice`, wrap it with a [`embedded_hal_bus::spi::ExclusiveDevice`](https://docs.rs/embedded-hal-bus/0.1.0/embedded_hal_bus/spi/struct.ExclusiveDevice.html), together with the CS pin:
175+
176+
```rust
177+
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
178+
179+
// Create the SPI from the HAL. This implements SpiBus, not SpiDevice!
180+
let spi_bus = my_hal::spi::Spi::new(...);
181+
// Create the CS. This must implement OutputPin.
182+
let cs = my_hal::gpio::Output::new(...);
183+
184+
// Combine the SPI bus and the CS pin into a SPI device. This now does implement SpiDevice!
185+
let spi_device = ExclusiveDevice::new(spi_bus, cs, NoDelay);
186+
187+
// Now you can create your driver with it!
188+
let driver = my_driver::Driver::new(spi_device, ...);
189+
```
190+
191+
If you want multiple drivers to share the same SPI bus, [`embedded_hal_bus::spi`](https://docs.rs/embedded-hal-bus/0.1.0/embedded_hal_bus/spi/index.html)
192+
has a few options depending on the kind of mutex you want to use. This is now built-in to `embedded-hal`, using external crates like `shared-bus` is discouraged.
193+
194+
For example, you can use `RefCellDevice` when you don't need drivers to be `Send`.
195+
196+
```rust
197+
use core::cell::RefCell;
198+
use embedded_hal_bus::spi::{RefCellDevice, NoDelay};
199+
200+
// Create the SPI bus and CS pins.
201+
let spi_bus = my_hal::spi::Spi::new(...);
202+
let cs1 = my_hal::gpio::Output::new(...);
203+
let cs2 = my_hal::gpio::Output::new(...);
204+
205+
// Wrap the bus with a RefCell.
206+
let spi_bus = RefCell::new(spi_bus);
207+
208+
// Combine references to the SPI bus with a CS pin to get a SpiDevice for one device on the bus.
209+
let device1 = RefCellDevice::new(&spi_bus, cs1, NoDelay);
210+
let device2 = RefCellDevice::new(&spi_bus, cs2, NoDelay);
211+
212+
// Now you can create drivers. They will transparently talk each to its own device, sharing the same bus.
213+
let driver1 = my_driver::Driver::new(device1, ...);
214+
let driver2 = my_driver::Driver::new(device2, ...);
215+
```
124216

125217
## Fallibility
126218

@@ -199,7 +291,7 @@ pub enum MyError {
199291
}
200292
```
201293

202-
Additionally, for the I2C, SPI and Serial communication interfaces we have added a dedicated mechanism
294+
Additionally, for the I2C and SPI communication interfaces we have added a dedicated mechanism
203295
which allows for two crucial requirements:
204296
1. Generic code like drivers can interpret and act on errors if they want to.
205297
2. HAL implementations can have arbitrarily-precise error types.
@@ -247,12 +339,6 @@ from the compiler should already tell you how it should look like in your case)
247339
to limit the scope of the trait imports and thus avoid the ambiguity.
248340
Please note that it is also possible to import traits *inside a function*.
249341

250-
## `rng` module
251-
252-
The `rng` module and its traits have been removed in favor of the [`rand_core`] traits.
253-
254-
[`rand_core`]: https://crates.io/crates/rand_core
255-
256342
## Removed blanket implementations
257343

258344
There were several blanket implementations of blocking traits using the non-blocking
@@ -261,9 +347,7 @@ traits as a base.
261347
Since the non-blocking traits have been extracted into the separate crate `embedded-hal-nb`,
262348
these have been removed.
263349

264-
<!-- TODO assumes nb separation merged -->
265-
266-
## Features
350+
## Cargo features
267351

268352
The `unproven` feature has been removed and the traits have been marked as proven.
269353
In the past, managing unproven features, and having "sort of breaking" changes has been a struggling point.
@@ -275,19 +359,16 @@ experiment externally, and merge when some kind of feasibility had been proven.
275359

276360
## Companion crates
277361

278-
## Use-case-specific help
279-
280-
### For driver authors
281-
282-
### I2C traits
283-
284-
285-
#### SPI traits
286-
287-
288-
### For HAL authors
289-
290-
TODO
362+
The `embedded-hal` project now spans several crates, where some functionality has been moved out from the main `embedded-hal` crate to separate crates as detailed above. Here is the full listing of crates:
291363

364+
| Crate | crates.io | Docs | |
365+
|-|-|-|-|
366+
| [embedded-hal](./embedded-hal) | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) | Core traits, blocking version |
367+
| [embedded-hal-async](./embedded-hal-async) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) | Core traits, async version |
368+
| [embedded-hal-nb](./embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate |
369+
| [embedded-hal-bus](./embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses |
370+
| [embedded-can](./embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits |
371+
| [embedded-io](./embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. |
372+
| [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version |
373+
| [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) |
292374

293-
[MeetingSummary]: https://hackmd.io/ck-xRXtMTmKYXdK5bEh82A

0 commit comments

Comments
 (0)