Isolate peripherals chapter

This commit is contained in:
James Munns
2018-09-24 19:51:00 +02:00
parent 7791f066de
commit 214af2e8e1
7 changed files with 3 additions and 261 deletions

View File

@@ -24,12 +24,9 @@ more information and coordination
- [The Borrow Checker](./peripherals/borrowck.md)
- [Singletons](./peripherals/singletons.md)
- [Peripherals in Rust](./peripherals/rusty.md)
- [Typestate Programming](./typestate-programming/typestate-programming.md)
- [Peripherals as State Machines](./typestate-programming/state-machines.md)
- [Design Contracts](./typestate-programming/design-contracts.md)
- [Zero Cost Abstractions](./typestate-programming/zero-cost-abstractions.md)
- [Static Guarantees](./static-guarantees/static-guarantees.md)
<!-- TODO: Define Sections -->
- [Portability](./portability/portability.md)
- [The Trait System](./portability/traits.md)
<!-- TODO: Define more sections -->
- [Concurrency](./concurrency/concurrency.md)
<!-- TODO: Define Sections -->

View File

@@ -1,14 +1,3 @@
# Portability
> ❌: This section has not yet been written. Please refer to [embedded-wg#119](https://github.com/rust-lang-nursery/embedded-wg/issues/119) for discussion of this section.
## Building something bigger
> drivers that work for more than one chip
![](./../assets/embedded-hal.svg)
* Device Crates (one per chip)
* HAL Implementation Crates (one per chip)
* `embedded-hal` (only one)
* Driver Crates (one per external component)
> ❌: This section has not yet been written. Please refer to [embedded-wg#119](https://github.com/rust-lang-nursery/embedded-wg/issues/119) for discussion of this section.

View File

@@ -1,50 +0,0 @@
# The Trait System
```rust
/// Single digital push-pull output pin
pub trait OutputPin {
/// Drives the pin low
fn set_low(&mut self);
/// Drives the pin high
fn set_high(&mut self);
}
```
[rust-embedded/embedded-hal](https://github.com/rust-embedded/embedded-hal/blob/master/src/digital.rs)
```rust
impl<MODE> OutputPin for OutputGpio<MODE> {
fn set_low(&mut self) {
self.set_pin_low()
}
fn set_high(&mut self) {
self.set_pin_high()
}
}
```
> this goes in your chip crate
```rust
impl<SPI, CS, E> L3gd20<SPI, CS>
where
SPI: Transfer<u8, Error = E> + Write<u8, Error = E>,
CS: OutputPin,
{
/// Creates a new driver from a SPI peripheral
/// and a NCS (active low chip select) pin
pub fn new(spi: SPI, cs: CS) -> Result<Self, E> {
// ...
}
// ...
}
```
[japaric/l3gd20](https://github.com/japaric/l3gd20/blob/master/src/lib.rs)
## N\*M >>> N+M
> to re-use a driver, just implement the embedded-hal interface

View File

@@ -1,48 +0,0 @@
# Design Contracts
In
```rust
use gpio::{InputGpio, OutputGpio};
struct GpioPin;
impl GpioPin {
fn into_input(self) -> InputGpio {
self.set_input_mode();
InputGpio
}
fn into_output(self) -> OutputGpio {
self.set_output_mode();
OutputGpio
}
}
```
* Use type transitions to enforce setup steps
* Like the builder pattern in "normal" Rust
```rust
impl LedPin {
fn new(pin: OutputGpio) -> Self { ... }
fn toggle(&mut self) -> bool { ... }
}
fn main() {
let gpio_1 = unsafe { PERIPHERALS.take_gpio_1() };
// This won't work, the types are wrong!
// let led_1 = LedPin::new(gpio_1);
let mut led_1 = LedPin::new(gpio_1.into_output());
let _ = led_1.toggle();
}
```
* You have to have the right types to have the interfaces you want
## Enforce design contracts
> entirely at compile time
>
> no runtime cost
>
> no room for human error

View File

@@ -1,5 +0,0 @@
# Peripherals as State Machines
The peripherals of a microcontroller can be thought of as set of state machines. For example, a simplified [GPIO pin]
[GPIO pin]: https://en.wikipedia.org/wiki/General-purpose_input/output

View File

@@ -1,63 +0,0 @@
# Typestate Programming
The concept of [typestates] describes the encoding of information about the current state of an object into the type of that object. Although this can sound a little arcane, if you have used the [Builder Pattern] in Rust, you have already started using Typestate Programming!
[typestates]: https://en.wikipedia.org/wiki/Typestate_analysis
[Builder Pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
```rust
#[derive(Debug)]
struct Foo {
inner: u32,
}
struct FooBuilder {
a: u32,
b: u32,
}
impl FooBuilder {
pub fn new(starter: u32) -> Self {
Self {
a: starter,
b: starter,
}
}
pub fn double_a(self) -> Self {
Self {
a: self.a * 2,
b: self.b,
}
}
pub fn into_foo(self) -> Foo {
Foo {
inner: self.a + self.b,
}
}
}
fn main() {
let x = FooBuilder::new(10)
.double_a()
.into_foo();
println!("{:#?}", x);
}
```
In this example, there is no direct way to create a `Foo` object. We must create a `FooBuilder`, and properly initialize it before we can obtain the `Foo` object we want.
This minimal example encodes two states:
* `FooBuilder`, which represents an "unconfigured", or "configuration in process" state
* `Foo`, which represents a "configured", or "ready to use" state.
## Strong Types
Because Rust has a [Strong Type System], there is no easy way to magically create an instance of `Foo`, or to turn a `FooBuilder` into a `Foo` without calling the `into_foo()` method. Additionally, calling the `into_foo()` method consumes the original `FooBuilder` structure, meaning it can not be reused without the creation of a new instance.
[Strong Type System]: https://en.wikipedia.org/wiki/Strong_and_weak_typing
This allows us to represent the states of our system as types, and to include the necessary actions for state transitions into the methods that exchange one type for another. By creating a `FooBuilder`, and exchanging it for a `Foo` object, we have walked through the steps of a basic state machine.

View File

@@ -1,78 +0,0 @@
# Zero Cost Abstractions
## "no runtime cost"?
```rust
use core::mem::size_of;
let _ = size_of::<GpioPin>(); // == 0
let _ = size_of::<InputGpio>(); // == 0
let _ = size_of::<OutputGpio>(); // == 0
let _ = size_of::<()>(); // == 0
```
## Zero Sized Types
```rust
struct GpioPin;
```
> acts real at compile time
>
> doesn't exist in the binary
>
> no RAM, no CPU, no space
>
> Evaporates at compile time
## What if our `OutputGpio` has multiple modes?
> (it does)
---
```rust
pub struct PushPull; // good for general usage
pub struct OpenDrain; // used when multiple devices could drive a bus
pub struct OutputGpio<MODE> {
_mode: MODE
}
impl<MODE> OutputGpio<MODE> {
fn default() -> OutputGpio<OpenDrain> { ... }
fn into_push_pull(self) -> OutputGpio<PushPull> { ... }
fn into_open_drain(self) -> OutputGpio<OpenDrain> { ... }
}
```
---
```rust
/// This kind of LED only works with OpenDrain settings
struct DrainLed {
pin: OutputGpio<OpenDrain>,
}
impl DrainLed {
fn new(pin: OutputGpio<OpenDrain>) -> Self { ... }
fn toggle(&self) -> bool { ... }
}
```
---
```rust
/// This kind of LED works with any output
struct LedDriver<MODE> {
pin: OutputGpio<MODE>,
}
/// Generically support any OutputGpio variant!
impl<MODE> LedDriver<MODE> {
fn new(pin: OutputGpio<MODE>) -> LedDriver<MODE> { ... }
fn toggle(&self) -> bool { ... }
}
```
* Nested zero sized types are still zero sized, no matter how deep you nest them