Multiple changes

Fix #1
Add serial chapter
Small fixes everywhere
This commit is contained in:
Michael Droogleever
2018-08-18 01:46:27 +02:00
parent 4be31b3ab7
commit 52b530ed2c
18 changed files with 235 additions and 60 deletions

View File

@@ -3,6 +3,8 @@ members = [
"src/getting-started",
"src/hello-world",
"src/display",
"src/microbit",
"src/serial",
]
[profile.dev]

View File

@@ -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.

View File

@@ -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:

View File

@@ -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) -->

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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
View 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

View 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
View 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

View 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
View File

@@ -0,0 +1,3 @@
# Echo Solution
{{#include src/main.rs}}

View File

@@ -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.