Merge pull request 'continue workshop' (#1) from workshop into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
muellerr committed 2026-06-11 14:11:58 +02:00
commit 0f1440e59c
32 files changed
+1354 -5

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
stages:
- hello
hello_world:
stage: hello
script:
- echo "Hello, world from GitLab CI!"
+14
View File
@@ -0,0 +1,14 @@
Embedded Rust Workshop
==========
This repository contains all components of the Embedded Rust Workshop.
This includes:
- [`slides`](./slides) contains introduction slides about this workshop and embedded Rust.
- [`exercise-book`](./exercise-book) contains the accompanying exercise book.
- [`microbit-code`](./microbit-code/) contains all example source code and exercises which run on
the microbit v2 target hardware
- [`host-client`](./host-client/) contains a host client used to communicate with the microbit v2 via a serial
interface.
File renamed without changes.
+3
View File
@@ -0,0 +1,3 @@
# Exercise book
You can build this book and display it locally using `mdbook build --open`.
File renamed without changes.
+5
View File
@@ -0,0 +1,5 @@
# Summary
- [Introduction](./introduction.md)
- [Preparation](./preparation.md)
- [Blinky Exercise](./blinky-exercise.md)
Whitespace-only changes.
+57
View File
@@ -0,0 +1,57 @@
# Introduction
This is the exercise book accompanying the Embedded Rust Workshop.
The Workshop provides exercises for Embedded Rust which run on the
[BBC microbit v2](https://microbit.org/new-microbit/).
For this exercises, you only require the following hardware:
- The [microbit v2](https://microbit.org/new-microbit/).
- A Micro-USB cable to power the microbit while also connecting it to your computer.
This little educational board has everything we need to teach you the practical part of embedded
Rust, including:
- A 5x5 LED matrix.
- The LSM303AGR on-board motion sensor.
- A serial connection accessible by a USB-CDC device
This device has even more features and external connectivity, and you can find all
of this information of the [microbit website](https://tech.microbit.org/hardware/).
## Goals
After working through this exercises, you should have the following skills:
- Extract relevant information from datasheets and/or schematics for firmware development.
- Understand and work in embedded code using the the [`embassy`](https://embassy.dev/) asynchronous
runtime, which includes using the `async` / `await` syntax.
- Schedule concurrent tasks using `embassy`.
- Work with some components of a provided HAL (or BSP).
- Using [`embassy-time`](https://docs.rs/embassy-time/latest/embassy_time/) to perform delays and
periodic operations or measure elapsed time.
- Writing drivers for simple sensors
However, these exercises do not replace or provide a good theoretic foundation and it also does
not teach general Rust programming. Low-level aspects like working with registers and details about
system boot are are intentionally skipped. The next section provides some further material
recommendations to address this.
## Further Materials
If you are a beginner in Rust, it is strongly recommended to work through the [Rust book](https://doc.rust-lang.org/book/) or work
through some other method of your choice to learn the general language.
The following materials might be valuable for you as well:
- [The embassy book](https://embassy.dev/book/) provides additional information and documentation
about the embassy asynchronous run-time.
- [The Rusty bits](https://www.youtube.com/@therustybits) provides excellent visual resources
about various embedded Rust aspects.
- [The Ferrous Systems Rust Training slides](https://rust-training.ferrous-systems.com/latest/slides/)
provide high-quality slides about various topics, including embedded Rust and aspects like
system boot and peripheral access crates.
## Preparation
We are going to start with the preparation of the software tools in the [next chapter](./preparation.md).
+88
View File
@@ -0,0 +1,88 @@
# Preparation
## Rust and `cargo`
The first thing required is the Rust compiler and the `cargo` build and dependency management
tool. The Rust installation includes the `cargo` tool, so all you need to do is install
Rust by going to the [Rust website](https://rust-lang.org/tools/install/) and following the operating
system specific instructions.
After you have done this, you can verify your installation by running:
```console
cargo version
```
Normally, you would have to also install the `thumbv7em-none-eabihf` toolchain and some other
useful tools, but this is performed automatically for you through the `rust-toolchain.toml`
file in the code directory.
## `flip-link` linker
You need to install a special linker called `flip-link` for building the software. Run
the following command in the terminal:
```console
cargo install flip-link
```
## Flasher tool `probe-rs`
Next, you need some software which allows flashing the microbit v2 via the USB interface.
We are going to use the `probe-rs` tool, which is well integrated into the Rust ecosystem:
The `probe-rs` website has [install instructions](https://probe.rs/docs/getting-started/installation/)
for various operating systems. Follow these, and then test your installation using the following
command
```console
probe-rs --version
```
## USB permission setup (Linux only)
Finally, if you are on Linux, you need to perform some steps related to `udev` to avoid permission
issues. `probe-rs` has a [page with steps you can follow](https://probe.rs/docs/getting-started/probe-setup/).
## IDE
You can use any IDE of your choice which has good [Rust Analzyer Support](https://rust-analyzer.github.io/).
If you are looking for a solid graphical IDE, [VS Code](https://code.visualstudio.com/<35;127;26M)
is an excellent choice. Make sure to install the [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) as well.
## Testing everything
Now you should have everything you need to build and flash some application to the board.
You can flash a test application now to test everything. We provide some test applications for you.
Connect the board to your computer using a Micro-USB cable. Make sure that your cable also
supports the data interface and is not power-only.
Go into the `code/app` directory.
Now, you can run the following command to build and flash a blinky application:
```console
cargo run --bin blinky
```
On the console, you should see an output like this:
```console
❯ cargo run --bin blinky
Compiling rust-app v0.1.0 (/home/muellerr/Rust/rust-embedded-workshop/code/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `probe-rs run --chip nRF52833_xxAA --allow-erase-all target/thumbv7em-none-eabihf/debug/blinky`
Erasing ✔ 100% [####################] 48.00 KiB @ 35.81 KiB/s (took 1s) Finished in 3.55s
-- microbit v2 Blinky application --
```
If `probe-rs` can not detect anything, make sure that (a) the board is connected through USB, and
(b) the `udev` rules were setup properly if you are on Linux.
If everything goes right, you should see the LED in the top-left corner blinking with a frequency of 1 second.
If this is the case, congratulations, you have built and flashed an embedded Rust app and you made if through
the preparation chapter successfully!
In the [first exercise](./blinky-exercise.md), you are going to build most parts of a blinky.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
Binary file not shown.
File renamed without changes.
File renamed without changes.
@@ -26,7 +26,8 @@ async fn main(_spawner: Spawner) -> ! {
row1.set_high();
col1.set_low();
loop {
defmt::debug!("blinking LED");
row1.toggle();
Timer::after_millis(200).await;
Timer::after_millis(1000).await;
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,15 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip nRF52833_xxAA --allow-erase-all"
rustflags = [
"-C", "link-arg=-Tlink.x", # use the cortex-m-rt linker script
"-C", "linker=flip-link", # adds stack overflow protection
"-C", "link-arg=-Tdefmt.x", # defmt support
]
[build]
target = "thumbv7em-none-eabihf"
[env]
DEFMT_LOG = "info"
+1032
View File
File diff suppressed because it is too large. Load diff
+30
View File
@@ -0,0 +1,30 @@
[package]
name = "exercises"
version = "0.1.0"
edition = "2024"
[dependencies]
embassy-executor = { version = "0.10.0", features = ["platform-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
embassy-time = { version = "0.5.1", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-nrf = { version = "0.10.0", features = ["defmt", "nrf52833", "time-driver-rtc1", "unstable-pac", "time"] }
defmt = "1.0.1"
defmt-rtt = "1.0.0"
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0" }
embedded-hal-bus = { version = "0.3", features = ["async"] }
embedded-io = { version = "0.7.1", features = ["defmt"] }
embedded-io-async = { version = "0.7.0", features = ["defmt"] }
portable-atomic = "1"
[profile.release]
debug = 2
[package.metadata.embassy]
build = [
{ target = "thumbv7em-none-eabi", artifact-dir = "out/examples/nrf52840" }
]
+31
View File
@@ -0,0 +1,31 @@
//! 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,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
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");
}
+6
View File
@@ -0,0 +1,6 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x00000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 128K
}
+23
View File
@@ -0,0 +1,23 @@
#![no_std]
#![no_main]
use exercises as _;
use embassy_executor::Spawner;
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let _periphs = embassy_nrf::init(embassy_nrf::config::Config::default());
// TODOs
//
// Step 1: Initialize the hardware by using the `embassy_nrf::init` method.
// Step 2: print some initial log to the console, for example the exercise name.
// Step 3: Create an Output GPIO driver for ROW1
// Step 4: Create an Output GPIO driver for COL1
// Step 5: Set the GPIOs to the correct state to drive LED D2, which is the LED in
// the upper left corner of the LED matrix
// Step 6: Inside the empty loop, toggle the correct output to toggle the LED.
// Step 7: Led the LED blink with a certain frequency by using embassy_time::Timer
loop {}
}
@@ -0,0 +1,30 @@
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_time::Timer;
use exercises as _;
use embassy_nrf::gpio::Output;
#[embassy_executor::main]
async fn main(_spawner: Spawner) -> ! {
let periphs = embassy_nrf::init(embassy_nrf::config::Config::default());
defmt::println!("-- microbit v2 Blinky application --");
let mut row1 = Output::new(
periphs.P0_21,
embassy_nrf::gpio::Level::Low,
embassy_nrf::gpio::OutputDrive::Standard,
);
let _col1 = Output::new(
periphs.P0_28,
embassy_nrf::gpio::Level::Low,
embassy_nrf::gpio::OutputDrive::Standard,
);
loop {
row1.toggle();
Timer::after_millis(200).await;
}
}
+5
View File
@@ -0,0 +1,5 @@
#![no_std]
use defmt_rtt as _;
use embassy_nrf as _;
use panic_probe as _;
+5
View File
@@ -0,0 +1,5 @@
[toolchain]
channel = "stable"
components = [ "llvm-tools-preview", "clippy" ]
targets = [ "thumbv7em-none-eabihf" ]
profile = "default"
-3
View File
@@ -1,3 +0,0 @@
# Summary
- [Chapter 1](./chapter_1.md)
-1
View File
@@ -1 +0,0 @@
# Chapter 1
+1
View File
@@ -0,0 +1 @@
*lock.*
Binary file not shown.