mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2025-01-24 23:08:08 +00:00
Start down-the-stack book.
Has notes on PACs.
This commit is contained in:
parent
3a4acd3543
commit
33778655e4
12 changed files with 1556 additions and 0 deletions
1
down-the-stack-book/.gitignore
vendored
Normal file
1
down-the-stack-book/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
book
|
16
down-the-stack-book/book.toml
Normal file
16
down-the-stack-book/book.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[book]
|
||||
authors = ["Jonathan Pallant (Ferrous Systems)"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Down the Stack"
|
||||
|
||||
[preprocessor]
|
||||
|
||||
[preprocessor.mermaid]
|
||||
command = "mdbook-mermaid"
|
||||
|
||||
[output]
|
||||
|
||||
[output.html]
|
||||
additional-js = ["mermaid.min.js", "mermaid-init.js"]
|
1
down-the-stack-book/mermaid-init.js
Normal file
1
down-the-stack-book/mermaid-init.js
Normal file
|
@ -0,0 +1 @@
|
|||
mermaid.initialize({startOnLoad:true});
|
1282
down-the-stack-book/mermaid.min.js
vendored
Normal file
1282
down-the-stack-book/mermaid.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
down-the-stack-book/src/SUMMARY.md
Normal file
10
down-the-stack-book/src/SUMMARY.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Summary
|
||||
|
||||
- [Introduction](./introduction.md)
|
||||
- [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
down-the-stack-book/src/building_common_abstractions.md
Normal file
1
down-the-stack-book/src/building_common_abstractions.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Building Common Abstractions
|
1
down-the-stack-book/src/creating_portable_drivers.md
Normal file
1
down-the-stack-book/src/creating_portable_drivers.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Creating Portable Drivers
|
84
down-the-stack-book/src/introduction.md
Normal file
84
down-the-stack-book/src/introduction.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Introduction
|
||||
|
||||
---
|
||||
|
||||
## A Layered Approach
|
||||
|
||||
When building Embedded Systems in Rust, we use Rust crates to help us build a modular system.
|
||||
|
||||
The elements are:
|
||||
|
||||
* The program you are writing
|
||||
* The MCU are running on
|
||||
* The PCB (or Board) your MCU is on
|
||||
* The external devices connected to your MCU
|
||||
|
||||
---
|
||||
|
||||
## The Layers
|
||||
|
||||
To support these elements, we (usually) have these layers.
|
||||
|
||||
* Application
|
||||
* Board Support
|
||||
* External Drivers (e.g. SPI LCD Driver)
|
||||
* Hardware Abstraction Layer Traits
|
||||
* MCU Hardware Abstraction Layer Implementation
|
||||
* MCU Peripheral Access Crate
|
||||
* Core Peripherals
|
||||
* Core Runtime
|
||||
|
||||
---
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
app(Application<br/><tt>my_application</tt>)
|
||||
bsc[Board Support<br/><tt>nrf52840_dk</tt>]
|
||||
hal[MCU HAL Implementation<br/><tt>nrf52480_hal</tt>]
|
||||
lcd_driver[SPI LCD Driver<br/><tt>ssd1306</tt>]
|
||||
hal_traits[[HAL Traits<br/><tt>embedded_hal</tt>]]
|
||||
pac[MCU PAC<br/><tt>nrf52840</tt>]
|
||||
rt[Core Runtime<br/><tt>cortex_m_rt</tt>]
|
||||
cp[Core Peripherals<br/><tt>cortex_m</tt>]
|
||||
|
||||
subgraph Key
|
||||
note1[Embedded Working Group]
|
||||
note2[nrf-rs]
|
||||
note3[You]
|
||||
note4[Others]
|
||||
end
|
||||
|
||||
direction TB
|
||||
app --> bsc
|
||||
app & bsc --> hal
|
||||
app --> lcd_driver
|
||||
app & lcd_driver --> hal_traits
|
||||
hal -- Implements --o hal_traits
|
||||
app & hal --> pac
|
||||
app & pac --> rt
|
||||
app & pac & rt --> cp
|
||||
|
||||
class app binary;
|
||||
class bsc library;
|
||||
class lcd_driver library;
|
||||
class hal mcu_library;
|
||||
class pac mcu_library;
|
||||
class hal_traits ewg_library;
|
||||
class rt ewg_library;
|
||||
class cp ewg_library;
|
||||
|
||||
class note1 ewg_library;
|
||||
class note2 mcu_library;
|
||||
class note3 binary;
|
||||
class note4 library;
|
||||
|
||||
classDef binary fill:#fb8,stroke:#333,stroke-width:4px;
|
||||
classDef library fill:#cf9,stroke:#333,stroke-width:2px;
|
||||
classDef ewg_library fill:#f9c,stroke:#333,stroke-width:2px;
|
||||
classDef mcu_library fill:#9cf,stroke:#333,stroke-width:2px;
|
||||
```
|
||||
---
|
||||
|
||||
## Don't worry!
|
||||
|
||||
There's a lot here. We're going to take it step by step, starting at the bottom.
|
|
@ -0,0 +1 @@
|
|||
# Supporting your particular board
|
1
down-the-stack-book/src/the_hal.md
Normal file
1
down-the-stack-book/src/the_hal.md
Normal file
|
@ -0,0 +1 @@
|
|||
# The Hardware Abstraction Layer
|
157
down-the-stack-book/src/the_pac.md
Normal file
157
down-the-stack-book/src/the_pac.md
Normal file
|
@ -0,0 +1,157 @@
|
|||
# The Peripheral Access Crate
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
This crate sits at the bottom of the 'stack'. It provides access to the
|
||||
memory-mapped peripherals in your MCU.
|
||||
|
||||
---
|
||||
|
||||
## Memory Mapped Peripherals
|
||||
|
||||
* e.g. a UART peripheral
|
||||
* Has registers, represented by a memory address
|
||||
* Registers are usually consecutive in memory (not always)
|
||||
* Peripherals can have instances (same layout of registers, different start address)
|
||||
* UART0, UART1, etc
|
||||
|
||||
---
|
||||
|
||||
## Datasheets
|
||||
|
||||
* *Registers* are comprised of one or more *fields*.
|
||||
* Each field is at least 1 bit in length.
|
||||
* Sometimes fields can only take from a limited set of values
|
||||
* This is all in your datasheet!
|
||||
|
||||
---
|
||||
|
||||
## C Code!
|
||||
|
||||
Embedded Code in C often uses shifts and bitwise-AND to make up registers from
|
||||
fields.
|
||||
|
||||
```c,no_run
|
||||
#define UARTE_INTEN_CTS_SHIFT (0)
|
||||
#define UARTE_INTEN_CTS_MASK (0x00000001)
|
||||
#define UARTE_INTEN_NCTS_SHIFT (1)
|
||||
#define UARTE_INTEN_NCTS_MASK (0x00000001)
|
||||
#define UARTE_INTEN_RXRDY_SHIFT (2)
|
||||
#define UARTE_INTEN_RXRDY_MASK (0x00000001)
|
||||
|
||||
// The other eight fields are skipped for brevity
|
||||
uint32_t cts = 0;
|
||||
uint32_t ncts = 1;
|
||||
uint32_t rxrdy = 1;
|
||||
|
||||
uint32_t inten_value = ((cts & UARTE_INTEN_CTS_MASK) << UARTE_INTEN_CTS_SHIFT)
|
||||
| ((ncts & UARTE_INTEN_NCTS_MASK) << UARTE_INTEN_NCTS_SHIFT)
|
||||
| ((rxrdy & UARTE_INTEN_RXRDY_MASK) << UARTE_INTEN_RXRDY_SHIFT);
|
||||
|
||||
*((volatile uint32_t*) 0x40002300) = inten_value;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding structure
|
||||
|
||||
The various registers for a peripheral are often grouped into a `struct`
|
||||
|
||||
```c,no_run
|
||||
typedef volatile struct uart0_reg_t {
|
||||
uint32_t tasks_startrx; // @ 0x000
|
||||
uint32_t tasks_stoprx; // @ 0x004
|
||||
// ...
|
||||
uint32_t inten; // @ 0x300
|
||||
uint32_t _padding[79];
|
||||
uint32_t baudrate; // @ 0x500
|
||||
} uart0_reg_t;
|
||||
```
|
||||
---
|
||||
|
||||
## Rust Code
|
||||
|
||||
You *could* do this in Rust if you wanted...
|
||||
|
||||
```rust,no_run
|
||||
const UARTE0_INTEN: *mut u32 = 0x4000_2300 as *mut u32;
|
||||
unsafe { UARTE0_INTEN.write_volatile(0x0000_0003); }
|
||||
```
|
||||
|
||||
But it seems like a lot of reading PDFs and re-typing everything?
|
||||
|
||||
---
|
||||
|
||||
## CMSIS-SVD Files
|
||||
|
||||
A CMSIS-SVD (or just SVD) file is an XML description of all the peripherals,
|
||||
registers and fields on an MCU.
|
||||
|
||||
We can use `svd2rust` to turn this into a Peripheral Access Crate.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
svd[(SVD XML)] --> svd2rust[<tt>svd2rust</tt>] --> rust[(Rust Source)]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The `svd2rust` generated API
|
||||
|
||||
* The crate has a top-level `struct Peripherals` with members for each *Peripheral*
|
||||
* Each *Peripheral* gets a `struct`, like `UARTE0`, `SPI1`, etc.
|
||||
* Each *Peripheral* `struct` has members for each *Register*
|
||||
* Each *Register* gets a `struct`, like `BAUDRATE`, `INTEN`, etc.
|
||||
* Each *Register* `struct` has `read()`, `write()` and `modify()` methods
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Peripherals --> uarte1[.UARTE1: <b>UARTE1</b>]
|
||||
uarte1 --> uart1_baudrate[.baudrate: <b>BAUDRATE</b>]
|
||||
uarte1 --> uart1_inten[.inten: <b>INTEN</b>]
|
||||
Peripherals --> uarte2[.UARTE2: <b>UARTE2</b>]
|
||||
uarte2 --> uart2_baudrate[.baudrate: <b>BAUDRATE</b>]
|
||||
uarte2 --> uart2_inten[.inten: <b>INTEN</b>]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The `svd2rust` generated API (2)
|
||||
|
||||
* The `read()` method returns a special proxy object, with methods for each *Field*
|
||||
* The `write()` method takes a closure, which is given a special 'proxy' object, with methods for each *Field*
|
||||
* All the *Field* changes are batched together and written in one go
|
||||
* Any un-written *Fields* are set to a default value
|
||||
* The `modify()` method gives you both
|
||||
* Any un-written *Fields* are left alone
|
||||
|
||||
---
|
||||
|
||||
## An example
|
||||
|
||||
```rust,no_run
|
||||
// nrf52840 is the PAC
|
||||
let p = nrf52840::Peripherals::take().unwrap();
|
||||
// This register has only one field
|
||||
let current_baud_rate = p.UARTE1.baudrate.read().baudrate();
|
||||
// This register has multiple fields
|
||||
p.UARTE1.inten.write(|w| {
|
||||
w.cts().enabled();
|
||||
w.ncts().enabled();
|
||||
w.rxrdy().enabled();
|
||||
w
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
Docs can be generated from the source code.
|
||||
|
||||
See <https://docs.rs/nrf52840>
|
||||
|
||||
Note that `uarte0` is a *module* and `UARTE0` could mean either a `struct` type,
|
||||
or a field on the `Peripherals` struct.
|
1
down-the-stack-book/src/writing_an_application.md
Normal file
1
down-the-stack-book/src/writing_an_application.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Writing an Application
|
Loading…
Reference in a new issue