diff --git a/src/03-setup/README.md b/src/03-setup/README.md
index 9f8840107..4771e9539 100644
--- a/src/03-setup/README.md
+++ b/src/03-setup/README.md
@@ -29,7 +29,7 @@ should work but we have listed the version we have tested.
[`cargo-binutils`]: https://github.com/rust-embedded/cargo-binutils
-- [`cargo-embed`]. Version 0.9.0 or newer.
+- [`cargo-embed`]. Version 0.9.1 or newer.
[`cargo-embed`]: https://github.com/probe-rs/cargo-embed
diff --git a/src/04-meet-your-hardware/README.md b/src/04-meet-your-hardware/README.md
index e96846da3..ad2089b9e 100644
--- a/src/04-meet-your-hardware/README.md
+++ b/src/04-meet-your-hardware/README.md
@@ -2,22 +2,19 @@
Let's get familiar with the hardware we'll be working with.
-## STM32F3DISCOVERY (the "F3")
+## micro:bit
-
+
-We'll refer to this board as "F3" throughout this book. Here are some of the
-many components on the board:
+Here are some of the many components on the board:
- A [microcontroller].
-- A number of LEDs, including the eight aligned in a "compass" formation.
-- Two buttons.
-- Two USB ports.
-- An [accelerometer].
-- A [magnetometer].
-- A [gyroscope].
+- A number of LEDs, most notably the LED matrix on the back
+- Two user buttons as well as a reset button (the one next to the USB port).
+- One USB port.
+- A sensor that is both a [magnetometer] and an [accelerometer]
[microcontroller]: https://en.wikipedia.org/wiki/Microcontroller
[accelerometer]: https://en.wikipedia.org/wiki/Accelerometer
@@ -25,17 +22,20 @@ many components on the board:
[gyroscope]: https://en.wikipedia.org/wiki/Gyroscope
Of these components, the most important is the microcontroller (sometimes
-shortened to "MCU" for "microcontroller unit"), which is the large black square
-sitting in the center of your board. The MCU is what runs your code. You might
-sometimes read about "programming a board", when in reality what we are doing
-is programming the MCU that is installed on the board.
+shortened to "MCU" for "microcontroller unit"), which is the bigger of the two
+black squares sitting on the side of the board with the USB port. The MCU is
+what runs your code. You might sometimes read about "programming a board", when
+in reality what we are doing is programming the MCU that is installed on the board.
-## STM32F303VCT6 (the "STM32F3")
+If you happen to be interested in a more in detail description of the board you
+can checkout the [micro:bit website](https://tech.microbit.org/hardware/).
+
+## Nordic nRF51822 (the "nRF51")
Since the MCU is so important, let's take a closer look at the one sitting on our board.
-Our MCU is surrounded by 100 tiny metal **pins**. These pins are connected to
-**traces**, the little "roads" that act as the wires connecting components
+Our MCU has 48 tiny metal **pins** sitting right underneath it (it's a so called [QFN48] chip).
+These pins are connected to **traces**, the little "roads" that act as the wires connecting components
together on the board. The MCU can dynamically alter the electrical properties
of the pins. This works similar to a light switch altering how electrical
current flows through a circuit. By enabling or disabling electrical current to
@@ -44,68 +44,57 @@ be turned on and off.
Each manufacturer uses a different part numbering scheme, but many will allow
you to determine information about a component simply by looking at the part
-number. Looking at our MCU's part number (`STM32F303VCT6`), the `ST` at the
-front hints to us that this is a part manufactured by [ST Microelectronics].
-Searching through [ST's marketing materials] we can also learn the following:
-
-[ST Microelectronics]: https://st.com/
-[ST's marketing materials]: https://www.st.com/en/microcontrollers-microprocessors/stm32-mainstream-mcus.html
-
-- The `M32` represents that this is an Arm®-based 32-bit microcontroller.
-- The `F3` represents that the MCU is from ST's "STM32F3" series. This is a
- series of MCUs based on the Cortex®-M4 processor design.
-- The remainder of the part number goes into more details about things like
- extra features and RAM size, which at this point we're less concerned about.
-
-> ### Arm? Cortex-M4?
->
-> If our chip is manufactured by ST, then who is Arm? And if our chip is the
-> STM32F3, what is the Cortex-M4?
->
-> You might be surprised to hear that while "Arm-based" chips are quite
-> popular, the company behind the "Arm" trademark ([Arm Holdings][]) doesn't
-> actually manufacture chips for purchase. Instead, their primary business
-> model is to just *design* parts of chips. They will then license those designs to
-> manufacturers, who will in turn implement the designs (perhaps with some of
-> their own tweaks) in the form of physical hardware that can then be sold.
-> Arm's strategy here is different from companies like Intel, which both
-> designs *and* manufactures their chips.
->
-> Arm licenses a bunch of different designs. Their "Cortex-M" family of designs
-> are mainly used as the core in microcontrollers. For example, the Cortex-M0
-> is designed for low cost and low power usage. The Cortex-M7 is higher cost,
-> but with more features and performance. The core of our STM32F3 is based on
-> the Cortex-M4, which is in the middle: more features and performance than the
-> Cortex-M0, but less expensive than the Cortex-M7.
->
-> Luckily, you don't need to know too much about different types of processors
-> or Cortex designs for the sake of this book. However, you are hopefully now a
-> bit more knowledgeable about the terminology of your device. While you are
-> working specifically with an STM32F3, you might find yourself reading
-> documentation and using tools for Cortex-M-based chips, as the STM32F3 is
-> based on a Cortex-M design.
+number. Looking at our MCU's part number (`nRF51822-QFAA-R`, you probably cannot
+see it with your bare eye, but it is on the chip), the `n` at the
+front hints to us that this is a part manufactured by [Nordic Semiconductor].
+Looking up the part number on their website we quickly find the [product page].
+There we learn that our chip's main marketing point is that it is a
+"Bluetooth Low Energy and 2.4 GHz SoC" (SoC being short for "System on a Chip"),
+which explains the RF in the product name since RF is short for radio frequency.
+If we search through the documentation of the chip linked on the [product page]
+for a bit we find the [product specification] which contains chapter 10 "Ordering Information"
+dedicated to explaining the weird chip naming. Here we learn that:
+
+[QFN48]: https://en.wikipedia.org/wiki/Flat_no-leads_package
+[Nordic Semiconductor]: https://www.nordicsemi.com/
+[product page]: https://www.nordicsemi.com/Products/Low-power-short-range-wireless/nRF51822
+[product specification]: https://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.3.pdf
+
+- The `nRF51` is the MCU's series, indicating that there are other `nRF51` MCUs
+- The `822` is the part code
+- The `QF` is short for `QFN48`
+- The `AA` is the variant code, indicating how much RAM and flash memory the MCU has,
+ in our case 256 kilobyte flash and 16 kilobyte RAM
+- The `R` is the packaging code which is relevant for factories manufacturing boards
+ with this chip on them in larger scales
+
+The product specification does of course contain a lot more useful information about
+the chip, for example that it is based on an ARM® Cortex™-M0 32 bit processor.
+
+### Arm? Cortex-M0?
+
+If our chip is manufactured by Nordic, then who is Arm? And if our chip is the
+nRF51822, what is the Cortex-M0?
+
+You might be surprised to hear that while "Arm-based" chips are quite
+popular, the company behind the "Arm" trademark ([Arm Holdings][]) doesn't
+actually manufacture chips for purchase. Instead, their primary business
+model is to just *design* parts of chips. They will then license those designs to
+manufacturers, who will in turn implement the designs (perhaps with some of
+their own tweaks) in the form of physical hardware that can then be sold.
+Arm's strategy here is different from companies like Intel, which both
+designs *and* manufactures their chips.
+
+Arm licenses a bunch of different designs. Their "Cortex-M" family of designs
+are mainly used as the core in microcontrollers. For example, the Cortex-M0
+(the core our chip is based on) is designed for low cost and low power usage.
+The Cortex-M7 is higher cost, but with more features and performance.
+
+Luckily, you don't need to know too much about different types of processors
+or Cortex designs for the sake of this book. However, you are hopefully now a
+bit more knowledgeable about the terminology of your device. While you are
+working specifically with an nRF51822, you might find yourself reading
+documentation and using tools for Cortex-M-based chips, as the nRF51822 is
+based on a Cortex-M design.
[Arm Holdings]: https://www.arm.com/
-
-## The Serial module
-
-
-
-
-
-If you have an older revision of the discovery board, you can use this module to
-exchange data between the microcontroller in the F3 and your computer. This module
-will be connected to your computer using an USB cable. I won't say more at this
-point.
-
-If you have a newer release of the board then you don't need this module. The
-ST-LINK will double as a USB<->serial converter connected to the microcontroller USART1 at pins PC4 and PC5.
-
-## The Bluetooth module
-
-
-
-
-
-This module has the exact same purpose as the serial module but it sends the data over Bluetooth
-instead of over USB.
diff --git a/src/05-led-roulette/.cargo/config b/src/05-led-roulette/.cargo/config
index 01d25c8b3..a0ec1777f 100644
--- a/src/05-led-roulette/.cargo/config
+++ b/src/05-led-roulette/.cargo/config
@@ -1,5 +1,7 @@
-[target.thumbv7em-none-eabihf]
-runner = "arm-none-eabi-gdb -q"
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
"-C", "link-arg=-Tlink.x",
]
+
+[build]
+target = "thumbv6m-none-eabi"
diff --git a/src/05-led-roulette/Cargo.toml b/src/05-led-roulette/Cargo.toml
index 43ac7579f..776e8a465 100644
--- a/src/05-led-roulette/Cargo.toml
+++ b/src/05-led-roulette/Cargo.toml
@@ -1,8 +1,13 @@
[package]
-authors = ["Jorge Aparicio "]
-edition = "2018"
name = "led-roulette"
version = "0.1.0"
+authors = ["Henrik Böving "]
+edition = "2018"
[dependencies]
-aux5 = { path = "auxiliary" }
+cortex-m = "0.6.0"
+cortex-m-rt = "0.6.10"
+panic-halt = "0.2.0"
+nrf51-hal = "0.11.0"
+rtt-target = { version = "0.2.2", features = ["cortex-m"] }
+panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
diff --git a/src/05-led-roulette/Embed.toml b/src/05-led-roulette/Embed.toml
new file mode 100644
index 000000000..91584ec92
--- /dev/null
+++ b/src/05-led-roulette/Embed.toml
@@ -0,0 +1,11 @@
+[default.general]
+chip = "nrf51822_xxAA"
+
+[default.reset]
+halt_afterwards = true
+
+[default.rtt]
+enabled = false
+
+[default.gdb]
+enabled = true
diff --git a/src/05-led-roulette/README.md b/src/05-led-roulette/README.md
index 1f5ccdd56..b3e928d6a 100644
--- a/src/05-led-roulette/README.md
+++ b/src/05-led-roulette/README.md
@@ -3,22 +3,17 @@
Alright, let's start by building the following application:
-
+
I'm going to give you a high level API to implement this app but don't worry we'll do low level
stuff later on. The main goal of this chapter is to get familiar with the *flashing* and debugging
process.
-Throughout this text we'll be using the starter code that's in the [discovery] repository. Make sure
-you always have the latest version of the master branch because this website tracks that branch.
-
The starter code is in the `src` directory of that repository. Inside that directory there are more
directories named after each chapter of this book. Most of those directories are starter Cargo
projects.
-[discovery]: https://github.com/rust-embedded/discovery
-
Now, jump into the `src/05-led-roulette` directory. Check the `src/main.rs` file:
``` rust
@@ -46,4 +41,18 @@ as well. This directory contains a Cargo configuration file (`.cargo/config`) th
linking process to tailor the memory layout of the program to the requirements of the target device.
This modified linking process is a requirement of the `cortex-m-rt` crate.
+Furthermore there is also an `Embed.toml` file
+
+```toml
+{{#include Embed.toml}}
+```
+
+This file tells `cargo-embed` that:
+
+* we are working with a nrf51822,
+* we want to halt the chip after we flashed it so our program does not instantly jump to the loop
+* we want to disable RTT, RTT being a protocol that allows the chip to send text to a debugger.
+ You have in fact already seen RTT in action, it was the protocol that sent "Hello World" in chapter 3.
+* we want to enable GDB, this will be required for the debugging procedure
+
Alright, let's start by building this program.
diff --git a/src/05-led-roulette/auxiliary/Cargo.toml b/src/05-led-roulette/auxiliary/Cargo.toml
deleted file mode 100644
index 1b7b80f55..000000000
--- a/src/05-led-roulette/auxiliary/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-authors = ["Jorge Aparicio "]
-edition = "2018"
-name = "aux5"
-version = "0.1.0"
-
-[dependencies]
-cortex-m = "0.6.3"
-panic-halt = "0.2.0"
-cortex-m-rt = "0.6.3"
-
-[dependencies.f3]
-features = ["rt"]
-version = "0.6.1"
diff --git a/src/05-led-roulette/auxiliary/src/lib.rs b/src/05-led-roulette/auxiliary/src/lib.rs
deleted file mode 100644
index 2218b18e5..000000000
--- a/src/05-led-roulette/auxiliary/src/lib.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-//! Initialization code
-
-#![no_std]
-
-#[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964
-extern crate panic_halt; // panic handler
-
-pub use cortex_m_rt::entry;
-pub use f3::{
- hal::{delay::Delay, prelude},
- led::Leds,
-};
-
-use f3::hal::{prelude::*, stm32f30x};
-
-pub fn init() -> (Delay, Leds) {
- let cp = cortex_m::Peripherals::take().unwrap();
- let dp = stm32f30x::Peripherals::take().unwrap();
-
- let mut flash = dp.FLASH.constrain();
- let mut rcc = dp.RCC.constrain();
-
- let clocks = rcc.cfgr.freeze(&mut flash.acr);
-
- let delay = Delay::new(cp.SYST, clocks);
-
- let leds = Leds::new(dp.GPIOE.split(&mut rcc.ahb));
-
- (delay, leds)
-}
diff --git a/src/05-led-roulette/build-it.md b/src/05-led-roulette/build-it.md
index 420cf524b..3766f0a96 100644
--- a/src/05-led-roulette/build-it.md
+++ b/src/05-led-roulette/build-it.md
@@ -5,21 +5,23 @@ architecture than your computer we'll have to cross compile. Cross compiling in
as passing an extra `--target` flag to `rustc`or Cargo. The complicated part is figuring out the
argument of that flag: the *name* of the target.
-The microcontroller in the F3 has a Cortex-M4F processor in it. `rustc` knows how to cross compile
-to the Cortex-M architecture and provides 4 different targets that cover the different processor
+The microcontroller in the micro:bit has a Cortex-M0 processor in it. `rustc` knows how to cross compile
+to the Cortex-M architecture and provides several different targets that cover the different processor
families within that architecture:
- `thumbv6m-none-eabi`, for the Cortex-M0 and Cortex-M1 processors
- `thumbv7m-none-eabi`, for the Cortex-M3 processor
- `thumbv7em-none-eabi`, for the Cortex-M4 and Cortex-M7 processors
- `thumbv7em-none-eabihf`, for the Cortex-M4**F** and Cortex-M7**F** processors
+- `thumbv8m.main-none-eabi`, for the Cortex-M33 and Cortex-M35P processors
+- `thumbv8m.main-none-eabihf`, for the Cortex-M33**F** and Cortex-M35P**F** processors
-For the F3, we'll use the `thumbv7em-none-eabihf` target. Before cross compiling you have to
+For the micro:bit, we'll use the `thumbv6m-none-eabi` target. Before cross compiling you have to
download pre-compiled version of the standard library (a reduced version of it actually) for your
target. That's done using `rustup`:
``` console
-$ rustup target add thumbv7em-none-eabihf
+$ rustup target add thumbv6m-none-eabi
```
You only need to do the above step once; `rustup` will re-install a new standard library
@@ -30,48 +32,54 @@ With the `rust-std` component in place you can now cross compile the program usi
``` console
$ # make sure you are in the `src/05-led-roulette` directory
-$ cargo build --target thumbv7em-none-eabihf
+$ cargo build --target thumbv6m-none-eabi
Compiling semver-parser v0.7.0
- Compiling aligned v0.1.1
- Compiling libc v0.2.35
- Compiling bare-metal v0.1.1
- Compiling cast v0.2.2
- Compiling cortex-m v0.4.3
- (..)
- Compiling stm32f30x v0.6.0
- Compiling stm32f30x-hal v0.1.2
- Compiling aux5 v0.1.0 (file://$PWD/aux)
- Compiling led-roulette v0.1.0 (file://$PWD)
- Finished dev [unoptimized + debuginfo] target(s) in 35.84 secs
+ Compiling typenum v1.12.0
+ Compiling proc-macro2 v1.0.19
+ Compiling unicode-xid v0.2.1
+ Compiling cortex-m v0.6.3
+ (...)
+ Compiling as-slice v0.1.3
+ Compiling aligned v0.3.4
+ Compiling cortex-m-rt-macros v0.1.8
+ Compiling nrf-hal-common v0.11.1
+ Finished dev [unoptimized + debuginfo] target(s) in 18.69s
```
-> **NOTE** Be sure to compile this crate *without* optimizations. The provided Cargo.toml file and build command above will ensure optimizations are off.
+> **NOTE** Be sure to compile this crate *without* optimizations. The provided Cargo.toml
+> file and build command above will ensure optimizations are off.
-OK, now we have produced an executable. This executable won't blink any leds, it's just a simplified version that we will build upon later in the chapter. As a sanity check, let's verify that the produced executable is actually an ARM binary:
+> **NOTE** If you have looked into `.cargo/config` you will have noticed that the target
+ is actually always set to "thumbv6m-none-eabi" so the --target flag to `cargo` can in
+ fact be omitted here.
+
+OK, now we have produced an executable. This executable won't blink any leds,
+it's just a simplified version that we will build upon later in the chapter.
+As a sanity check, let's verify that the produced executable is actually an ARM binary:
``` console
-$ # equivalent to `readelf -h target/thumbv7em-none-eabihf/debug/led-roulette`
-$ cargo readobj --target thumbv7em-none-eabihf --bin led-roulette -- -file-headers
+$ # equivalent to `readelf -h target/thumbv6m-none-eabi/debug/led-roulette`
+ cargo readobj --target thumbv6m-none-eabi --bin led-roulette -- -file-headers
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
- ABI Version: 0x0
+ ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
- Entry point address: 0x8000197
+ Entry point address: 0xC1
Start of program headers: 52 (bytes into file)
- Start of section headers: 740788 (bytes into file)
- Flags: 0x5000400
+ Start of section headers: 599484 (bytes into file)
+ Flags: 0x5000200
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
- Number of section headers: 20
- Section header string table index: 18
+ Number of section headers: 22
+ Section header string table index: 20
```
Next, we'll flash the program into our microcontroller.
diff --git a/src/05-led-roulette/build.rs b/src/05-led-roulette/build.rs
new file mode 100644
index 000000000..c8d8c9141
--- /dev/null
+++ b/src/05-led-roulette/build.rs
@@ -0,0 +1,30 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory (wherever `Cargo.toml` is). However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! a rebuild of the application with new memory settings is ensured after updating `memory.x`.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+}
diff --git a/src/05-led-roulette/debug-it.md b/src/05-led-roulette/debug-it.md
index 4131dfca9..fedee282b 100644
--- a/src/05-led-roulette/debug-it.md
+++ b/src/05-led-roulette/debug-it.md
@@ -1,33 +1,64 @@
# Debug it
+## How does this even work?
+Before we debug our little program let's take a moment to quickly understand what is actually
+happening here. In the previous chapter we already discussed the purpose of the second chip
+on the board as well as how it talks to our computer, but how can we actually use it?
-We are already inside a debugging session so let's debug our program.
+As you can see from the output of `cargo-embed` it opened a "GDB stub", this is a server that our GDB
+can connect to and send commands like "set a breakpoint at address X" to, the server can then decide
+on its own how to handle this command. In the case of the `cargo-embed` GDB stub it will forward the
+command to the debugging probe on the board via USB which then does the job of actually talking to the
+MCU for us.
-After the `load` command, our program is stopped at its *entry point*. This is indicated by the
-"Start address 0x8000XXX" part of GDB's output. The entry point is the part of a program that a
-processor / CPU will execute first.
+## Let's debug!
-The starter project I've provided to you has some extra code that runs *before* the `main` function.
-At this time, we are not interested in that "pre-main" part so let's skip right to the beginning of
-the `main` function. We'll do that using a breakpoint:
+Since `cargo-embed` is blocking our current shell we can simply open a new one and cd back into our project
+directory. Once we are there we can connect to the GDB server like this:
+```shell
+$ gdb target/thumbv6m-none-eabi/debug/led-roulette
+(gdb) target remote :1337
+Remote debugging using :1337
+::fmt (
+ self=,
+ f=)
+ at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.12/src/lib.rs:489
+489 pub unsafe extern "C" fn Reset() -> ! {
+(gdb)
```
-(gdb) break main
-Breakpoint 1 at 0x800018c: file src/05-led-roulette/src/main.rs, line 10.
+> **NOTE** Depending on which GDB you installed you will have to use a different command to launch it,
+> check out chapter 3 if you forgot which one it was.
+
+> **NOTE**: If `cargo-embed` prints a lot of warnings here don't worry about it. As of now it does not fully
+> implement the GDB protocol and thus might not recognize all of the commands your GDB is sending to it,
+> as long as it does not crash, you are fine.
+
+Right now we are inside the `Reset()` function. This is (surprisingly) the function that is run after a reset
+of the chip. Since we did tell cargo-embed to halt the chip after we flashed it, this is where we start.
+
+This `Reset()` function is part of a small piece of setup code that initializes some things for our Rust program
+before moving on to the `main()` function. Let's set a breakpoint there and jump to it:
+
+```
+(gdb) break main
+Breakpoint 1 at 0xac: file src/05-led-roulette/src/main.rs, line 9.
(gdb) continue
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.
-Breakpoint 1, main () at src/05-led-roulette/src/main.rs:10
-10 let x = 42;
+Breakpoint 1, main () at src/05-led-roulette/src/main.rs:9
+9 #[entry]
+(gdb)
```
Breakpoints can be used to stop the normal flow of a program. The `continue` command will let the
program run freely *until* it reaches a breakpoint. In this case, until it reaches the `main`
function because there's a breakpoint there.
-Note that GDB output says "Breakpoint 1". Remember that our processor can only use six of these
-breakpoints so it's a good idea to pay attention to these messages.
+Note that GDB output says "Breakpoint 1". Remember that our processor can only use a limited amount of these
+breakpoints so it's a good idea to pay attention to these messages. If you happen to run out of breakpoints,
+you can list all the current ones with `info break` and delete desired ones with `delete `.
For a nicer debugging experience, we'll be using GDB's Text User Interface (TUI). To enter into that
mode, on the GDB shell enter the following command:
@@ -41,43 +72,40 @@ mode, on the GDB shell enter the following command:

-At any point you can leave the TUI mode using the following command:
+GDB's break command does not only work for function names, it can also break at certain line numbers.
+If we wanted to break in line 13 we can simply do:
```
-(gdb) tui disable
-```
+(gdb) break 13
+Breakpoint 2 at 0xb8: file src/05-led-roulette/src/main.rs, line 13.
+(gdb) continue
+Continuing.
-OK. We are now at the beginning of `main`. We can advance the program statement by statement using
-the `step` command. So let's use that twice to reach the `_y = x` statement. Once you've typed `step`
-once you can just hit enter to run it
-again.
+Breakpoint 2, led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:13
+(gdb)
+```
+At any point you can leave the TUI mode using the following command:
```
-(gdb) step
-14 _y = x;
+(gdb) tui disable
```
-If you are not using the TUI mode, on each `step` call GDB will print back the current statement
-along with its line number.
-
We are now "on" the `_y = x` statement; that statement hasn't been executed yet. This means that `x`
is initialized but `_y` is not. Let's inspect those stack/local variables using the `print` command:
```
(gdb) print x
$1 = 42
-
(gdb) print &x
-$2 = (i32 *) 0x10001ff4
-
+$2 = (*mut i32) 0x20003fe8
(gdb) print _y
-$3 = -536810104
-
+$3 = 536870912
(gdb) print &_y
-$4 = (i32 *) 0x10001ff0
+$4 = (*mut i32) 0x20003fec
+(gdb)
```
-As expected, `x` contains the value `42`. `_y`, however, contains the value `-536810104` (?). Because
+As expected, `x` contains the value `42`. `_y`, however, contains the value `536870912` (?). Because
`_y` has not been initialized yet, it contains some garbage value.
The command `print &x` prints the address of the variable `x`. The interesting bit here is that GDB
@@ -90,14 +118,16 @@ Instead of printing the local variables one by one, you can also use the `info l
```
(gdb) info locals
x = 42
-_y = -536810104
+_y = 536870912
+(gdb)
```
-OK. With another `step`, we'll be on top of the `loop {}` statement:
+If we want to continue the program execution line by line we can do that using the `next` command
+so let's proceed to the `loop {}` statement:
```
-(gdb) step
-17 loop {}
+(gdb) next
+16 loop {}
```
And `_y` should now be initialized.
@@ -107,12 +137,12 @@ And `_y` should now be initialized.
$5 = 42
```
-If we use `step` again on top of the `loop {}` statement, we'll get stuck because the program will
+If we use `next` again on top of the `loop {}` statement, we'll get stuck because the program will
never pass that statement. Instead, we'll switch to the disassemble view with the `layout asm`
command and advance one instruction at a time using `stepi`. You can always switch back into Rust
source code view later by issuing the `layout src` command again.
-> **NOTE** If you used the `step` command by mistake and GDB got stuck, you can get unstuck by hitting `Ctrl+C`.
+> **NOTE**: If you used the `next` or `continue` command by mistake and GDB got stuck, you can get unstuck by hitting `Ctrl+C`.
```
(gdb) layout asm
@@ -125,62 +155,54 @@ program around the line you are currently at.
```
(gdb) disassemble /m
-Dump of assembler code for function main:
-7 #[entry]
- 0x08000188 <+0>: sub sp, #8
- 0x0800018a <+2>: movs r0, #42 ; 0x2a
-
-8 fn main() -> ! {
-9 let _y;
-10 let x = 42;
- 0x0800018c <+4>: str r0, [sp, #4]
-
-11 _y = x;
- 0x0800018e <+6>: ldr r0, [sp, #4]
- 0x08000190 <+8>: str r0, [sp, #0]
-
-12
-13 // infinite loop; just so we don't leave this stack frame
-14 loop {}
-=> 0x08000192 <+10>: b.n 0x8000194
- 0x08000194 <+12>: b.n 0x8000194
+Dump of assembler code for function led_roulette::__cortex_m_rt_main:
+10 fn main() -> ! {
+ 0x000000b2 <+0>: sub sp, #8
+ 0x000000b4 <+2>: movs r0, #42 ; 0x2a
+
+11 let _y;
+12 let x = 42;
+ 0x000000b6 <+4>: str r0, [sp, #0]
+
+13 _y = x;
+ 0x000000b8 <+6>: str r0, [sp, #4]
+
+14
+15 // infinite loop; just so we don't leave this stack frame
+16 loop {}
+=> 0x000000ba <+8>: b.n 0xbc
+ 0x000000bc <+10>: b.n 0xbc
End of assembler dump.
```
See the fat arrow `=>` on the left side? It shows the instruction the processor will execute next.
-If not inside the TUI mode on each `stepi` command GDB will print the statement, the line number
-*and* the address of the instruction the processor will execute next.
+If not inside the TUI mode on each `stepi` command GDB will print the statement and the line number
+of the instruction the processor will execute next.
```
(gdb) stepi
-0x08000194 14 loop {}
-
+16 loop {}
(gdb) stepi
-0x08000194 14 loop {}
+16 loop {}
```
One last trick before we move to something more interesting. Enter the following commands into GDB:
```
-(gdb) monitor reset halt
-Unable to match requested speed 1000 kHz, using 950 kHz
-Unable to match requested speed 1000 kHz, using 950 kHz
-adapter speed: 950 kHz
-target halted due to debug-request, current mode: Thread
-xPSR: 0x01000000 pc: 0x08000196 msp: 0x10002000
-
-(gdb) continue
+(gdb) monitor reset
+(gdb) c
Continuing.
-Breakpoint 1, main () at src/05-led-roulette/src/main.rs:10
-10 let x = 42;
+Breakpoint 1, main () at src/05-led-roulette/src/main.rs:9
+9 #[entry]
+(gdb)
```
We are now back at the beginning of `main`!
-`monitor reset halt` will reset the microcontroller and stop it right at the program entry point.
+`monitor reset` will reset the microcontroller and stop it right at the program entry point.
The following `continue` command will let the program run freely until it reaches the `main`
function that has a breakpoint on it.
@@ -202,8 +224,9 @@ A debugging session is active.
Inferior 1 [Remote target] will be detached.
Quit anyway? (y or n) y
-Detaching from program: $PWD/target/thumbv7em-none-eabihf/debug/led-roulette, Remote target
+Detaching from program: $PWD/target/thumbv6m-none-eabi/debug/led-roulette, Remote target
Ending remote debugging.
+[Inferior 1 (Remote target) detached]
```
> **NOTE** If the default GDB CLI is not to your liking check out [gdb-dashboard]. It uses Python to
@@ -212,8 +235,6 @@ Ending remote debugging.
[gdb-dashboard]: https://github.com/cyrus-and/gdb-dashboard#gdb-dashboard
-Don't close OpenOCD though! We'll use it again and again later on. It's better
-just to leave it running. If you want to learn more about what GDB can do, check out the section [How to use GDB](../appendix/2-how-to-use-gdb).
-
+If you want to learn more about what GDB can do, check out the section [How to use GDB](../appendix/2-how-to-use-gdb).
What's next? The high level API I promised.
diff --git a/src/05-led-roulette/flash-it.md b/src/05-led-roulette/flash-it.md
index 54c50b98e..2ea7da03b 100644
--- a/src/05-led-roulette/flash-it.md
+++ b/src/05-led-roulette/flash-it.md
@@ -7,150 +7,32 @@ In this case, our `led-roulette` program will be the *only* program in the micro
By this I mean that there's nothing else running on the microcontroller: no OS, no "daemon",
nothing. `led-roulette` has full control over the device.
-Onto the actual flashing. First thing we need is to do is launch OpenOCD. We did that in the
-previous section but this time we'll run the command inside a temporary directory (`/tmp` on \*nix;
-`%TEMP%` on Windows).
+Flashing the binary itself is quite simple thanks to `cargo-embed`, you only have to type `cargo embed`.
-Make sure the F3 is connected to your computer and run the following commands on a new terminal.
+Before executing that command though, lets look into what it actually does. If you look at the side of your micro:bit
+with the USB connector facing upwards you will notice, that there are actually 2 black squares on there, one is our MCU
+we already talked about but what purpose does the other one serve? The other chip has 3 main purposes:
-``` console
-$ # *nix
-$ cd /tmp
+1. Provide power from the USB connector to our MCU
+2. Provide a serial to USB bridge for our MCU (we will look into that in a later chapter)
+3. Being a programmer/debugger (this is the relevant purpose for now)
-$ # Windows
-$ cd %TEMP%
+Basically this chip acts as a bridge between our computer (to which it is connected via USB) and the MCU (to which it is
+connected via traces and communicates with using the SWD protocol). This bridge enables us to flash new binaries on to
+the MCU, inspect its state via a debugger and other things.
-$ # Windows: remember that you need an extra `-s %PATH_TO_OPENOCD%\share\scripts`
-$ openocd \
- -f interface/stlink-v2-1.cfg \
- -f target/stm32f3x.cfg
-```
-
-> **NOTE** Older revisions of the board need to pass slightly different arguments to
-> `openocd`. Review [this section] for the details.
-
-[this section]: ../03-setup/verify.md#first-openocd-connection
-
-The program will block; leave that terminal open.
-
-Now it's a good time to explain what this command is actually doing.
-
-I mentioned that the F3 actually has two microcontrollers. One of them is used as a
-programmer/debugger. The part of the board that's used as a programmer is called ST-LINK (that's what
-STMicroelectronics decided to call it). This ST-LINK is connected to the target microcontroller
-using a Serial Wire Debug (SWD) interface (this interface is an ARM standard so you'll run into it
-when dealing with other Cortex-M based microcontrollers). This SWD interface can be used to flash
-and debug a microcontroller. The ST-LINK is connected to the "USB ST-LINK" port and will appear as
-a USB device when you connect the F3 to your computer.
-
-
-
-
-
-
-As for OpenOCD, it's software that provides some services like a *GDB server* on top of USB
-devices that expose a debugging protocol like SWD or JTAG.
+So lets flash it!
-Onto the actual command: those `.cfg` files we are using instruct OpenOCD to look for a ST-LINK USB
-device (`interface/stlink-v2-1.cfg`) and to expect a STM32F3XX microcontroller
-(`target/stm32f3x.cfg`) to be connected to the ST-LINK.
-
-The OpenOCD output looks like this:
-
-``` console
-Open On-Chip Debugger 0.9.0 (2016-04-27-23:18)
-Licensed under GNU GPL v2
-For bug reports, read
- http://openocd.org/doc/doxygen/bugs.html
-Info : auto-selecting first available session transport "hla_swd". To override use 'transport select '.
-adapter speed: 1000 kHz
-adapter_nsrst_delay: 100
-Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
-none separate
-Info : Unable to match requested speed 1000 kHz, using 950 kHz
-Info : Unable to match requested speed 1000 kHz, using 950 kHz
-Info : clock speed 950 kHz
-Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
-Info : using stlink api v2
-Info : Target voltage: 2.919073
-Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
-```
-
-The "6 breakpoints, 4 watchpoints" part indicates the debugging features the processor has
-available.
-
-Leave that `openocd` process running, and open a new terminal. Make sure that you are inside the project's `src/05-led-roulette/` directory.
-
-I mentioned that OpenOCD provides a GDB server so let's connect to that right now:
-
-``` console
-$ -q target/thumbv7em-none-eabihf/debug/led-roulette
-Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...done.
-(gdb)
+```console
+$ cargo embed
+ (...)
+ Erasing sectors ✔ [00:00:00] [##################################################################################################################################################################] 2.00KB/ 2.00KB @ 4.57KB/s (eta 0s )
+ Programming pages ✔ [00:00:00] [##################################################################################################################################################################] 2.00KB/ 2.00KB @ 1.93KB/s (eta 0s )
+ Finished flashing in 0.764s
+Firing up GDB stub at localhost:1337.
+GDB stub listening on localhost:1337
```
-**NOTE**: `` represents a GDB program capable of debugging ARM binaries.
-This could be `arm-none-eabi-gdb`, `gdb-multiarch` or `gdb` depending on your
-system -- you may have to try all three.
-
-This only opens a GDB shell. To actually connect to the OpenOCD GDB server, use the following
-command within the GDB shell:
-
-```
-(gdb) target remote :3333
-Remote debugging using :3333
-0x00000000 in ?? ()
-```
-
-**NOTE**: If you are getting errors like `undefined debug reason 7 - target needs reset`, you can try running `monitor reset halt` as described [here](https://stackoverflow.com/questions/38994596/reason-7-target-needs-reset-unreliable-debugging-setup).
-
-**NOTE**: If the debugger is still not connecting to the OpenOCD server, then you may need to try using `arm-none-eabi-gdb` instead of the `gdb` command, as described above.
-
-By default OpenOCD's GDB server listens on TCP port 3333 (localhost). This command is connecting to
-that port.
-
-After entering this command, you'll see new output in the OpenOCD terminal:
-
-``` diff
- Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
-+Info : accepting 'gdb' connection on tcp/3333
-+Info : device id = 0x10036422
-+Info : flash size = 256kbytes
-```
-
-Almost there. To flash the device, we'll use the `load` command inside the GDB shell:
-
-```
-(gdb) load
-Loading section .vector_table, size 0x188 lma 0x8000000
-Loading section .text, size 0x38a lma 0x8000188
-Loading section .rodata, size 0x8 lma 0x8000514
-Start address 0x8000188, load size 1306
-Transfer rate: 6 KB/sec, 435 bytes/write.
-```
-
-And that's it. You'll also see new output in the OpenOCD terminal.
-
-``` diff
- Info : flash size = 256kbytes
-+Info : Unable to match requested speed 1000 kHz, using 950 kHz
-+Info : Unable to match requested speed 1000 kHz, using 950 kHz
-+adapter speed: 950 kHz
-+target state: halted
-+target halted due to debug-request, current mode: Thread
-+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
-+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
-+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
-+adapter speed: 4000 kHz
-+target state: halted
-+target halted due to breakpoint, current mode: Thread
-+xPSR: 0x61000000 pc: 0x2000003a msp: 0x2000a000
-+Info : Unable to match requested speed 1000 kHz, using 950 kHz
-+Info : Unable to match requested speed 1000 kHz, using 950 kHz
-+adapter speed: 950 kHz
-+target state: halted
-+target halted due to debug-request, current mode: Thread
-+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
-```
-Our program is loaded, let's debug it!
+You will notice that `cargo-embed` blocks after outputting the last line, this is inteded and you should not close it
+since we need it in this state for the next step, debugging it!
diff --git a/src/05-led-roulette/it-blinks.md b/src/05-led-roulette/it-blinks.md
new file mode 100644
index 000000000..17b572abd
--- /dev/null
+++ b/src/05-led-roulette/it-blinks.md
@@ -0,0 +1,102 @@
+# It blinks
+
+## Delaying
+Now we're going to take a brief look into delay abstractions provided by `embedded-hal`
+before combining this with the GPIO abstractions from the previous chapter in order to
+finally make an LED blink.
+
+`embedded-hal` provides us with two abstractions to delay the execution of our program:
+[DelayUs] and [DelayMs]. Both of them essentially work the exact same way except
+that they accept different units for their delay function.
+
+[DelayUs]: https://docs.rs/embedded-hal/0.2.4/embedded_hal/blocking/delay/trait.DelayUs.html
+[DelayMs]: https://docs.rs/embedded-hal/0.2.4/embedded_hal/blocking/delay/trait.DelayMs.html
+
+Inside of our MCU, several so-called "timers" exist. They can do various things regarding time for us,
+including simply pausing the execution of our program for a fixed amount of time. A very
+simple delay-based program that prints something every second might for example look like this:
+
+```rs
+#![deny(unsafe_code)]
+#![no_main]
+#![no_std]
+
+use cortex_m_rt::entry;
+use rtt_target::{rtt_init_print, rprintln};
+use panic_rtt_target as _;
+use nrf51_hal as hal;
+use hal::prelude::*;
+
+#[entry]
+fn main() -> ! {
+ rtt_init_print!();
+ let p = hal::pac::Peripherals::take().unwrap();
+
+ let mut delay = hal::Timer::new(p.TIMER0);
+ loop {
+ delay.delay_ms(1000u32);
+ rprintln!("1000 ms passed");
+ }
+}
+```
+
+In order to actually see the prints we have to change `Embed.toml` like this:
+```
+[default.general]
+chip = "nrf51822_xxAA"
+
+[default.reset]
+halt_afterwards = false
+
+[default.rtt]
+enabled = true
+
+[default.gdb]
+enabled = false
+```
+
+And now after putting the code into `src/main.rs` and another quick `cargo embed` you should see
+"`1000 ms passed`" being sent to your console every second from your MCU.
+
+## Blinking
+
+Now we've arrived at the point where we can combine our new knowledge about GPIO and delay abstractions
+in order to actually make an LED on the back of the micro:bit blink. The resulting program is really just
+a mash-up of the one above and the one that turned an LED on in the last chapter and looks like this:
+
+```rs
+#![deny(unsafe_code)]
+#![no_main]
+#![no_std]
+
+use cortex_m_rt::entry;
+use rtt_target::{rtt_init_print, rprintln};
+use panic_rtt_target as _;
+use nrf51_hal as hal;
+use hal::prelude::*;
+
+#[entry]
+fn main() -> ! {
+ rtt_init_print!();
+ let p = hal::pac::Peripherals::take().unwrap();
+
+ let mut delay = hal::Timer::new(p.TIMER0);
+
+ let p0 = hal::gpio::p0::Parts::new(p.GPIO);
+ let mut row1 = p0.p0_13.into_push_pull_output(hal::gpio::Level::Low);
+ let _col1 = p0.p0_04.into_push_pull_output(hal::gpio::Level::Low);
+
+ loop {
+ row1.set_high().unwrap();
+ rprintln!("Light!");
+ delay.delay_ms(500u32);
+
+ row1.set_low().unwrap();
+ rprintln!("Dark!");
+ delay.delay_ms(500u32);
+ }
+}
+```
+
+And after putting the code into `src/main.rs` and a final `cargo embed` you should see the LED we light up before
+blinking as well as a print, every time the LED changes from off to on and vice versa.
diff --git a/src/05-led-roulette/light-it-up.md b/src/05-led-roulette/light-it-up.md
new file mode 100644
index 000000000..c17f4181f
--- /dev/null
+++ b/src/05-led-roulette/light-it-up.md
@@ -0,0 +1,112 @@
+# Light it up
+## embedded-hal
+
+In this chapter we are going to make one of the many LEDs on the back of the micro:bit light up since this is
+basically the "Hello World" of embedded programming. In order to get this task done we will use a set of
+abstractions provided by the crate `embedded-hal`. `embedded-hal` is a crate which provides a set of traits
+that describe behaviour of hardware, for example the [OutputPin trait] which allows us to turn a pin on or off.
+
+In order to use these traits we have to implement them for the chip we are using. Luckily this has already been done
+in our case in the [nrf51-hal]. Crates like this are commonly referred to as HALs (Hardware Abstraction Layer)
+and allow us to use the same API to blink an LED and of course many more complex things accross all chips that implement
+the `embedded-hal` traits.
+
+For example, a person working on an embedded project might need to read temperature data from a sensor. In
+order to achieve this they can write a driver library that doesn't do anything MCU specific but instead just relies on
+`embedded-hal`. This will allow anyone with an MCU that implements the `embedded-hal` traits to easily plug and play
+their driver crate, despite having an MCU made by a completely different manufacturer or even with a different architecture, etc.
+
+[OutputPin trait]: https://docs.rs/embedded-hal/0.2.4/embedded_hal/digital/v2/trait.OutputPin.html
+[nrf51-hal]: https://crates.io/crates/nrf51-hal
+
+## The micro:bit LEDs
+
+On the back of the micro:bit you can see a 5x5 square of LEDs, usually called an LED matrix. This matrix alignment is
+used so that instead of having to use 25 seperate pins to drive every single one of the LEDs, we can just use 10 (5+5) pins in
+order to control which column and which row of our matrix lights up. However, the micro:bit team implemented this a
+little differently. Their [schematic page] says that it is actually implemented as a 3x9 matrix but a few columns simply
+remain unused.
+
+In order to determine which pins we need to control to light up an LED we can check out
+micro:bit's open source [schematic], linked on the same page. The very first sheet contains the LED matrix circuit which
+is apparently connected to the pins named ROW1-3 and COL1-9. Further down on sheet 5 you can see that these pins
+directly map to our MCU. For example, ROW1 is connected to P0.13.
+
+> **NOTE**: The naming scheme of the NRF51 for its pins (P0.13) simply refers to port 0 (P0) pin 13. This is done
+> because on MCUs with dozens or hundreds of pins you usually end up with multiple pins grouped up as ports for the sake of
+> clarity. The NRF51, however, is so small that it only has one GPIO port (P0).
+
+[schematic page]: https://tech.microbit.org/hardware/schematic/
+[schematic]: https://github.com/bbcmicrobit/hardware/blob/master/V1.5/SCH_BBC-Microbit_V1.5.PDF
+
+## Actually lighting it up!
+
+The code required to light up an LED in the matrix is actually quite simple but it requires a bit of setup. First take
+a look at it and then we can go through it step by step:
+
+```rust
+#![deny(unsafe_code)]
+#![no_main]
+#![no_std]
+
+use cortex_m_rt::entry;
+use panic_halt as _;
+use nrf51_hal as hal;
+use hal::prelude::*;
+
+#[entry]
+fn main() -> ! {
+ let p = hal::pac::Peripherals::take().unwrap();
+
+ let p0 = hal::gpio::p0::Parts::new(p.GPIO);
+ let mut row1 = p0.p0_13.into_push_pull_output(hal::gpio::Level::Low);
+ let _col1 = p0.p0_04.into_push_pull_output(hal::gpio::Level::Low);
+
+ row1.set_high().unwrap();
+
+ loop {}
+}
+```
+
+The first few lines until the main function just do some basic imports and setup we already looked at before.
+However, the main function looks pretty different to what we have seen up to now.
+
+The first line is related to how most HALs written in Rust work internally. Usually these crates rely on so-called
+PACs (Peripheral Access Crates). A PAC is usually an autogenerated crate that provides some minimal abstractions
+for all the peripherals our MCU has to offer. `let p = hal::pac::Peripherals::take().unwrap();` basically takes all
+these peripherals from the PAC and binds them to a variable.
+
+> **NOTE**: If you are wondering why we have to call `unwrap()` here, in theory it is possible for `take()` to be called
+> more than once. This would lead to the peripherals being represented by two separate variables and thus lots of
+> possible confusing behaviour because two variables modify the same resource. In order to avoid this, PACs are
+> implemented in a way that it would panic if you tried to take the peripherals twice.
+
+Once we got the peripherals, we assemble the GPIO port 0 from them with `let p0 = hal::gpio::p0::Parts::new(p.GPIO);` and
+proceed to construct the `ROW1` and `COL1` pin using the two lines below, initialized as a switched-off
+(`hal::gpio::Level::Low`) push-pull output pin (`into_push_pull_output`).
+
+> **NOTE** If you don't know what push-pull means, don't worry about it, it's mostly irrelevant for us here, if you do
+> want to figure it out, have a look [here](https://en.wikipedia.org/wiki/Push%E2%80%93pull_output).
+
+Now we can finally light the LED connected to `ROW1`, `COL1` up by setting the `ROW1` pin to high (i.e. switching it on).
+The reason we can leave `COL1` set to low is because of how the LED matrix circuit works. Furthermore, `embedded-hal` is
+designed in a way that every operation on hardware can possibly return an error, even just toggling a pin on or off. Since
+that is highly unlikely in our case, we can just `unwrap()` the result.
+
+
+## Testing it
+
+Testing our little program is quite simple. First put it into `src/mains.rs`. Afterwards we simply have to run `cargo-embed`
+again, let it flash and just like before, open our GDB and connect to the GDB stub:
+
+```
+$ gdb target/thumbv6m-none-eabi/debug/led-roulette
+(gdb) target remote :1337
+Remote debugging using :1337
+cortex_m_rt::Reset () at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.12/src/lib.rs:489
+489 pub unsafe extern "C" fn Reset() -> ! {
+(gdb)
+```
+
+If we now let the program run via the GDB `continue` command, one of the LEDs on the back of the micro:bit should light
+up.
diff --git a/src/05-led-roulette/memory.x b/src/05-led-roulette/memory.x
new file mode 100644
index 000000000..9e2ab65f6
--- /dev/null
+++ b/src/05-led-roulette/memory.x
@@ -0,0 +1,6 @@
+MEMORY
+{
+ /* NOTE K = KiBi = 1024 bytes */
+ FLASH : ORIGIN = 0x00000000, LENGTH = 256K
+ RAM : ORIGIN = 0x20000000, LENGTH = 16K
+}
diff --git a/src/05-led-roulette/my-solution.md b/src/05-led-roulette/my-solution.md
index 0bf05d0e9..a9c2614ed 100644
--- a/src/05-led-roulette/my-solution.md
+++ b/src/05-led-roulette/my-solution.md
@@ -9,99 +9,134 @@ Here's mine:
#![no_main]
#![no_std]
-use aux5::{entry, prelude::*, Delay, Leds};
+use cortex_m_rt::entry;
+use rtt_target::rtt_init_print;
+use panic_rtt_target as _;
+use nrf51_hal as hal;
+use hal::prelude::*;
+
+// All border LEDs in order with the exception of the very first LED which is set
+// at the last spot
+const COMBINATIONS: [(usize, usize); 16] = [
+ (2, 4), (1, 2), (2, 5), (1, 3), (3, 8), (2, 1), (1, 4), (3, 2), (2,6),
+ (3, 1), (2, 7), (3, 3), (1, 8), (2, 2), (3, 4), (1, 1)
+];
#[entry]
fn main() -> ! {
- let (mut delay, mut leds): (Delay, Leds) = aux5::init();
+ rtt_init_print!();
+ let p = hal::pac::Peripherals::take().unwrap();
+
+ let mut delay = hal::Timer::new(p.TIMER0);
+
+ let p0 = hal::gpio::p0::Parts::new(p.GPIO);
+
+ // Initialize all rows and cols to off
+ let mut row1 = p0.p0_13.into_push_pull_output(hal::gpio::Level::Low).degrade();
+ let row2 = p0.p0_14.into_push_pull_output(hal::gpio::Level::Low).degrade();
+ let row3 = p0.p0_15.into_push_pull_output(hal::gpio::Level::Low).degrade();
+ let mut col1 = p0.p0_04.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col2 = p0.p0_05.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col3 = p0.p0_06.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col4 = p0.p0_07.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col5 = p0.p0_08.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col6 = p0.p0_09.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col7 = p0.p0_10.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col8 = p0.p0_11.into_push_pull_output(hal::gpio::Level::High).degrade();
+ let col9 = p0.p0_12.into_push_pull_output(hal::gpio::Level::High).degrade();
+
+ // bring up the very first LED
+ row1.set_high().unwrap();
+ col1.set_low().unwrap();
+
+ let mut cols = [col1, col2, col3, col4, col5, col6, col7, col8, col9];
+ let mut rows = [row1, row2, row3];
- let ms = 50_u8;
loop {
- for curr in 0..8 {
- let next = (curr + 1) % 8;
+ let mut previous_pair = (1, 1);
+ for current_pair in COMBINATIONS.iter() {
+ delay.delay_ms(30u32);
- leds[next].on();
- delay.delay_ms(ms);
- leds[curr].off();
- delay.delay_ms(ms);
+ rows[current_pair.0 - 1].set_high().unwrap();
+ cols[current_pair.1 - 1].set_low().unwrap();
+
+ rows[previous_pair.0 - 1].set_low().unwrap();
+ cols[previous_pair.1 - 1].set_high().unwrap();
+
+ previous_pair = *current_pair;
}
}
}
-
```
One more thing! Check that your solution also works when compiled in "release" mode:
``` console
-$ cargo build --target thumbv7em-none-eabihf --release
+$ cargo embed --release
```
-You can test it with this `gdb` command:
+If you want to debug your "release" mode binary you'll have to use a different GDB command:
``` console
-$ # or, you could simply call `cargo run --target thumbv7em-none-eabihf --release`
-$ arm-none-eabi-gdb target/thumbv7em-none-eabihf/release/led-roulette
-$ # ~~~~~~~
+$ gdb target/thumbv6m-none-eabi/release/led-roulette
```
Binary size is something we should always keep an eye on! How big is your solution? You can check
that using the `size` command on the release binary:
``` console
-$ # equivalent to size target/thumbv7em-none-eabihf/debug/led-roulette
-$ cargo size --target thumbv7em-none-eabihf --bin led-roulette -- -A
+$ cargo size --bin led-roulette -- -A
+ Finished dev [unoptimized + debuginfo] target(s) in 0.03s
led-roulette :
section size addr
-.vector_table 392 0x8000000
-.text 16404 0x8000188
-.rodata 2924 0x80041a0
+.vector_table 168 0x0
+.text 20996 0xa8
+.rodata 2956 0x52ac
.data 0 0x20000000
-.bss 4 0x20000000
-.debug_str 602185 0x0
-.debug_abbrev 24134 0x0
-.debug_info 553143 0x0
-.debug_ranges 112744 0x0
-.debug_macinfo 86 0x0
-.debug_pubnames 56467 0x0
-.debug_pubtypes 94866 0x0
-.ARM.attributes 58 0x0
-.debug_frame 174812 0x0
-.debug_line 354866 0x0
-.debug_loc 534 0x0
-.comment 75 0x0
-Total 1993694
-
-$ cargo size --target thumbv7em-none-eabihf --bin led-roulette --release -- -A
+.bss 1088 0x20000000
+.uninit 0 0x20000440
+.debug_abbrev 21988 0x0
+.debug_info 283389 0x0
+.debug_aranges 15832 0x0
+.debug_str 307609 0x0
+.debug_pubnames 68859 0x0
+.debug_pubtypes 55406 0x0
+.ARM.attributes 50 0x0
+.debug_frame 47732 0x0
+.debug_line 199401 0x0
+.debug_ranges 68936 0x0
+.debug_loc 976 0x0
+.comment 147 0x0
+Total 1095533
+
+
+$ cargo size --bin led-roulette --release -- -A
+ Finished release [optimized + debuginfo] target(s) in 0.02s
led-roulette :
section size addr
-.vector_table 392 0x8000000
-.text 1826 0x8000188
-.rodata 84 0x80008ac
+.vector_table 168 0x0
+.text 4044 0xa8
+.rodata 692 0x1074
.data 0 0x20000000
-.bss 4 0x20000000
-.debug_str 23334 0x0
-.debug_loc 6964 0x0
-.debug_abbrev 1337 0x0
-.debug_info 40582 0x0
-.debug_ranges 2936 0x0
-.debug_macinfo 1 0x0
-.debug_pubnames 5470 0x0
-.debug_pubtypes 10016 0x0
-.ARM.attributes 58 0x0
-.debug_frame 164 0x0
-.debug_line 9081 0x0
-.comment 18 0x0
-Total 102267
+.bss 1076 0x20000000
+.uninit 0 0x20000434
+.debug_loc 7520 0x0
+.debug_abbrev 3444 0x0
+.debug_info 55229 0x0
+.debug_aranges 1144 0x0
+.debug_ranges 3608 0x0
+.debug_str 48267 0x0
+.debug_pubnames 15435 0x0
+.debug_pubtypes 15970 0x0
+.ARM.attributes 50 0x0
+.debug_frame 2152 0x0
+.debug_line 17050 0x0
+.comment 147 0x0
+Total 175996
```
> **NOTE** The Cargo project is already configured to build the release binary using LTO.
-Know how to read this output? The `text` section contains the program instructions. It's around 2KB
+Know how to read this output? The `text` section contains the program instructions. It's around 4KB
in my case. On the other hand, the `data` and `bss` sections contain variables statically allocated
-in RAM (`static` variables). A `static` variable is being used in `aux5::init`; that's why it shows 4
-bytes of `bss`.
-
-One final thing! We have been running our programs from within GDB but our programs don't depend on
-GDB at all. You can confirm this be closing both GDB and OpenOCD and then resetting the board by
-pressing the black button on the board. The LED roulette application will run without intervention
-of GDB.
+in RAM (`static` variables).
diff --git a/src/05-led-roulette/src/main.rs b/src/05-led-roulette/src/main.rs
index 26e449962..9380a52ba 100644
--- a/src/05-led-roulette/src/main.rs
+++ b/src/05-led-roulette/src/main.rs
@@ -2,7 +2,9 @@
#![no_main]
#![no_std]
-use aux5::entry;
+use cortex_m_rt::entry;
+use panic_halt as _;
+use nrf51_hal as _;
#[entry]
fn main() -> ! {
diff --git a/src/05-led-roulette/the-challenge.md b/src/05-led-roulette/the-challenge.md
index b023849fc..2067ff76c 100644
--- a/src/05-led-roulette/the-challenge.md
+++ b/src/05-led-roulette/the-challenge.md
@@ -3,73 +3,24 @@
You are now well armed to face a challenge! Your task will be to implement the application I showed
you at the beginning of this chapter.
-Here's the GIF again:
-
-
+
-Also, this may help:
+If you can't exactly see what's happening here it is in a much slower version:
-
+
-This is a timing diagram. It indicates which LED is on at any given instant of time and for how long
-each LED should be on. On the X axis we have the time in milliseconds. The timing diagram shows a
-single period. This pattern will repeat itself every 800 ms. The Y axis labels each LED with a
-cardinal point: North, East, etc. As part of the challenge you'll have to figure out how each
-element in the `Leds` array maps to these cardinal points (hint: `cargo doc --open` `;-)`).
-
-Before you attempt this challenge, let me give you one last tip. Our GDB sessions always involve
-entering the same commands at the beginning. We can use a `.gdb` file to execute some commands
-right after GDB is started. This way you can save yourself the effort of having to enter them
-manually on each GDB session.
-
-Place this `openocd.gdb` file in the root of the Cargo project, right next to the `Cargo.toml`:
-
-``` console
-$ cat openocd.gdb
-```
-
-``` text
-target remote :3333
-load
-break main
-continue
-```
-
-Then modify the second line of the `.cargo/config` file:
-
-``` console
-$ cat .cargo/config
-```
-
-``` toml
-[target.thumbv7em-none-eabihf]
-runner = "arm-none-eabi-gdb -q -x openocd.gdb" # <-
-rustflags = [
- "-C", "link-arg=-Tlink.x",
-]
-```
-
-With that in place, you should now be able to start a `gdb` session that will automatically flash
-the program and jump to the beginning of `main`:
+2 hints before you start:
-``` console
-$ cargo run --target thumbv7em-none-eabihf
- Running `arm-none-eabi-gdb -q -x openocd.gdb target/thumbv7em-none-eabihf/debug/led-roulette`
-Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...done.
-(..)
-Loading section .vector_table, size 0x188 lma 0x8000000
-Loading section .text, size 0x3b20 lma 0x8000188
-Loading section .rodata, size 0xb0c lma 0x8003cc0
-Start address 0x8003b1c, load size 18356
-Transfer rate: 20 KB/sec, 6118 bytes/write.
-Breakpoint 1 at 0x800018c: file src/05-led-roulette/src/main.rs, line 9.
-Note: automatically using hardware breakpoints for read-only addresses.
+1. As we learned before the LED matrix of the micro:bit is actually a 3x9 while being exposed as a 5x5. Furthermore,
+ it seems like the 9 columns and 3 rows are more or less randomly mapped to the visual 5x5 matrix. If you don't want
+ to go through the effort of figuring out the pins you have to set high/low in order to blink the border of the
+ matrix, here is the list: `(R1, C1) (R2, C4) (R1, C2), (R2, C5) (R1, C3) (R3, C8) (R2, C1) (R1, C4) (R3, C2) (R2,
+ C6) (R3, C1) (R2, C7) (R3, C3) (R1, C8) (R2, C2) (R3, C4)`
-Breakpoint 1, main () at src/05-led-roulette/src/main.rs:9
-9 let (mut delay, mut leds): (Delay, Leds) = aux5::init();
-(gdb)
-```
+2. If you are thinking about storing columns and rows in arrays you will quickly notice they are of different types since
+ all GPIO pins are represented as their own type. However, you can call `.degrade()` on the individual GPIO objects in
+ order to "degrade" them all into the same type and then store them in an array.
diff --git a/src/05-led-roulette/the-led-and-delay-abstractions.md b/src/05-led-roulette/the-led-and-delay-abstractions.md
deleted file mode 100644
index a91a0eb10..000000000
--- a/src/05-led-roulette/the-led-and-delay-abstractions.md
+++ /dev/null
@@ -1,222 +0,0 @@
-# The `Led` and `Delay` abstractions
-
-Now, I'm going to introduce two high level abstractions that we'll use to implement the LED roulette
-application.
-
-The auxiliary crate, `aux5`, exposes an initialization function called `init`. When called this
-function returns two values packed in a tuple: a `Delay` value and a `Leds` value.
-
-`Delay` can be used to block your program for a specified amount of milliseconds.
-
-`Leds` is actually an array of eight `Led`s. Each `Led` represents one of the LEDs on the F3 board,
-and exposes two methods: `on` and `off` which can be used to turn the LED on or off, respectively.
-
-Let's try out these two abstractions by modifying the starter code to look like this:
-
-``` rust
-#![deny(unsafe_code)]
-#![no_main]
-#![no_std]
-
-use aux5::{entry, prelude::*, Delay, Leds};
-
-#[entry]
-fn main() -> ! {
- let (mut delay, mut leds): (Delay, Leds) = aux5::init();
-
- let half_period = 500_u16;
-
- loop {
- leds[0].on();
- delay.delay_ms(half_period);
-
- leds[0].off();
- delay.delay_ms(half_period);
- }
-}
-```
-
-Now build it:
-
-``` console
-$ cargo build --target thumbv7em-none-eabihf
-```
-
-> **NOTE** It's possible to forget to rebuild the program *before* starting a GDB session; this
-> omission can lead to very confusing debug sessions. To avoid this problem you can call `cargo run`
-> instead of `cargo build`; `cargo run` will build *and* start a debug session ensuring you never
-> forget to recompile your program.
-
-Now, we'll repeat the flashing procedure that we did in the previous section:
-
-``` console
-$ # this starts a GDB session of the program; no need to specify the path to the binary
-$ arm-none-eabi-gdb -q target/thumbv7em-none-eabihf/debug/led-roulette
-Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...done.
-(gdb) target remote :3333
-Remote debugging using :3333
-(..)
-
-(gdb) load
-Loading section .vector_table, size 0x188 lma 0x8000000
-Loading section .text, size 0x3fc6 lma 0x8000188
-Loading section .rodata, size 0xa0c lma 0x8004150
-Start address 0x8000188, load size 19290
-Transfer rate: 19 KB/sec, 4822 bytes/write.
-
-(gdb) break main
-Breakpoint 1 at 0x800018c: file src/05-led-roulette/src/main.rs, line 9.
-
-(gdb) continue
-Continuing.
-Note: automatically using hardware breakpoints for read-only addresses.
-
-Breakpoint 1, main () at src/05-led-roulette/src/main.rs:9
-9 let (mut delay, mut leds): (Delay, Leds) = aux5::init();
-```
-
-OK. Let's step through the code. This time, we'll use the `next` command instead of `step`. The
-difference is that the `next` command will step *over* function calls instead of going inside them.
-
-```
-(gdb) next
-11 let half_period = 500_u16;
-
-(gdb) next
-13 loop {
-
-(gdb) next
-14 leds[0].on();
-
-(gdb) next
-15 delay.delay_ms(half_period);
-```
-
-After executing the `leds[0].on()` statement, you should see a red LED, the one pointing North,
-turn on.
-
-Let's continue stepping over the program:
-
-```
-(gdb) next
-17 leds[0].off();
-
-(gdb) next
-18 delay.delay_ms(half_period);
-```
-
-The `delay_ms` call will block the program for half a second but you may not notice because the
-`next` command also takes some time to execute. However, after stepping over the `leds[0].off()`
-statement you should see the red LED turn off.
-
-You can already guess what this program does. Let it run uninterrupted using the `continue` command.
-
-```
-(gdb) continue
-Continuing.
-```
-
-Now, let's do something more interesting. We are going to modify the behavior of our program using
-GDB.
-
-First, let's stop the infinite loop by hitting `Ctrl+C`. You'll probably end up somewhere inside
-`Led::on`, `Led::off` or `delay_ms`:
-
-```
-Program received signal SIGINT, Interrupt.
-0x080033f6 in core::ptr::read_volatile (src=0xe000e010) at /checkout/src/libcore/ptr.rs:472
-472 /checkout/src/libcore/ptr.rs: No such file or directory.
-```
-
-In my case, the program stopped its execution inside a `read_volatile` function. GDB output shows
-some interesting information about that: `core::ptr::read_volatile (src=0xe000e010)`. This means
-that the function comes from the `core` crate and that it was called with argument `src =
-0xe000e010`.
-
-Just so you know, a more explicit way to show the arguments of a function is to use the `info args`
-command:
-
-```
-(gdb) info args
-src = 0xe000e010
-```
-
-Regardless of where your program may have stopped you can always look at the output of the
-`backtrace` command (`bt` for short) to learn how it got there:
-
-```
-(gdb) backtrace
-#0 0x080033f6 in core::ptr::read_volatile (src=0xe000e010)
- at /checkout/src/libcore/ptr.rs:472
-#1 0x08003248 in >::get (self=0xe000e010)
- at $REGISTRY/vcell-0.1.0/src/lib.rs:43
-#2 >::read (self=0xe000e010)
- at $REGISTRY/volatile-register-0.2.0/src/lib.rs:75
-#3 cortex_m::peripheral::syst::::has_wrapped (self=0x10001fbc)
- at $REGISTRY/cortex-m-0.5.7/src/peripheral/syst.rs:124
-#4 0x08002d9c in >::delay_us (self=0x10001fbc, us=500000)
- at $REGISTRY/stm32f30x-hal-0.2.0/src/delay.rs:58
-#5 0x08002cce in >::delay_ms (self=0x10001fbc, ms=500)
- at $REGISTRY/stm32f30x-hal-0.2.0/src/delay.rs:32
-#6 0x08002d0e in >::delay_ms (self=0x10001fbc, ms=500)
- at $REGISTRY/stm32f30x-hal-0.2.0/src/delay.rs:38
-#7 0x080001ee in main () at src/05-led-roulette/src/main.rs:18
-```
-
-`backtrace` will print a trace of function calls from the current function down to main.
-
-Back to our topic. To do what we are after, first, we have to return to the `main` function. We can
-do that using the `finish` command. This command resumes the program execution and stops it again
-right after the program returns from the current function. We'll have to call it several times.
-
-```
-(gdb) finish
-cortex_m::peripheral::syst::::has_wrapped (self=0x10001fbc)
- at $REGISTRY/cortex-m-0.5.7/src/peripheral/syst.rs:124
-124 self.csr.read() & SYST_CSR_COUNTFLAG != 0
-Value returned is $1 = 5
-
-(gdb) finish
-Run till exit from #0 cortex_m::peripheral::syst::::has_wrapped (
- self=0x10001fbc)
- at $REGISTRY/cortex-m-0.5.7/src/peripheral/syst.rs:124
-0x08002d9c in >::delay_us (
- self=0x10001fbc, us=500000)
- at $REGISTRY/stm32f30x-hal-0.2.0/src/delay.rs:58
-58 while !self.syst.has_wrapped() {}
-Value returned is $2 = false
-
-(..)
-
-(gdb) finish
-Run till exit from #0 0x08002d0e in >::delay_ms (self=0x10001fbc, ms=500)
- at $REGISTRY/stm32f30x-hal-0.2.0/src/delay.rs:38
-0x080001ee in main () at src/05-led-roulette/src/main.rs:18
-18 delay.delay_ms(half_period);
-```
-
-We are back in `main`. We have a local variable in here: `half_period`
-
-```
-(gdb) info locals
-half_period = 500
-delay = (..)
-leds = (..)
-```
-
-Now, we are going to modify this variable using the `set` command:
-
-```
-(gdb) set half_period = 100
-
-(gdb) print half_period
-$1 = 100
-```
-
-If you let program run free again using the `continue` command, you should see that the LED will
-blink at a much faster rate now!
-
-Question! What happens if you keep lowering the value of `half_period`? At what value of
-`half_period` you can no longer see the LED blink?
-
-Now, it's your turn to write a program.
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 1f1b5788a..8737c5379 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -11,7 +11,8 @@
- [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)
+ - [Light it up](05-led-roulette/light-it-up.md)
+ - [It blinks](05-led-roulette/it-blinks.md)
- [The challenge](05-led-roulette/the-challenge.md)
- [My solution](05-led-roulette/my-solution.md)
- [Hello, world!](06-hello-world/README.md)
diff --git a/src/appendix/2-how-to-use-gdb/README.md b/src/appendix/2-how-to-use-gdb/README.md
index 4153c6dcc..cdc8d6a65 100644
--- a/src/appendix/2-how-to-use-gdb/README.md
+++ b/src/appendix/2-how-to-use-gdb/README.md
@@ -83,7 +83,4 @@ Below are some useful GDB commands that can help us debug our programs. This ass
### Controlling OpenOCD Remotely
-* `monitor reset run`: Reset the CPU, starting execution over again
- * `monitor reset`: Same as above
-* `monitor reset init`: Reset the CPU, halting execution at the start
-* `monitor targets`: Display information and state of current target
+* `monitor reset`: Reset the CPU, starting execution over again
diff --git a/src/assets/gdb-layout-asm.png b/src/assets/gdb-layout-asm.png
index 70fa51c75..0f1f1ef0d 100644
Binary files a/src/assets/gdb-layout-asm.png and b/src/assets/gdb-layout-asm.png differ
diff --git a/src/assets/gdb-layout-src.png b/src/assets/gdb-layout-src.png
index 1cf5e3a86..8c374199c 100644
Binary files a/src/assets/gdb-layout-src.png and b/src/assets/gdb-layout-src.png differ
diff --git a/src/assets/roulette_fast.mp4 b/src/assets/roulette_fast.mp4
new file mode 100644
index 000000000..fec3f6c05
Binary files /dev/null and b/src/assets/roulette_fast.mp4 differ
diff --git a/src/assets/roulette_slow.mp4 b/src/assets/roulette_slow.mp4
new file mode 100644
index 000000000..a7aae2a19
Binary files /dev/null and b/src/assets/roulette_slow.mp4 differ