diff --git a/down-the-stack-book/src/creating_portable_drivers.md b/down-the-stack-book/src/creating_portable_drivers.md index fcf21fe..08ef839 100644 --- a/down-the-stack-book/src/creating_portable_drivers.md +++ b/down-the-stack-book/src/creating_portable_drivers.md @@ -1 +1,99 @@ # 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; + fn write(&mut self, buffer: &[u8]) -> Result; +} +``` + +--- + +## My Library... + +```rust +struct AtCommandParser { + uart: T, + ... +} + +impl AtCommandParser where T: GenericSerial { + fn new(uart: T) -> AtCommandParser { ... } + fn get_command(&mut self) -> Result, 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 diff --git a/down-the-stack-book/src/the_hal.md b/down-the-stack-book/src/the_hal.md index 21264b9..f668ed8 100644 --- a/down-the-stack-book/src/the_hal.md +++ b/down-the-stack-book/src/the_hal.md @@ -1 +1,97 @@ # 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. +