mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2024-05-17 07:52:59 +00:00
Compare commits
2 commits
4ffeb468df
...
3ae002e00b
Author | SHA1 | Date | |
---|---|---|---|
3ae002e00b | |||
b6aaf36f30 |
|
@ -4,7 +4,6 @@
|
|||
- [The Peripheral Access Crate](./the_pac.md)
|
||||
- [The Hardware Abstraction Layer](./the_hal.md)
|
||||
- [Building Common Abstractions](./building_common_abstractions.md)
|
||||
- [Creating Portable Drivers](./creating_portable_drivers.md)
|
||||
- [Supporting your particular board](./supporting_your_particular_board.md)
|
||||
- [Writing an Application](./writing_an_application.md)
|
||||
|
||||
|
|
|
@ -1 +1,99 @@
|
|||
# Building Common Abstractions
|
||||
|
||||
---
|
||||
|
||||
## 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,99 +0,0 @@
|
|||
# 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,41 @@
|
|||
# Supporting your particular board
|
||||
|
||||
---
|
||||
|
||||
## Using a 'normal' PC
|
||||
|
||||
* Did you tell your PC it had a mouse plugged in?
|
||||
* Did you tell it what I/O address the video card was located at?
|
||||
* No! It auto-discovers all of these things.
|
||||
* USB, PCI-Express, SATA all have "plug-and-play"
|
||||
|
||||
---
|
||||
|
||||
## Using an Embedded System
|
||||
|
||||
* There is no plug-and-play
|
||||
* Your MCU can put different functions (UART, SPI, etc) on different pins
|
||||
* The choice of which function goes on which pin was decided by the PCB designer
|
||||
* You now have to tell the software how the PCB was laid out
|
||||
* i.e UART0 TX is on Port 0, Pin 13
|
||||
|
||||
---
|
||||
|
||||
## Board Support Crates
|
||||
|
||||
* You can wrap this up into a Board Support Crate
|
||||
* Especially useful if you are using a widely available dev-kit
|
||||
* e.g. the nRF52840-DK, or the STM32 Discovery
|
||||
* Still useful if the board design is an in-house one-off
|
||||
* Create the drivers and does the pin assignments for you
|
||||
* Helps make your application portable across different boards
|
||||
|
||||
---
|
||||
|
||||
## Using a Board Support Crate
|
||||
|
||||
```rust
|
||||
|
||||
```
|
||||
|
||||
---
|
Loading…
Reference in a new issue