start tutorial
This commit is contained in:
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Rust build output in workshop steps
|
||||||
|
tutorial/**/target/
|
||||||
|
|
||||||
|
# Probe-rs RTT logs
|
||||||
|
tutorial/**/target/logs/
|
||||||
|
|
||||||
|
# Editor temp files
|
||||||
|
*~
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
sources
|
||||||
31
HARDWARE.md
Normal file
31
HARDWARE.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Hardware Baseline (Bluepill + ST-Link)
|
||||||
|
|
||||||
|
## Board and Probe
|
||||||
|
|
||||||
|
- MCU board: STM32F103C8T6 Bluepill
|
||||||
|
- Debug probe: ST-Link (SWD)
|
||||||
|
|
||||||
|
## SWD Wiring
|
||||||
|
|
||||||
|
1. ST-Link `SWDIO` -> Bluepill `PA13`
|
||||||
|
2. ST-Link `SWCLK` -> Bluepill `PA14`
|
||||||
|
3. ST-Link `GND` -> Bluepill `GND`
|
||||||
|
4. ST-Link `3V3` -> Bluepill `3V3` (falls notwendig)
|
||||||
|
5. Optional: ST-Link `NRST` -> Bluepill `NRST`
|
||||||
|
|
||||||
|
## Workshop I/O Wiring
|
||||||
|
|
||||||
|
1. LED:
|
||||||
|
- Onboard LED an `PC13` (active-low)
|
||||||
|
2. Button:
|
||||||
|
- Externer Taster von `PA0` nach `GND`
|
||||||
|
- Interner Pull-up wird im Code aktiviert
|
||||||
|
3. Analog:
|
||||||
|
- Poti/Sensor-Ausgang an `PA1`
|
||||||
|
- Sensorversorgung über `3V3` + `GND`
|
||||||
|
|
||||||
|
## Sanity Checks
|
||||||
|
|
||||||
|
1. `probe-rs list` zeigt ST-Link an.
|
||||||
|
2. `probe-rs chip list | grep STM32F103C8` findet Target.
|
||||||
|
3. Beim Flashen keine "No probe found"-Meldung.
|
||||||
33
README.md
Normal file
33
README.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Rust on Robots - Workshop (Didacta Ed.)
|
||||||
|
|
||||||
|
Rust ist schnell, modern und hilft dabei, typische Embedded-Fehler früh zu vermeiden – genau das, was man auf Robotern braucht.
|
||||||
|
In diesem Workshop gebe ich eine kurze, praxisnahe Einführung in Rust und wir steigen dann direkt in gemeinsame Übungen ein: selbst Rust schreiben, kleine Aufgaben lösen und anschließend Rust auf einem STM32 Bluepill flashen und testen.
|
||||||
|
Ideal für RoboCup-Interessierte aus der außerschulischen bzw. beruflichen Bildung, die schon einmal programmiert haben und Rust als Werkzeug für robuste Robotik ausprobieren wollen.
|
||||||
|
|
||||||
|
## Workshop Layout
|
||||||
|
|
||||||
|
Dieses Repository ist als "Rust by Example"-ähnlicher Lernpfad aufgebaut:
|
||||||
|
|
||||||
|
- `tutorial/00-setup-live` bis `tutorial/08-final-combined`
|
||||||
|
- Jeder Schritt hat:
|
||||||
|
- `task/` (Aufgabe mit TODOs)
|
||||||
|
- `solution/` (Referenzlösung)
|
||||||
|
- Teilnehmer arbeiten nur in `task/`.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. Host vorbereiten:
|
||||||
|
- `bash scripts/setup-live.sh`
|
||||||
|
2. Host prüfen:
|
||||||
|
- `bash scripts/verify-host.sh`
|
||||||
|
3. Probe prüfen:
|
||||||
|
- `bash scripts/verify-probe.sh`
|
||||||
|
4. Schritt ausführen:
|
||||||
|
- `bash scripts/run-step.sh 01 task`
|
||||||
|
|
||||||
|
## Core Files
|
||||||
|
|
||||||
|
- [WORKSHOP.md](/home/wieerwill/Dokumente/GitHub/didkata-rust-on-robots/WORKSHOP.md)
|
||||||
|
- [HARDWARE.md](/home/wieerwill/Dokumente/GitHub/didkata-rust-on-robots/HARDWARE.md)
|
||||||
|
- [TROUBLESHOOTING.md](/home/wieerwill/Dokumente/GitHub/didkata-rust-on-robots/TROUBLESHOOTING.md)
|
||||||
|
- [references/source-map.md](/home/wieerwill/Dokumente/GitHub/didkata-rust-on-robots/references/source-map.md)
|
||||||
41
TROUBLESHOOTING.md
Normal file
41
TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
## Host Setup
|
||||||
|
|
||||||
|
1. `rustup: command not found`
|
||||||
|
- Rust via rustup installieren.
|
||||||
|
2. `probe-rs: command not found`
|
||||||
|
- `cargo install probe-rs-tools`
|
||||||
|
3. Target fehlt
|
||||||
|
- `rustup target add thumbv7m-none-eabi`
|
||||||
|
|
||||||
|
## Probe / Flashing
|
||||||
|
|
||||||
|
1. `No probe found`
|
||||||
|
- USB-Kabel/Port prüfen
|
||||||
|
- ST-Link Treiber/udev prüfen
|
||||||
|
- `probe-rs list` erneut ausführen
|
||||||
|
2. `chip not found`
|
||||||
|
- Runner-String prüfen: `STM32F103C8`
|
||||||
|
3. `Permission denied` (Linux)
|
||||||
|
- udev-Regeln für ST-Link setzen und neu laden
|
||||||
|
|
||||||
|
## Runtime Behavior
|
||||||
|
|
||||||
|
1. LED blinkt nicht
|
||||||
|
- PC13 active-low beachten
|
||||||
|
- Versorgung prüfen
|
||||||
|
2. Button reagiert nicht
|
||||||
|
- Taster nach GND verdrahten
|
||||||
|
- `PA0` korrekt belegt?
|
||||||
|
3. Analogwerte ändern sich nicht
|
||||||
|
- Sensor/Poti wirklich an `PA1`
|
||||||
|
- Gemeinsame Masse sicherstellen
|
||||||
|
|
||||||
|
## Fallback in Session
|
||||||
|
|
||||||
|
Wenn eine Station nicht stabil läuft:
|
||||||
|
|
||||||
|
1. Mit funktionierender Nachbarn pairen.
|
||||||
|
2. `solution/` ausführen.
|
||||||
|
3. Später wieder auf `task/` zurückwechseln.
|
||||||
71
WORKSHOP.md
Normal file
71
WORKSHOP.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Workshop Plan (60 Minutes)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Von Rust-Basics zu echter Hardware auf STM32F103C8 Bluepill:
|
||||||
|
|
||||||
|
- LED blinken
|
||||||
|
- Button einlesen
|
||||||
|
- Analogwert einlesen
|
||||||
|
- Ausgabe live über `probe-rs` / RTT sehen
|
||||||
|
|
||||||
|
## Timing
|
||||||
|
|
||||||
|
1. 00:00-00:10: Setup + Probe Smoke Test (`00`)
|
||||||
|
2. 00:10-00:28: Rust Basics (`01`-`03`)
|
||||||
|
3. 00:28-00:36: Erstes `no_std` Firmware-Projekt (`04`)
|
||||||
|
4. 00:36-00:44: LED Blinky (`05`)
|
||||||
|
5. 00:44-00:50: Button Input (`06`)
|
||||||
|
6. 00:50-00:56: Analog Readout (`07`)
|
||||||
|
7. 00:56-01:00: Integration (`08`)
|
||||||
|
|
||||||
|
## Rules During Workshop
|
||||||
|
|
||||||
|
1. Bearbeite nur `task/`.
|
||||||
|
2. Nutze `solution/` nur als Hilfe bei Blockern.
|
||||||
|
3. Falls >3 Minuten blockiert: Diff vergleichen, minimalen Fix übernehmen, weiter.
|
||||||
|
4. Bei Embedded-Schritten: Board vor jedem `cargo run` korrekt verkabeln.
|
||||||
|
|
||||||
|
## Repo Management During Workshop
|
||||||
|
|
||||||
|
1. `main` enthält Aufgaben und Referenzlösungen.
|
||||||
|
2. Instructor kann Wiederherstellungspunkte als Tags setzen:
|
||||||
|
- `step-00-start` ... `step-08-solution`
|
||||||
|
3. Teilnehmer arbeiten auf lokalem Branch:
|
||||||
|
- `participant/<name>`
|
||||||
|
4. Wenn eine Aufgabe nicht weitergeht:
|
||||||
|
- minimalen Diff aus `solution/` übernehmen und fortfahren.
|
||||||
|
|
||||||
|
## Step Contract
|
||||||
|
|
||||||
|
Jeder Schritt enthält:
|
||||||
|
|
||||||
|
- `README.md` mit Ziel, Ablauf, Done-Check
|
||||||
|
- `task/` (Aufgabe)
|
||||||
|
- `solution/` (Referenz)
|
||||||
|
|
||||||
|
## Script Contract
|
||||||
|
|
||||||
|
- `scripts/setup-live.sh`
|
||||||
|
- `scripts/verify-host.sh`
|
||||||
|
- `scripts/verify-probe.sh`
|
||||||
|
- `scripts/run-step.sh <step-id> <task|solution>`
|
||||||
|
- `scripts/check-step.sh <step-id>`
|
||||||
|
|
||||||
|
## Target Contract (Embedded Steps 04-08)
|
||||||
|
|
||||||
|
- Target: `thumbv7m-none-eabi`
|
||||||
|
- Runner: `probe-rs run --chip STM32F103C8`
|
||||||
|
- Logging: `defmt-rtt` + `panic-probe`
|
||||||
|
|
||||||
|
## Acceptance Scenarios
|
||||||
|
|
||||||
|
1. Steps `01`-`03`: `cargo run` funktioniert für `task/` und `solution/`.
|
||||||
|
2. Steps `04`-`08`: `cargo build --release` funktioniert für `task/` und `solution/`.
|
||||||
|
3. Probe-Verbindung:
|
||||||
|
- `probe-rs list` erkennt ST-Link.
|
||||||
|
4. Behavior:
|
||||||
|
- `05`: LED blinkt
|
||||||
|
- `06`: Press/Release Logs
|
||||||
|
- `07`: ADC-Wert reagiert auf Eingang
|
||||||
|
- `08`: integriertes Verhalten läuft stabil
|
||||||
39
source-map.md
Normal file
39
source-map.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Source Map
|
||||||
|
|
||||||
|
Dieses Mapping dokumentiert, welche gesammelten Quellen in welche Workshop-Bausteine eingeflossen sind.
|
||||||
|
|
||||||
|
## Rust Basics (`01`-`03`)
|
||||||
|
|
||||||
|
1. `sources/rust-by-example/*`
|
||||||
|
- Schrittweiser Lernstil mit kleinen, lauffähigen Beispielen
|
||||||
|
2. `sources/Introduction - A Gentle Introduction to Rust.md`
|
||||||
|
- sanfter Einstieg in Ownership/Borrowing
|
||||||
|
3. `sources/Learn Rust - Rust Programming Language.md`
|
||||||
|
- Orientierung an offiziellen Lernpfaden
|
||||||
|
|
||||||
|
## Embedded Foundations (`04`)
|
||||||
|
|
||||||
|
1. `sources/Intro to Rust on Embedded Your First no_std Project with Code Examples – Hubble Network Community.md`
|
||||||
|
- `no_std`, PAC/HAL/BSP, Einstiegspfad
|
||||||
|
2. `sources/rust-embedded-book/src/intro/no-std.md`
|
||||||
|
- saubere no_std-Grundlagen
|
||||||
|
3. `sources/rust-embedded-book/src/peripherals/singletons.md`
|
||||||
|
- Ownership auf Hardware-Peripherie übertragen
|
||||||
|
|
||||||
|
## Tooling and Workflow (`00`, scripts)
|
||||||
|
|
||||||
|
1. `sources/Embedded Rust Toolchains – Albert Skog.md`
|
||||||
|
- Probe-rs/probe-run Workflow-Einordnung
|
||||||
|
2. `sources/Embassy Book.md`
|
||||||
|
- praktischer Cargo + probe-rs Runner-Ansatz
|
||||||
|
|
||||||
|
## STM32 Practical Steps (`05`-`08`)
|
||||||
|
|
||||||
|
1. `sources/rust-embassy-stm32/src/bin/{blinky.rs,button.rs,adc.rs}`
|
||||||
|
- konkrete Zielübungen (LED/Button/ADC + Logs)
|
||||||
|
2. `sources/STM32F4 Embedded Rust at the HAL *.md`
|
||||||
|
- didaktische Sequenz: GPIO -> Input -> ADC -> Integration
|
||||||
|
3. `sources/AnyLeaf articles Writing embedded firmware using Rust.md`
|
||||||
|
- Register/HAL-Abstraktionslevel, Embedded-Rust Argumentation
|
||||||
|
4. `sources/Brave new IO Embedded in Rust.md`
|
||||||
|
- Ownership/Typisierung für sichere Hardwarezugriffe
|
||||||
17
tutorial/00-setup-live/README.md
Normal file
17
tutorial/00-setup-live/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# 00 - Setup Live (10 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Jede Station kann Rust-Code bauen und eine Bluepill über ST-Link erkennen.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. install rustup
|
||||||
|
2. add target
|
||||||
|
3. add probe-rs
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. `rustc`, `cargo`, `probe-rs` verfügbar.
|
||||||
|
2. `thumbv7m-none-eabi` installiert.
|
||||||
|
3. `probe-rs list` zeigt die Probe.
|
||||||
7
tutorial/00-setup-live/task/checklist.md
Normal file
7
tutorial/00-setup-live/task/checklist.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Task Checklist
|
||||||
|
|
||||||
|
- [ ] Rust toolchain installiert (`rustup`, `cargo`, `rustc`)
|
||||||
|
- [ ] Target `thumbv7m-none-eabi` installiert
|
||||||
|
- [ ] `probe-rs-tools` installiert
|
||||||
|
- [ ] ST-Link wird von `probe-rs list` erkannt
|
||||||
|
- [ ] `probe-rs chip list | grep STM32F103C8` liefert Treffer
|
||||||
36
tutorial/00-setup-live/task/setup.sh
Executable file
36
tutorial/00-setup-live/task/setup.sh
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "[verify-host] rustup"
|
||||||
|
if ! command -v rustup >/dev/null 2>&1; then
|
||||||
|
echo "rustup not found. Install from https://rustup.rs/ and rerun."
|
||||||
|
echo "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rustup --version
|
||||||
|
|
||||||
|
echo "[verify-host] cargo"
|
||||||
|
cargo --version
|
||||||
|
|
||||||
|
echo "[verify-host] rustc"
|
||||||
|
rustc --version
|
||||||
|
|
||||||
|
echo "[setup] ensuring stable toolchain..."
|
||||||
|
rustup toolchain install stable
|
||||||
|
rustup default stable
|
||||||
|
|
||||||
|
echo "[setup] adding embedded target thumbv7m-none-eabi..."
|
||||||
|
rustup target add thumbv7m-none-eabi
|
||||||
|
|
||||||
|
echo "[setup] checking probe-rs..."
|
||||||
|
if ! command -v probe-rs >/dev/null 2>&1; then
|
||||||
|
echo "[setup] installing probe-rs-tools via cargo..."
|
||||||
|
cargo install probe-rs-tools
|
||||||
|
sudo groupadd --system plugdev
|
||||||
|
sudo usermod -a -G plugdev $USER
|
||||||
|
else
|
||||||
|
echo "[setup] probe-rs already installed"
|
||||||
|
probe-rs --version
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[setup] done"
|
||||||
20
tutorial/01-rust-hello/README.md
Normal file
20
tutorial/01-rust-hello/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 01 - Rust Hello (5 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Erstes lauffähiges Rust-Programm, das Daten über `println!` ausgibt.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 01 task`
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
1. Öffne `task/src/main.rs`.
|
||||||
|
2. Ersetze die TODO-Werte.
|
||||||
|
3. Starte erneut mit `cargo run`.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. Das Programm kompiliert.
|
||||||
|
2. Die Ausgabe enthält euren Namen und Workshop-Titel.
|
||||||
4
tutorial/01-rust-hello/solution/Cargo.toml
Normal file
4
tutorial/01-rust-hello/solution/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "step01_rust_hello_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
8
tutorial/01-rust-hello/solution/src/main.rs
Normal file
8
tutorial/01-rust-hello/solution/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fn main() {
|
||||||
|
let participant_name = "Didacta Participant";
|
||||||
|
let workshop_title = "Rust on Embedded @ Didacta";
|
||||||
|
|
||||||
|
println!("Hello, {participant_name}!");
|
||||||
|
println!("Welcome to: {workshop_title}");
|
||||||
|
println!("Next step: types, control flow, and ownership.");
|
||||||
|
}
|
||||||
4
tutorial/01-rust-hello/task/Cargo.toml
Normal file
4
tutorial/01-rust-hello/task/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "step01_rust_hello_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
9
tutorial/01-rust-hello/task/src/main.rs
Normal file
9
tutorial/01-rust-hello/task/src/main.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fn main() {
|
||||||
|
// TODO: Replace these strings with your own values.
|
||||||
|
let participant_name = "Your Name";
|
||||||
|
let workshop_title = "Rust on Embedded @ Didacta";
|
||||||
|
|
||||||
|
println!("Hello, {participant_name}!");
|
||||||
|
println!("Welcome to: {workshop_title}");
|
||||||
|
println!("Next step: types, control flow, and ownership.");
|
||||||
|
}
|
||||||
20
tutorial/02-rust-types-control/README.md
Normal file
20
tutorial/02-rust-types-control/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 02 - Types and Control Flow (6 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Mit Typen, `if`, und `match` kleine Entscheidungen ausdrücken.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 02 task`
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
1. Passe die Schwellwerte in `classify_adc` an.
|
||||||
|
2. Teste mit mehreren ADC-Werten.
|
||||||
|
3. Beobachte die Ausgabe von `pressed/released` und `low/medium/high`.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. Das Programm kompiliert.
|
||||||
|
2. Für unterschiedliche Werte entstehen unterschiedliche Klassifizierungen.
|
||||||
4
tutorial/02-rust-types-control/solution/Cargo.toml
Normal file
4
tutorial/02-rust-types-control/solution/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "step02_rust_types_control_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
31
tutorial/02-rust-types-control/solution/src/main.rs
Normal file
31
tutorial/02-rust-types-control/solution/src/main.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
fn classify_button(pressed: bool) -> &'static str {
|
||||||
|
if pressed {
|
||||||
|
"pressed"
|
||||||
|
} else {
|
||||||
|
"released"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classify_adc(raw: u16) -> &'static str {
|
||||||
|
const LOW_MAX: u16 = 1365;
|
||||||
|
const MID_MAX: u16 = 2730;
|
||||||
|
|
||||||
|
match raw {
|
||||||
|
0..=LOW_MAX => "low",
|
||||||
|
(LOW_MAX + 1)..=MID_MAX => "medium",
|
||||||
|
_ => "high",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let button_samples = [false, true, true, false];
|
||||||
|
let adc_samples: [u16; 5] = [120, 900, 1500, 2600, 3900];
|
||||||
|
|
||||||
|
for pressed in button_samples {
|
||||||
|
println!("button: {}", classify_button(pressed));
|
||||||
|
}
|
||||||
|
|
||||||
|
for raw in adc_samples {
|
||||||
|
println!("adc raw={raw:4} => {}", classify_adc(raw));
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tutorial/02-rust-types-control/task/Cargo.toml
Normal file
4
tutorial/02-rust-types-control/task/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "step02_rust_types_control_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
29
tutorial/02-rust-types-control/task/src/main.rs
Normal file
29
tutorial/02-rust-types-control/task/src/main.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
fn classify_button(pressed: bool) -> &'static str {
|
||||||
|
if pressed {
|
||||||
|
"pressed"
|
||||||
|
} else {
|
||||||
|
"released"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classify_adc(raw: u16) -> &'static str {
|
||||||
|
// TODO: Adjust thresholds if you want a different behavior.
|
||||||
|
match raw {
|
||||||
|
0..=1200 => "low",
|
||||||
|
1201..=2800 => "medium",
|
||||||
|
_ => "high",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let button_samples = [false, true, true, false];
|
||||||
|
let adc_samples: [u16; 5] = [120, 900, 1500, 2600, 3900];
|
||||||
|
|
||||||
|
for pressed in button_samples {
|
||||||
|
println!("button: {}", classify_button(pressed));
|
||||||
|
}
|
||||||
|
|
||||||
|
for raw in adc_samples {
|
||||||
|
println!("adc raw={raw:4} => {}", classify_adc(raw));
|
||||||
|
}
|
||||||
|
}
|
||||||
20
tutorial/03-rust-ownership-borrow/README.md
Normal file
20
tutorial/03-rust-ownership-borrow/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 03 - Ownership and Borrowing (7 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Ownership und Borrowing an einem kleinen Robot-State praktisch verstehen.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 03 task`
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
1. Lies die Funktionen mit `&RobotState` und `&mut RobotState`.
|
||||||
|
2. Ergänze die TODOs in der Task-Datei.
|
||||||
|
3. Achte auf Compiler-Fehler, wenn mutable/immutable borrows kollidieren.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. Das Programm kompiliert.
|
||||||
|
2. LED-State, Button-Count und ADC-Wert werden korrekt aktualisiert.
|
||||||
4
tutorial/03-rust-ownership-borrow/solution/Cargo.toml
Normal file
4
tutorial/03-rust-ownership-borrow/solution/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "step03_rust_ownership_borrow_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
41
tutorial/03-rust-ownership-borrow/solution/src/main.rs
Normal file
41
tutorial/03-rust-ownership-borrow/solution/src/main.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
struct RobotState {
|
||||||
|
led_on: bool,
|
||||||
|
button_count: u32,
|
||||||
|
last_adc: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_state(state: &RobotState) {
|
||||||
|
println!(
|
||||||
|
"state => led_on={}, button_count={}, last_adc={}",
|
||||||
|
state.led_on, state.button_count, state.last_adc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_led(state: &mut RobotState) {
|
||||||
|
state.led_on = !state.led_on;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_button_press(state: &mut RobotState) {
|
||||||
|
state.button_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_adc(state: &mut RobotState, raw: u16) {
|
||||||
|
state.last_adc = raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut state = RobotState {
|
||||||
|
led_on: false,
|
||||||
|
button_count: 0,
|
||||||
|
last_adc: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
print_state(&state);
|
||||||
|
|
||||||
|
toggle_led(&mut state);
|
||||||
|
record_button_press(&mut state);
|
||||||
|
update_adc(&mut state, 2024);
|
||||||
|
|
||||||
|
print_state(&state);
|
||||||
|
}
|
||||||
4
tutorial/03-rust-ownership-borrow/task/Cargo.toml
Normal file
4
tutorial/03-rust-ownership-borrow/task/Cargo.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "step03_rust_ownership_borrow_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
42
tutorial/03-rust-ownership-borrow/task/src/main.rs
Normal file
42
tutorial/03-rust-ownership-borrow/task/src/main.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
struct RobotState {
|
||||||
|
led_on: bool,
|
||||||
|
button_count: u32,
|
||||||
|
last_adc: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_state(state: &RobotState) {
|
||||||
|
println!(
|
||||||
|
"state => led_on={}, button_count={}, last_adc={}",
|
||||||
|
state.led_on, state.button_count, state.last_adc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_led(state: &mut RobotState) {
|
||||||
|
state.led_on = !state.led_on;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_button_press(state: &mut RobotState) {
|
||||||
|
state.button_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_adc(state: &mut RobotState, raw: u16) {
|
||||||
|
// TODO: keep this function as the single writer for ADC state.
|
||||||
|
state.last_adc = raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut state = RobotState {
|
||||||
|
led_on: false,
|
||||||
|
button_count: 0,
|
||||||
|
last_adc: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
print_state(&state);
|
||||||
|
|
||||||
|
toggle_led(&mut state);
|
||||||
|
record_button_press(&mut state);
|
||||||
|
update_adc(&mut state, 2024);
|
||||||
|
|
||||||
|
print_state(&state);
|
||||||
|
}
|
||||||
20
tutorial/04-embedded-no-std-hello/README.md
Normal file
20
tutorial/04-embedded-no-std-hello/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 04 - Embedded no_std Hello (8 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Erstes Bare-Metal-Programm mit `#![no_std]`, `#![no_main]` und RTT Logs.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 04 task`
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
1. Lies die Top-Level-Attribute in `task/src/main.rs`.
|
||||||
|
2. Passe die Log-Ausgaben an.
|
||||||
|
3. Flashe mit `cargo run --release`.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. Firmware läuft auf Bluepill.
|
||||||
|
2. RTT zeigt zyklisch eine "alive"-Meldung.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/04-embedded-no-std-hello/solution/Cargo.toml
Normal file
22
tutorial/04-embedded-no-std-hello/solution/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step04_embedded_no_std_hello_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/04-embedded-no-std-hello/solution/memory.x
Normal file
5
tutorial/04-embedded-no-std-hello/solution/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
17
tutorial/04-embedded-no-std-hello/solution/src/main.rs
Normal file
17
tutorial/04-embedded-no-std-hello/solution/src/main.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
info!("step04: boot ok");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
asm::delay(8_000_000);
|
||||||
|
info!("step04: alive");
|
||||||
|
}
|
||||||
|
}
|
||||||
12
tutorial/04-embedded-no-std-hello/task/.cargo/config.toml
Normal file
12
tutorial/04-embedded-no-std-hello/task/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/04-embedded-no-std-hello/task/Cargo.toml
Normal file
22
tutorial/04-embedded-no-std-hello/task/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step04_embedded_no_std_hello_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/04-embedded-no-std-hello/task/memory.x
Normal file
5
tutorial/04-embedded-no-std-hello/task/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
18
tutorial/04-embedded-no-std-hello/task/src/main.rs
Normal file
18
tutorial/04-embedded-no-std-hello/task/src/main.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
// TODO: personalize these log lines.
|
||||||
|
info!("step04: no_std hello on stm32f103c8");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
asm::delay(8_000_000);
|
||||||
|
info!("step04: alive");
|
||||||
|
}
|
||||||
|
}
|
||||||
20
tutorial/05-led-blink/README.md
Normal file
20
tutorial/05-led-blink/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 05 - LED Blink (8 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Onboard-LED (PC13, active-low) per Timer toggeln.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 05 task`
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
1. Prüfe in `task/src/main.rs`, welche Pegel "LED an/aus" bedeuten.
|
||||||
|
2. Passe Blinkfrequenz an.
|
||||||
|
3. Verifiziere LED und Logs.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. LED blinkt sichtbar.
|
||||||
|
2. Logs zeigen den Schaltzustand.
|
||||||
12
tutorial/05-led-blink/solution/.cargo/config.toml
Normal file
12
tutorial/05-led-blink/solution/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/05-led-blink/solution/Cargo.toml
Normal file
22
tutorial/05-led-blink/solution/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step05_led_blink_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/05-led-blink/solution/memory.x
Normal file
5
tutorial/05-led-blink/solution/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/05-led-blink/solution/rust-toolchain.toml
Normal file
4
tutorial/05-led-blink/solution/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
31
tutorial/05-led-blink/solution/src/main.rs
Normal file
31
tutorial/05-led-blink/solution/src/main.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioc = dp.GPIOC.split(&mut rcc);
|
||||||
|
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
|
||||||
|
|
||||||
|
let mut timer = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
timer.start(2.Hz()).unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
info!("led on (pc13 low)");
|
||||||
|
led.set_low();
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
|
||||||
|
info!("led off (pc13 high)");
|
||||||
|
led.set_high();
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
tutorial/05-led-blink/task/.cargo/config.toml
Normal file
12
tutorial/05-led-blink/task/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/05-led-blink/task/Cargo.toml
Normal file
22
tutorial/05-led-blink/task/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step05_led_blink_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/05-led-blink/task/memory.x
Normal file
5
tutorial/05-led-blink/task/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/05-led-blink/task/rust-toolchain.toml
Normal file
4
tutorial/05-led-blink/task/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
34
tutorial/05-led-blink/task/src/main.rs
Normal file
34
tutorial/05-led-blink/task/src/main.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioc = dp.GPIOC.split(&mut rcc);
|
||||||
|
|
||||||
|
// Bluepill onboard LED on PC13 is active-low.
|
||||||
|
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
|
||||||
|
|
||||||
|
let mut timer = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
timer.start(2.Hz()).unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// TODO: swap set_high/set_low and observe behavior.
|
||||||
|
info!("led on (pc13 low)");
|
||||||
|
led.set_low();
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
|
||||||
|
info!("led off (pc13 high)");
|
||||||
|
led.set_high();
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
tutorial/06-button-input/README.md
Normal file
20
tutorial/06-button-input/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 06 - Button Input (6 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Button an PA0 (Pull-up) lesen und Press/Release als Log ausgeben.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 06 task`
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
1. Verdrahtung prüfen: PA0 -> Button -> GND.
|
||||||
|
2. Starte `task` und beobachte Logs.
|
||||||
|
3. Passe Polling-Intervall an.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. Pressed/Released wird korrekt erkannt.
|
||||||
|
2. Keine Flut von Wiederhol-Logs bei gehaltenem Taster.
|
||||||
12
tutorial/06-button-input/solution/.cargo/config.toml
Normal file
12
tutorial/06-button-input/solution/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/06-button-input/solution/Cargo.toml
Normal file
22
tutorial/06-button-input/solution/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step06_button_input_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/06-button-input/solution/memory.x
Normal file
5
tutorial/06-button-input/solution/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/06-button-input/solution/rust-toolchain.toml
Normal file
4
tutorial/06-button-input/solution/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
37
tutorial/06-button-input/solution/src/main.rs
Normal file
37
tutorial/06-button-input/solution/src/main.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
|
let button = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut timer = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
timer.start(40.Hz()).unwrap();
|
||||||
|
|
||||||
|
let mut last_pressed = false;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let pressed = button.is_low();
|
||||||
|
if pressed != last_pressed {
|
||||||
|
if pressed {
|
||||||
|
info!("button: pressed");
|
||||||
|
} else {
|
||||||
|
info!("button: released");
|
||||||
|
}
|
||||||
|
last_pressed = pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
tutorial/06-button-input/task/.cargo/config.toml
Normal file
12
tutorial/06-button-input/task/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/06-button-input/task/Cargo.toml
Normal file
22
tutorial/06-button-input/task/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step06_button_input_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/06-button-input/task/memory.x
Normal file
5
tutorial/06-button-input/task/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/06-button-input/task/rust-toolchain.toml
Normal file
4
tutorial/06-button-input/task/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
40
tutorial/06-button-input/task/src/main.rs
Normal file
40
tutorial/06-button-input/task/src/main.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
|
|
||||||
|
// Button wiring: PA0 -> button -> GND
|
||||||
|
let button = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut timer = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
timer.start(40.Hz()).unwrap();
|
||||||
|
|
||||||
|
let mut last_pressed = false;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let pressed = button.is_low();
|
||||||
|
if pressed != last_pressed {
|
||||||
|
if pressed {
|
||||||
|
info!("button: pressed");
|
||||||
|
} else {
|
||||||
|
info!("button: released");
|
||||||
|
}
|
||||||
|
last_pressed = pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll at 25ms to avoid too much log spam.
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
tutorial/07-analog-readout/README.md
Normal file
20
tutorial/07-analog-readout/README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 07 - Analog Readout (6 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
ADC auf PA1 lesen und Werte über RTT streamen.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 07 task`
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
1. Verdrahtung prüfen: Analogsignal an PA1.
|
||||||
|
2. Werte ausgeben und in drei Bereiche klassifizieren.
|
||||||
|
3. Potentiometer/Sensor bewegen und Änderung beobachten.
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. Rohwert ändert sich mit Eingangsspannung.
|
||||||
|
2. Klassifizierung (`low/medium/high`) reagiert sinnvoll.
|
||||||
12
tutorial/07-analog-readout/solution/.cargo/config.toml
Normal file
12
tutorial/07-analog-readout/solution/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/07-analog-readout/solution/Cargo.toml
Normal file
22
tutorial/07-analog-readout/solution/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step07_analog_readout_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/07-analog-readout/solution/memory.x
Normal file
5
tutorial/07-analog-readout/solution/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/07-analog-readout/solution/rust-toolchain.toml
Normal file
4
tutorial/07-analog-readout/solution/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
37
tutorial/07-analog-readout/solution/src/main.rs
Normal file
37
tutorial/07-analog-readout/solution/src/main.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{adc::Adc, pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
fn classify(raw: u16) -> &'static str {
|
||||||
|
match raw {
|
||||||
|
0..=1365 => "low",
|
||||||
|
1366..=2730 => "medium",
|
||||||
|
_ => "high",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
|
|
||||||
|
let mut adc = Adc::new(dp.ADC1, &mut rcc);
|
||||||
|
let mut analog_pin = gpioa.pa1.into_analog(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut timer = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
timer.start(10.Hz()).unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let raw: u16 = adc.read(&mut analog_pin).unwrap();
|
||||||
|
info!("adc raw={} level={}", raw, classify(raw));
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
tutorial/07-analog-readout/task/.cargo/config.toml
Normal file
12
tutorial/07-analog-readout/task/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/07-analog-readout/task/Cargo.toml
Normal file
22
tutorial/07-analog-readout/task/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step07_analog_readout_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/07-analog-readout/task/memory.x
Normal file
5
tutorial/07-analog-readout/task/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/07-analog-readout/task/rust-toolchain.toml
Normal file
4
tutorial/07-analog-readout/task/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
39
tutorial/07-analog-readout/task/src/main.rs
Normal file
39
tutorial/07-analog-readout/task/src/main.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{adc::Adc, pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
fn classify(raw: u16) -> &'static str {
|
||||||
|
// TODO: tune thresholds for your sensor range.
|
||||||
|
match raw {
|
||||||
|
0..=1365 => "low",
|
||||||
|
1366..=2730 => "medium",
|
||||||
|
_ => "high",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
|
|
||||||
|
let mut adc = Adc::new(dp.ADC1, &mut rcc);
|
||||||
|
let mut analog_pin = gpioa.pa1.into_analog(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut timer = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
timer.start(10.Hz()).unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// NOTE: read() returns a Result for one-shot conversions.
|
||||||
|
let raw: u16 = adc.read(&mut analog_pin).unwrap();
|
||||||
|
info!("adc raw={} level={}", raw, classify(raw));
|
||||||
|
block!(timer.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
22
tutorial/08-final-combined/README.md
Normal file
22
tutorial/08-final-combined/README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# 08 - Final Combined (4 min)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Blink + Button + ADC in einem Programm kombinieren.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
1. ADC steuert Blink-Intervall.
|
||||||
|
2. Button toggelt Betriebsmodus:
|
||||||
|
- `Mode::Blinking`
|
||||||
|
- `Mode::ForcedOff`
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
- `bash scripts/run-step.sh 08 task`
|
||||||
|
|
||||||
|
## Done when
|
||||||
|
|
||||||
|
1. LED-Verhalten ändert sich per Button.
|
||||||
|
2. ADC beeinflusst Blinkgeschwindigkeit im Blink-Modus.
|
||||||
|
3. Logs zeigen Mode, ADC und Delay.
|
||||||
12
tutorial/08-final-combined/solution/.cargo/config.toml
Normal file
12
tutorial/08-final-combined/solution/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/08-final-combined/solution/Cargo.toml
Normal file
22
tutorial/08-final-combined/solution/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step08_final_combined_solution"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/08-final-combined/solution/memory.x
Normal file
5
tutorial/08-final-combined/solution/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/08-final-combined/solution/rust-toolchain.toml
Normal file
4
tutorial/08-final-combined/solution/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
83
tutorial/08-final-combined/solution/src/main.rs
Normal file
83
tutorial/08-final-combined/solution/src/main.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{adc::Adc, pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum Mode {
|
||||||
|
Blinking,
|
||||||
|
ForcedOff,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interval_from_adc(raw: u16) -> u32 {
|
||||||
|
100 + (u32::from(raw) * 800 / 4095)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
|
let mut gpioc = dp.GPIOC.split(&mut rcc);
|
||||||
|
|
||||||
|
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
|
||||||
|
let button = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut adc = Adc::new(dp.ADC1, &mut rcc);
|
||||||
|
let mut analog_pin = gpioa.pa1.into_analog(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut tick = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
tick.start(100.Hz()).unwrap();
|
||||||
|
|
||||||
|
let mut mode = Mode::Blinking;
|
||||||
|
let mut last_pressed = false;
|
||||||
|
let mut led_on = false;
|
||||||
|
let mut elapsed_ms: u32 = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let pressed = button.is_low();
|
||||||
|
if pressed && !last_pressed {
|
||||||
|
mode = if mode == Mode::Blinking {
|
||||||
|
info!("mode -> ForcedOff");
|
||||||
|
Mode::ForcedOff
|
||||||
|
} else {
|
||||||
|
info!("mode -> Blinking");
|
||||||
|
Mode::Blinking
|
||||||
|
};
|
||||||
|
}
|
||||||
|
last_pressed = pressed;
|
||||||
|
|
||||||
|
let raw: u16 = adc.read(&mut analog_pin).unwrap();
|
||||||
|
let interval_ms = interval_from_adc(raw);
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
Mode::Blinking => {
|
||||||
|
elapsed_ms += 10;
|
||||||
|
if elapsed_ms >= interval_ms {
|
||||||
|
elapsed_ms = 0;
|
||||||
|
led_on = !led_on;
|
||||||
|
if led_on {
|
||||||
|
led.set_low();
|
||||||
|
} else {
|
||||||
|
led.set_high();
|
||||||
|
}
|
||||||
|
info!("mode=Blinking adc={} interval_ms={} led_on={}", raw, interval_ms, led_on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mode::ForcedOff => {
|
||||||
|
led_on = false;
|
||||||
|
led.set_high();
|
||||||
|
elapsed_ms = 0;
|
||||||
|
info!("mode=ForcedOff adc={} interval_ms={}", raw, interval_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block!(tick.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
tutorial/08-final-combined/task/.cargo/config.toml
Normal file
12
tutorial/08-final-combined/task/.cargo/config.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = "probe-rs run --chip STM32F103C8"
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7m-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
22
tutorial/08-final-combined/task/Cargo.toml
Normal file
22
tutorial/08-final-combined/task/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "step08_final_combined_task"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
defmt = "0.3.10"
|
||||||
|
defmt-rtt = "0.4.2"
|
||||||
|
panic-probe = { version = "0.3.2", features = ["print-defmt"] }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[dependencies.stm32f1xx-hal]
|
||||||
|
version = "0.11.0"
|
||||||
|
features = ["rt", "stm32f103", "medium"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
lto = true
|
||||||
|
opt-level = "z"
|
||||||
5
tutorial/08-final-combined/task/memory.x
Normal file
5
tutorial/08-final-combined/task/memory.x
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
4
tutorial/08-final-combined/task/rust-toolchain.toml
Normal file
4
tutorial/08-final-combined/task/rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = ["rustfmt"]
|
||||||
|
targets = ["thumbv7m-none-eabi"]
|
||||||
85
tutorial/08-final-combined/task/src/main.rs
Normal file
85
tutorial/08-final-combined/task/src/main.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use defmt::info;
|
||||||
|
use nb::block;
|
||||||
|
use stm32f1xx_hal::{adc::Adc, pac, prelude::*, timer::Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum Mode {
|
||||||
|
Blinking,
|
||||||
|
ForcedOff,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interval_from_adc(raw: u16) -> u32 {
|
||||||
|
// Map 0..4095 -> 100..900 ms
|
||||||
|
100 + (u32::from(raw) * 800 / 4095)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let mut rcc = dp.RCC.constrain();
|
||||||
|
let mut gpioa = dp.GPIOA.split(&mut rcc);
|
||||||
|
let mut gpioc = dp.GPIOC.split(&mut rcc);
|
||||||
|
|
||||||
|
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
|
||||||
|
let button = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);
|
||||||
|
|
||||||
|
let mut adc = Adc::new(dp.ADC1, &mut rcc);
|
||||||
|
let mut analog_pin = gpioa.pa1.into_analog(&mut gpioa.crl);
|
||||||
|
|
||||||
|
// Fast base tick: all app timing derives from this.
|
||||||
|
let mut tick = Timer::syst(cp.SYST, &rcc.clocks).counter_hz();
|
||||||
|
tick.start(100.Hz()).unwrap(); // 10 ms
|
||||||
|
|
||||||
|
let mut mode = Mode::Blinking;
|
||||||
|
let mut last_pressed = false;
|
||||||
|
let mut led_on = false;
|
||||||
|
let mut elapsed_ms: u32 = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let pressed = button.is_low();
|
||||||
|
if pressed && !last_pressed {
|
||||||
|
mode = if mode == Mode::Blinking {
|
||||||
|
info!("mode -> ForcedOff");
|
||||||
|
Mode::ForcedOff
|
||||||
|
} else {
|
||||||
|
info!("mode -> Blinking");
|
||||||
|
Mode::Blinking
|
||||||
|
};
|
||||||
|
}
|
||||||
|
last_pressed = pressed;
|
||||||
|
|
||||||
|
let raw: u16 = adc.read(&mut analog_pin).unwrap();
|
||||||
|
let interval_ms = interval_from_adc(raw);
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
Mode::Blinking => {
|
||||||
|
elapsed_ms += 10;
|
||||||
|
if elapsed_ms >= interval_ms {
|
||||||
|
elapsed_ms = 0;
|
||||||
|
led_on = !led_on;
|
||||||
|
if led_on {
|
||||||
|
led.set_low(); // active-low
|
||||||
|
} else {
|
||||||
|
led.set_high();
|
||||||
|
}
|
||||||
|
info!("mode=Blinking adc={} interval_ms={} led_on={}", raw, interval_ms, led_on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Mode::ForcedOff => {
|
||||||
|
led_on = false;
|
||||||
|
led.set_high();
|
||||||
|
elapsed_ms = 0;
|
||||||
|
info!("mode=ForcedOff adc={} interval_ms={}", raw, interval_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block!(tick.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
verify-probe.sh
Executable file
18
verify-probe.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "[verify-probe] connected probes"
|
||||||
|
probe-rs list || {
|
||||||
|
echo "probe-rs list failed"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "[verify-probe] known chip check"
|
||||||
|
if probe-rs chip list | grep -q 'STM32F103C8'; then
|
||||||
|
echo "chip id available: STM32F103C8"
|
||||||
|
else
|
||||||
|
echo "chip id STM32F103C8 not found in probe-rs chip list"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[verify-probe] done"
|
||||||
236
workshop.html
Normal file
236
workshop.html
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Rust on Bluepill Workshop</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5/dist/reset.css" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5/dist/reveal.css" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5/dist/theme/white.css" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.reveal h1, .reveal h2, .reveal h3 {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
.reveal pre code {
|
||||||
|
max-height: 420px;
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
.small {
|
||||||
|
font-size: 0.72em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="reveal">
|
||||||
|
<div class="slides">
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h1>Rust on Embedded</h1>
|
||||||
|
<h3>Didacta Workshop</h3>
|
||||||
|
<p>STM32F103C8 Bluepill + ST-Link + Rust</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Workshop Goal</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Understand core Rust basics</li>
|
||||||
|
<li>Build and flash no_std firmware</li>
|
||||||
|
<li>Blink LED, read button, read analog input</li>
|
||||||
|
<li>See output with probe-rs RTT logs</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Flow (60 min)</h2>
|
||||||
|
<ul>
|
||||||
|
<li>00 setup, 01-03 Rust basics</li>
|
||||||
|
<li>04 first embedded app</li>
|
||||||
|
<li>05 LED blink</li>
|
||||||
|
<li>06 button input</li>
|
||||||
|
<li>07 analog readout</li>
|
||||||
|
<li>08 final combined app</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>How We Work</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Edit only <code>task/</code> folders</li>
|
||||||
|
<li>Use <code>solution/</code> only when stuck</li>
|
||||||
|
<li>One step at a time, linear slides</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 00 Task: Setup Live</h2>
|
||||||
|
<p><strong>Next task:</strong> Prepare host + probe tools.</p>
|
||||||
|
<p><strong>Learn:</strong> Embedded Rust toolchain basics.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/setup-live.sh
|
||||||
|
bash scripts/verify-host.sh
|
||||||
|
bash scripts/verify-probe.sh</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 00 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>rustup</code>, <code>cargo</code>, <code>probe-rs</code> available</li>
|
||||||
|
<li>Target <code>thumbv7m-none-eabi</code> installed</li>
|
||||||
|
<li><code>probe-rs list</code> sees ST-Link</li>
|
||||||
|
<li><code>probe-rs chip list</code> contains <code>STM32F103C8</code></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 01 Task: Rust Hello</h2>
|
||||||
|
<p><strong>Next task:</strong> Edit the TODO values and run.</p>
|
||||||
|
<p><strong>Learn:</strong> <code>fn main</code>, variables, <code>println!</code>.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 01 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 01 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Program compiles and runs with <code>cargo run</code></li>
|
||||||
|
<li>Console output shows your name and workshop title</li>
|
||||||
|
<li>You can navigate the task/solution structure</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 02 Task: Types + Control Flow</h2>
|
||||||
|
<p><strong>Next task:</strong> Tune threshold logic in <code>classify_adc</code>.</p>
|
||||||
|
<p><strong>Learn:</strong> explicit types, <code>if</code>, <code>match</code>.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 02 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 02 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>ADC values map to low/medium/high</li>
|
||||||
|
<li>Button states map to pressed/released</li>
|
||||||
|
<li>Program output changes correctly with input values</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 03 Task: Ownership + Borrowing</h2>
|
||||||
|
<p><strong>Next task:</strong> Complete/update state mutation functions.</p>
|
||||||
|
<p><strong>Learn:</strong> <code>&T</code> vs <code>&mut T</code>, safe mutation design.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 03 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 03 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>State is read via immutable reference</li>
|
||||||
|
<li>State changes happen through mutable references</li>
|
||||||
|
<li>Compiler enforces safe access patterns</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 04 Task: Embedded no_std Hello</h2>
|
||||||
|
<p><strong>Next task:</strong> Flash first no_std firmware and watch RTT logs.</p>
|
||||||
|
<p><strong>Learn:</strong> <code>#![no_std]</code>, <code>#![no_main]</code>, <code>#[entry]</code>.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 04 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 04 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Firmware flashes to Bluepill</li>
|
||||||
|
<li>RTT shows boot + alive messages</li>
|
||||||
|
<li>You now have a working embedded Rust baseline</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 05 Task: LED Blink</h2>
|
||||||
|
<p><strong>Next task:</strong> Toggle PC13 in a timed loop.</p>
|
||||||
|
<p><strong>Learn:</strong> HAL GPIO output + active-low LED behavior.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 05 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 05 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>PC13 low = LED on, PC13 high = LED off</li>
|
||||||
|
<li>Timer loop controls blink frequency</li>
|
||||||
|
<li>RTT logs match LED state changes</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 06 Task: Button Input</h2>
|
||||||
|
<p><strong>Next task:</strong> Read PA0 with pull-up and detect transitions.</p>
|
||||||
|
<p><strong>Learn:</strong> digital input polling + simple edge detection.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 06 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 06 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Pressed/released events are logged correctly</li>
|
||||||
|
<li>No repeated spam while button is held</li>
|
||||||
|
<li>Wiring pattern: PA0 -> button -> GND</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 07 Task: Analog Readout</h2>
|
||||||
|
<p><strong>Next task:</strong> Read ADC on PA1 and print values.</p>
|
||||||
|
<p><strong>Learn:</strong> one-shot ADC + value classification.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 07 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 07 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>ADC raw value changes with analog input</li>
|
||||||
|
<li>Classification low/medium/high works</li>
|
||||||
|
<li>Periodic readout appears in RTT console</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 08 Task: Final Combined</h2>
|
||||||
|
<p><strong>Next task:</strong> Integrate LED + button + ADC behavior.</p>
|
||||||
|
<p><strong>Learn:</strong> small embedded state machine.</p>
|
||||||
|
<pre><code class="language-bash">bash scripts/run-step.sh 08 task</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Step 08 Solution</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Button toggles mode: Blinking / ForcedOff</li>
|
||||||
|
<li>ADC value controls blink interval in Blinking mode</li>
|
||||||
|
<li>Integrated firmware runs stable with logs</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Workshop Wrap-up</h2>
|
||||||
|
<ul>
|
||||||
|
<li>You moved from Rust basics to real MCU firmware</li>
|
||||||
|
<li>You used no_std, HAL GPIO, button input, and ADC</li>
|
||||||
|
<li>Next: extend with UART, interrupts, or Embassy async</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5/dist/reveal.js"></script>
|
||||||
|
<script>
|
||||||
|
Reveal.initialize({
|
||||||
|
hash: true,
|
||||||
|
controls: true,
|
||||||
|
progress: true,
|
||||||
|
center: true,
|
||||||
|
transition: 'slide'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user