mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2024-05-24 11:18:05 +00:00
Add The HAL and Portable Drivers
This commit is contained in:
parent
5e6fb4ab06
commit
4ffeb468df
|
@ -1 +1,99 @@
|
||||||
# Creating Portable Drivers
|
# Creating Portable Drivers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## These things are different
|
||||||
|
|
||||||
|
* STM32F030 UART Driver
|
||||||
|
* nRF52840 UART Driver
|
||||||
|
* But I want to write a library which is generic!
|
||||||
|
* e.g. an AT Command Parser
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How does Rust allow generic behaviour?
|
||||||
|
|
||||||
|
* Generics!
|
||||||
|
* `where T: SomeTrait`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Traits
|
||||||
|
|
||||||
|
```rust
|
||||||
|
trait GenericSerial {
|
||||||
|
type Error;
|
||||||
|
fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Error>;
|
||||||
|
fn write(&mut self, buffer: &[u8]) -> Result<usize, Error>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## My Library...
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct AtCommandParser<T> {
|
||||||
|
uart: T,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AtCommandParser<T> where T: GenericSerial {
|
||||||
|
fn new(uart: T) -> AtCommandParser<T> { ... }
|
||||||
|
fn get_command(&mut self) -> Result<Option<AtCommand>, Error> { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note how `AtCommandParser` *owns* the object which meets the `GenericSerial` trait.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## My Application
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let uart = stm32_hal::Uart::new(...);
|
||||||
|
let at_parser = at_library::AtCommandParser::new(uart);
|
||||||
|
while let Some(cmd) = at_parser.get_command().unwrap() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## My Application (2)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let uart = nrf52_hal::Uart::new(...);
|
||||||
|
let at_parser = at_library::AtCommandParser::new(uart);
|
||||||
|
while let Some(cmd) = at_parser.get_command().unwrap() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How do we agree on the traits?
|
||||||
|
|
||||||
|
* The Rust Embedded Working Group has developed some traits
|
||||||
|
* They are called the *Embedded HAL*
|
||||||
|
* See https://docs.rs/embedded-hal/
|
||||||
|
* All HAL implementations should implement these traits
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Blocking vs Non-blocking
|
||||||
|
|
||||||
|
* Should a trait API stall your CPU until the data is ready?
|
||||||
|
* Or should it return early, saying "not yet ready"
|
||||||
|
* So you can go an do something else in the mean time?
|
||||||
|
* Or sleep?
|
||||||
|
* `embedded_hal::blocking::serial::Write`, vs
|
||||||
|
* `embedded_hal::serial::Write`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Trade-offs
|
||||||
|
|
||||||
|
* Some MCUs have more features than others
|
||||||
|
* The trait design has an inherent trade-off
|
||||||
|
* Flexibility/Performance vs Portability
|
||||||
|
|
|
@ -1 +1,97 @@
|
||||||
# The Hardware Abstraction Layer
|
# The Hardware Abstraction Layer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building a driver
|
||||||
|
|
||||||
|
* Writing to all those registers is tedious
|
||||||
|
* You have to get the values right, and the order right
|
||||||
|
* Can we wrap it up into a nicer, easier-to-use object?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typical driver interface
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let p = pac::Peripherals.take().unwrap();
|
||||||
|
let mut uarte0 = hal::uarte::Uarte::new(
|
||||||
|
// Our singleton representing exclusive access to
|
||||||
|
// the peripheral IP block
|
||||||
|
p.UARTE0,
|
||||||
|
// Some other settings we might need
|
||||||
|
115200,
|
||||||
|
hal::uarte::Parity::None,
|
||||||
|
hal::uarte::Handshaking::None,
|
||||||
|
);
|
||||||
|
// Using the `uarte0` object:
|
||||||
|
uarte0.write_all(b"Hey, I'm using a UART!").unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Hardware Abstraction Layer
|
||||||
|
|
||||||
|
* Contains all the drivers for a chip
|
||||||
|
* Often common/shared across chip families
|
||||||
|
* e.g. nRF52 HAL for 52832, 52840, etc
|
||||||
|
* Usually community developed
|
||||||
|
* Often quite different between MCU vendors
|
||||||
|
* Different teams came up with different designs!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kinds of driver
|
||||||
|
|
||||||
|
* PLL / Clock Configuration
|
||||||
|
* Reset / Power Control of Peripherals
|
||||||
|
* GPIO pins
|
||||||
|
* UART
|
||||||
|
* SPI
|
||||||
|
* I²C
|
||||||
|
* ADC
|
||||||
|
* Timer/Counters
|
||||||
|
* and more!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Handling GPIO pins with code
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Get the singletons
|
||||||
|
let p = pac::Peripherals.take().unwrap();
|
||||||
|
// Make a driver for GPIO port P0
|
||||||
|
let pins = hal::gpio::p0::Parts::new(p.P0);
|
||||||
|
// Get Pin 13 on port P0 and make it an output
|
||||||
|
let mut led_pin = pins.p0_13.into_push_pull_output(Level::High);
|
||||||
|
// Now set the output low
|
||||||
|
led_pin.set_low();
|
||||||
|
```
|
||||||
|
|
||||||
|
This differs widely across MCUs (ST, Nordic, Espressif, Atmel, etc). Some MCUs (e.g. Nordic) let you put any function on any pin, and some are much more restrictive!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Correctness by design
|
||||||
|
|
||||||
|
* HALs want to make it hard to do the wrong thing
|
||||||
|
* Is a UART driver any use, if you haven't configured at least one TX pin and one RX pin?
|
||||||
|
* Should the UART driver check you've done that?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Giving the pins to the driver
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 'degrade()' converts a P0_08 type into a generic Pin type.
|
||||||
|
let pins = hal::uarte::Pins {
|
||||||
|
rxd: pins.p0_08.degrade().into_floating_input(),
|
||||||
|
txd: pins.p0_06.degrade().into_push_pull_output(Level::High),
|
||||||
|
cts: None,
|
||||||
|
rts: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let uarte = hal::uarte::Uarte::new(periph.UARTE1, pins, Parity::EXCLUDED, Baudrate::BAUD115200);
|
||||||
|
```
|
||||||
|
|
||||||
|
This is example is for the nRF52. We'll use it later in the example.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue