@@ -3,6 +3,8 @@ members = [
|
||||
"src/getting-started",
|
||||
"src/hello-world",
|
||||
"src/display",
|
||||
"src/microbit",
|
||||
"src/serial",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
> Learn embedded software in Rust on the micro:bit
|
||||
|
||||
[**droogmic.github.io/microrust**](https://droogmic.github.io/microrust/)
|
||||
|
||||
## License
|
||||
|
||||
The documentation is licensed under
|
||||
@@ -23,6 +25,7 @@ at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Contribution is welcome by either submitting an issue or a pull request.
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
licensed as above, without any additional terms or conditions.
|
||||
|
||||
@@ -5,14 +5,34 @@
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
[microbit]: https://microbit.org/
|
||||
|
||||
This book is an introductory course on microcontroller-based embedded systems that uses Rust as the
|
||||
teaching language (rather than the usual C/C++), and the micro:bit as target system.
|
||||
This book is an introductory course on microcontroller-based embedded systems
|
||||
that uses Rust as the teaching language (rather than the usual C/C++),
|
||||
and the micro:bit as the target system.
|
||||
|
||||
## Approach
|
||||
|
||||
- Beginner friendly.
|
||||
No previous experience with microcontrollers or embedded systems is required.
|
||||
|
||||
- Hands on.
|
||||
*You* will be doing most of the work here.
|
||||
When possible, pages will end on a problem for you to solve, with the solution on the next page.
|
||||
There are plenty of exercises to put the theory into practice.
|
||||
|
||||
- Standard.
|
||||
We'll make plenty use of standard tooling and processes to ease development
|
||||
so you can apply the skills learnt to any Rust embedded project.
|
||||
Fixing compiler errors, debugging with GDB, and logging will be introduced early on.
|
||||
Using LEDs as a debugging mechanism has no place here.
|
||||
|
||||
## Scope
|
||||
|
||||
The following topics will be covered:
|
||||
The following topics are covered in the core chapters:
|
||||
|
||||
- How to write, build, flash and debug an embedded program.
|
||||
- Basic operation of a GPIO, ubiquitous in microcontrollers.
|
||||
|
||||
The rest of the chapters are independent, only requiring the core knowledge:
|
||||
|
||||
- Functionality ("peripherals") commonly found in microcontrollers:
|
||||
- Digital input and output, including buttons and LEDs
|
||||
@@ -26,20 +46,6 @@ The following topics will be covered:
|
||||
<!-- - Control systems concepts: sensors, calibration, digital filters, actuators, open loop control,
|
||||
closed loop control, etc. -->
|
||||
|
||||
## Approach
|
||||
|
||||
- Beginner friendly.
|
||||
No previous experience with microcontrollers or embedded systems is required.
|
||||
|
||||
- Hands on.
|
||||
*You* will be doing most of the work here.
|
||||
When possible, pages will end on a problem for you to solve, with the solution on the next page.
|
||||
Plenty of exercises to put the theory into practice.
|
||||
|
||||
- Standard. We'll make plenty use of standard tooling and processes to ease development.
|
||||
Fixing compiler errors, debugging with GDB, and logging will be introduced early on.
|
||||
Using LEDs as a debugging mechanism has no place here.
|
||||
|
||||
## Non-goals
|
||||
|
||||
What's out of scope for this book:
|
||||
|
||||
@@ -23,10 +23,29 @@
|
||||
- [Serial communication](hello-world/02.00.UART.md)
|
||||
- [*nix](hello-world/02.01.NIX.md)
|
||||
- [Windows](hello-world/02.02.WINDOWS.md)
|
||||
- [LED](hello-world/03.00.LED.md)
|
||||
- [GPIO and LEDs](hello-world/03.00.LED.md)
|
||||
- [Solution](hello-world/03.01.SOLUTION.md)
|
||||
|
||||
- [Choose your own adventure](choice/00.00.README.md)
|
||||
- [Choose Your Own Adventure](choice/00.00.README.md)
|
||||
|
||||
- [WIP - micro:bit HAL basics](microbit/00.00.README.md)
|
||||
- [WIP - Buttons](microbit/01.00.BUTTONS.md)
|
||||
- [WIP - Delays](microbit/02.00.DELAY.md)
|
||||
- [WIP - Display](microbit/03.00.DISPLAY.md)
|
||||
|
||||
- [WIP - Serial UART](serial/00.00.README.md)
|
||||
- [Echo Server](serial/01.00.ECHO.md)
|
||||
- [Theory](serial/01.01.THEORY.md)
|
||||
- [Solution](serial/01.02.ECHO.md)
|
||||
- [Exercises](serial/02.00.md)
|
||||
- [Reverse echo](serial/02.01.md)
|
||||
- [Solution](serial/02.01.SOLUTION.md)
|
||||
- [Countdown](serial/02.02.md)
|
||||
- [Solution](serial/02.02.SOLUTION.md)
|
||||
- [Display echo](serial/02.03.md)
|
||||
- [Solution](serial/02.03.SOLUTION.md)
|
||||
- [Quiz](serial/02.04.md)
|
||||
- [Solution](serial/02.04.SOLUTION.md)
|
||||
|
||||
- [LED display](display/00.00.README.md)
|
||||
- [Theory](display/01.00.THEORY.md)
|
||||
@@ -43,7 +62,7 @@
|
||||
|
||||
- [WIP - Real time](rtfm/00.00.README.md)
|
||||
|
||||
- [WIP - HAL](hal/00.00.README.md)
|
||||
- [WIP - Creating a HAL](hal/00.00.README.md)
|
||||
<!-- - [Registers](hal/01.00.REGISTERS.md)
|
||||
- [Peripherals](hal/01.00.PERIPHERALS.md)
|
||||
- [Clocks and timers](hal/02.00.DELAY.md) -->
|
||||
@@ -53,14 +72,3 @@
|
||||
[GDB cheatsheet](appendix/gdb.md)
|
||||
|
||||
[General troubleshooting](appendix/troubleshooting.md)
|
||||
|
||||
<!-- - [LED roulette](05-led-roulette/README.md)
|
||||
- [Build it](05-led-roulette/build-it.md)
|
||||
- [Flash it](05-led-roulette/flash-it.md)
|
||||
- [Debug it](05-led-roulette/debug-it.md)
|
||||
- [The `led` and `delay` abstractions](05-led-roulette/the-led-and-delay-abstractions.md)
|
||||
- [The challenge](05-led-roulette/the-challenge.md)
|
||||
- [My solution](05-led-roulette/my-solution.md) -->
|
||||
|
||||
<!-- - [Hello, world!](06-hello-world/README.md)
|
||||
- [`panic!`](06-hello-world/panic.md) -->
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
At this point of the book,
|
||||
you know the basics to get started with embedded development with Rust.
|
||||
|
||||
The following chapters are all independent of each other.
|
||||
This means they can be done in any order,
|
||||
and the only required knowledge is found in the chapters before this.
|
||||
The following chapters are more independent of each other, and can be done in any order;
|
||||
the only required knowledge is found in the chapters before this.
|
||||
If attempting the exerises, it is best to follow the book in order to pace the difficulty.
|
||||
|
||||
Feel free to either follow the rest of the book in order, or choose a chapter which interests you.
|
||||
|
||||
> A large portion of this book is still unfinished and we would love your support.
|
||||
> A large portion of this book is still unfinished and I would love your support.
|
||||
> Please submit an issue to request a new section, and a pull request to add a section.
|
||||
@@ -17,7 +17,7 @@ We need to give OCD the name of the interfaces we are using:
|
||||
|
||||
``` console
|
||||
$ # All
|
||||
$ # Windows: remember that you need an extra `-s %PATH_TO_OPENOCD%\share\scripts`
|
||||
$ # Windows: remember that you need an extra `-s %PATH_TO_OPENOCD%\<version>\scripts`
|
||||
$ openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg
|
||||
```
|
||||
|
||||
@@ -79,7 +79,7 @@ Reading symbols from target/thumbv6m-none-eabi/debug/rustled...done.
|
||||
This only opens a GDB shell. To actually connect to the OpenOCD GDB server, use the following
|
||||
command within the GDB shell:
|
||||
|
||||
```
|
||||
``` gdb
|
||||
(gdb) target remote :3333
|
||||
Remote debugging using :3333
|
||||
0x00000000 in ?? ()
|
||||
@@ -98,7 +98,7 @@ After entering this command, you'll see new output in the OpenOCD terminal:
|
||||
|
||||
Almost there. To flash the device, we'll use the `load` command inside the GDB shell:
|
||||
|
||||
```
|
||||
``` gdb
|
||||
(gdb) load
|
||||
Loading section .vector_table, size 0x188 lma 0x8000000
|
||||
Loading section .text, size 0x38a lma 0x8000188
|
||||
@@ -133,7 +133,7 @@ And that's it. You'll also see new output in the OpenOCD terminal.
|
||||
|
||||
Our program is loaded, we can now run it!
|
||||
|
||||
```
|
||||
``` gdb
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
```
|
||||
@@ -148,7 +148,8 @@ Before we move on though, we are going to add one more file to our project.
|
||||
This will automate the last few steps so we don't need to repeatedly do the same actions in gdb:
|
||||
|
||||
`.gdbinit`
|
||||
```
|
||||
|
||||
``` gdbinit
|
||||
# Connects GDB to OpenOCD server port
|
||||
target remote :3333
|
||||
# (optional) Unmangle function names when debugging
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Hello world
|
||||
|
||||
In this chapter, we will discuss the basic I/O of embedded development in rust.
|
||||
|
||||
After this chapter,you should have all the neccesary basic knowledge to do embedded development in Rust,
|
||||
with anything remaining being solution specific.
|
||||
|
||||
@@ -63,7 +63,7 @@ panicked at 'test-panic', src/hello-world/src/main.rs:27:5
|
||||
|
||||
## stdout
|
||||
|
||||
Finally, this is how to write to stdout, although writing to stderr is just as easy.
|
||||
Finally, this is how to write to stdout.
|
||||
|
||||
``` rust
|
||||
extern crate cortex_m_semihosting as sh;
|
||||
@@ -76,4 +76,4 @@ use sh::hio;
|
||||
writeln!(hio::hstdout().unwrap(), "Init").unwrap();
|
||||
```
|
||||
|
||||
And that's it!
|
||||
Writing to stderr is just as easy.
|
||||
|
||||
@@ -12,9 +12,7 @@ The data rate is called the _baud rate_, and we will use 115200bps.
|
||||
|
||||
## USB
|
||||
|
||||
The micro:bit allows us to transmit and receive this serial communication over USB
|
||||
with no additional hardware,
|
||||
along with our flashing and debugging activities.
|
||||
The micro:bit allows us to transmit and receive this serial communication over USB with no additional hardware.
|
||||
|
||||
## Tooling
|
||||
|
||||
@@ -35,19 +33,19 @@ if let Some(p) = microbit::Peripherals::take() {
|
||||
// Configure RX and TX pins accordingly
|
||||
let tx = gpio.pin24.into_push_pull_output().downgrade();
|
||||
let rx = gpio.pin25.into_floating_input().downgrade();
|
||||
// Configure serial communication
|
||||
let (mut tx, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
|
||||
// Write string with newline and carriage return
|
||||
// This could also be a format string
|
||||
let _ = write!(tx, "serial test\n\r");
|
||||
}
|
||||
```
|
||||
|
||||
In minicom/PuTTY you should see:
|
||||
|
||||
```
|
||||
Welcome to minicom 2.7.1
|
||||
|
||||
OPTIONS: I18n
|
||||
Compiled on Jun 5 2018, 10:54:41.
|
||||
Port /dev/ttyACM0, 19:50:57
|
||||
|
||||
Press CTRL-A Z for help on special keys
|
||||
|
||||
serial test
|
||||
```
|
||||
|
||||
This is a very simple introduction to using the UART as one way serial logging.
|
||||
The chapter on UART serial communication goes into much more detail.
|
||||
|
||||
@@ -66,6 +66,17 @@ $ minicom -D /dev/ttyUSB0 -b 115200
|
||||
This tells `minicom` to open the serial device at `/dev/ttyUSB0` and set its baud rate to 115200.
|
||||
A text-based user interface (TUI) will pop out.
|
||||
|
||||
```
|
||||
Welcome to minicom 2.7.1
|
||||
|
||||
OPTIONS: I18n
|
||||
Compiled on Jun 5 2018, 10:54:41.
|
||||
Port /dev/ttyACM0, 19:50:57
|
||||
|
||||
Press CTRL-A Z for help on special keys
|
||||
|
||||
```
|
||||
|
||||
You can now send data using the keyboard! Go ahead and type something.
|
||||
Note that the TUI *won't* echo back what you type (nothing will happen when you type)
|
||||
but you'll see TX (red) LED on the serial module blink with each keystroke.
|
||||
|
||||
@@ -1,17 +1,39 @@
|
||||
# LED
|
||||
# GPIO and LEDs
|
||||
|
||||
## GPIO
|
||||
|
||||
> GPIO: General purpose input-output
|
||||
|
||||
The GPIO is a block of pins found on nearly all microcontrollers.
|
||||
As the name implies, they are general-purpose, configureable, analog or digital, input or output, electrical pins.
|
||||
Exactly what features each pin has on a given microcontroller will require looking at a datasheet.
|
||||
|
||||
> Analog vs Digital: Analog signals carry data in their amplitude as they continuously vary over time,
|
||||
> whereas digital signals have a fixed rate and fixed amplitudes. Digital signals are usually just 0 or 1, i.e. 0V or 3.3V
|
||||
|
||||
## LED
|
||||
|
||||
> LED: Light emitting diode
|
||||
|
||||
Let us now turn on an LED! But how?
|
||||
|
||||
Well, first we should look at the [documentation of our crate][microbit],
|
||||
Many integrated periperals like LEDs and buttons are already connected to certain GPIO pins,
|
||||
so lighting up an LED can be as simple as configuring a GPIO pin to be a digital output.
|
||||
|
||||
First we should look at the [documentation of our crate][microbit],
|
||||
and you should be able to figure out how to get access to the gpio,
|
||||
and set individual pins high and low:
|
||||
|
||||
[microbit]: https://docs.rs/microbit/0.5.1/microbit/
|
||||
|
||||
``` rust
|
||||
// This takes singleton ownership of the micro:bit's peripherals
|
||||
if let Some(p) = microbit::Peripherals::take() {
|
||||
// Take the micro:bit's GPIO
|
||||
let mut gpio = p.GPIO.split();
|
||||
// Take pin 1 of the GPIO, and configure it as a digital output
|
||||
let mut pin1 = gpio.pin1.into_push_pull_output();
|
||||
// Set pin 1 high
|
||||
pin1.set_high();
|
||||
}
|
||||
```
|
||||
@@ -22,12 +44,15 @@ On the first sheet you should find a diagram with a grid of numbered LEDs.
|
||||
|
||||
> If you do not know much about electronics:
|
||||
> Each row and column (labelled ROW and COL) represent a GPIO output pin.
|
||||
> The components labelled LED are light emitting diodes.
|
||||
> The components labelled are LEDs.
|
||||
> LEDs only let current flow one way, and only emit light when current is flowing.
|
||||
> If a row is set high, high voltage, and a column is set low, low voltage,
|
||||
> the LED at the point that they cross will have a potential difference across it;
|
||||
> current will flow and it will light up.
|
||||
|
||||
As you can see, the micro:bit's display LEDs are a bit more complicated than being connected to a single pin.
|
||||
Each LED is connected to 2 pins, where one needs to be high, and the other low for the LED to light up.
|
||||
|
||||
The 5x5 array of LEDs are actually wired up as a 3x9 array (3 rows by 9 columns), with 2 missing.
|
||||
This is usually done to make the circuit design easier.
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ fn main() -> ! {
|
||||
let _ = gpio.pin4.into_push_pull_output();
|
||||
// Set row high (column starts low)
|
||||
led.set_high();
|
||||
// Write string with newline and carriage return
|
||||
write!(tx, "serial - LED on\r\n");
|
||||
}
|
||||
panic!("End");
|
||||
|
||||
12
src/serial/.gdbinit
Normal file
12
src/serial/.gdbinit
Normal file
@@ -0,0 +1,12 @@
|
||||
# Connects GDB to OpenOCD server port
|
||||
target remote :3333
|
||||
# (optional) Unmangle function names when debugging
|
||||
set print asm-demangle on
|
||||
# Enable semihosting
|
||||
monitor arm semihosting enable
|
||||
# Load your program, breaks at entry
|
||||
load
|
||||
# (optional) Add breakpoint at function
|
||||
break serial::main
|
||||
# Continue with execution
|
||||
continue
|
||||
9
src/serial/00.00.README.md
Normal file
9
src/serial/00.00.README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# WIP - UART serial server
|
||||
|
||||
In the first section of this book we saw how to do a simple debug print using serial.
|
||||
This is useful for logging and debugging, but does not cover the full potential of the UART peripheral.
|
||||
|
||||
## Input and output
|
||||
|
||||
The simultaneous input and output capabilities of the UART allow for both your computer and the micro:bit to act as a server.
|
||||
They can receive a transmission, process it, perform some action, and send a response.
|
||||
19
src/serial/01.00.ECHO.md
Normal file
19
src/serial/01.00.ECHO.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Echo Server
|
||||
|
||||
An echo server, is probably the simplest server we could make.
|
||||
It should receive a message, and echo it back to the sender.
|
||||
|
||||
We earlier said that minicom/PuTTY would transmit any keystrokes we send,
|
||||
and display and data received,
|
||||
so the end result should be the familiar experience of typing and seeing the letters typed appear as expected.
|
||||
|
||||
## Flow
|
||||
|
||||
1. The character `a` is typed
|
||||
2. minicom/PuTTY encodes the `a` character's unicode code point (`097` in decimal) as a word in a serial frame
|
||||
3. The frame is transmitted to the micro:bit over USB
|
||||
4. The micro:bit software decodes the frame to get the word
|
||||
5. The microbit software re-encodes the word to get a (new but identical) frame
|
||||
6. The frame is transmitted to the computer over USB
|
||||
7. minicom/PuTTY decodes the frame's word as a unicode code point
|
||||
8. The letter `a` is displayed
|
||||
78
src/serial/01.01.THEORY.md
Normal file
78
src/serial/01.01.THEORY.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Serial Theory
|
||||
|
||||
The micro:bit core crate implements the embedded_hal::serial::Write and embedded_hal::serial::Read traits for the tx and rx pins respectively.
|
||||
|
||||
## `writeln!` and Carriage Return
|
||||
|
||||
In the introduction page on serial communication, I brushed over this:
|
||||
|
||||
``` rust
|
||||
// Write string with newline and carriage return
|
||||
let _ = write!(tx, "serial test\r\n");
|
||||
```
|
||||
|
||||
A naïve assumption would be to try the seemingly more correct `writeln!` macro:
|
||||
|
||||
``` rust
|
||||
// Write string with newline and carriage return
|
||||
let _ = writeln!(tx, "serial test");
|
||||
```
|
||||
|
||||
This will usually fail to do what is intended,
|
||||
as multiple writes will only print one line in PuTTY,
|
||||
and produce the following in minicom:
|
||||
|
||||
```
|
||||
serial test
|
||||
serial test
|
||||
serial test
|
||||
serial test
|
||||
```
|
||||
|
||||
Your choices are to either configure minicom and PuTTY appropriately or use `write!` with `\r\n`.
|
||||
|
||||
### Control Characters
|
||||
|
||||
The control characters operate based on a print head, as used in [teleprinters][tty].
|
||||
|
||||
`\r` - Carriage Return - The print head is moves left to the start of the line.
|
||||
`\n` - Line Feed - The print head moves down once to a new line.
|
||||
|
||||
[tty]: https://en.wikipedia.org/wiki/Teleprinter
|
||||
|
||||
### `writeln!` macro
|
||||
|
||||
The `writeln!` macro should append a new line,
|
||||
but he [documentation for core::writeln][doc] says:
|
||||
|
||||
> On all platforms, the newline is the LINE FEED character (\n/U+000A) alone (no additional CARRIAGE RETURN (\r/U+000D).
|
||||
|
||||
[doc]: https://doc.rust-lang.org/core/macro.writeln.html
|
||||
|
||||
### minicom
|
||||
|
||||
CTRL-A + Z will tell you that CTRL-A + U will add a carriage return.
|
||||
This will add a carriage return to a received `\n`
|
||||
|
||||
### PuTTY
|
||||
|
||||
In PuTTY, you can enable enable `Implicit LF in every CR` under Terminal options.
|
||||
|
||||
## Blocking
|
||||
|
||||
Behind the scenes, `embedded_hal::serial` uses the nb crate to allow for blocking and non-blocking operation.
|
||||
This is implemented in embedded_hal crates by returning nb::Error::WouldBlock
|
||||
when a read or write action cannot be performed immediately.
|
||||
In this chapter, we will only be using read and write as simple blocking calls.
|
||||
|
||||
### `block!`
|
||||
|
||||
The `block!` macro provided by the crate continuously calls the expression
|
||||
contained until it no longer returns Error::WouldBlock.
|
||||
|
||||
## Tx - `embedded_hal::serial::Write` or `core::fmt::Write`
|
||||
|
||||
The `write!` and `writeln!` macros call `write_str` of the `core::fmt::Write` trait which is implemented for Tx.
|
||||
`write_str` is implemented as a blocking call to `write` of the `embedded_hal::serial::Write` trait.
|
||||
|
||||
This means `write!(tx, "a")` is equivalent to `block!(tx.write(b'a'))`.
|
||||
3
src/serial/01.02.ECHO.md
Normal file
3
src/serial/01.02.ECHO.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Echo Solution
|
||||
|
||||
{{#include src/main.rs}}
|
||||
@@ -1,7 +1,5 @@
|
||||
# Windows
|
||||
|
||||
> UNTESTED: please submit an issue if you can confirm this works.
|
||||
|
||||
## `arm-none-eabi-*`
|
||||
|
||||
ARM provides `.exe` installers for Windows. Grab one from [here][gcc], and follow the instructions.
|
||||
|
||||
Reference in New Issue
Block a user