start tutorial

This commit is contained in:
2026-03-08 19:41:38 +01:00
commit a48ba2963d
81 changed files with 1738 additions and 0 deletions

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

View 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

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

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

View File

@@ -0,0 +1,4 @@
[package]
name = "step01_rust_hello_solution"
version = "0.1.0"
edition = "2021"

View 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.");
}

View File

@@ -0,0 +1,4 @@
[package]
name = "step01_rust_hello_task"
version = "0.1.0"
edition = "2021"

View 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.");
}

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

View File

@@ -0,0 +1,4 @@
[package]
name = "step02_rust_types_control_solution"
version = "0.1.0"
edition = "2021"

View 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));
}
}

View File

@@ -0,0 +1,4 @@
[package]
name = "step02_rust_types_control_task"
version = "0.1.0"
edition = "2021"

View 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));
}
}

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

View File

@@ -0,0 +1,4 @@
[package]
name = "step03_rust_ownership_borrow_solution"
version = "0.1.0"
edition = "2021"

View 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);
}

View File

@@ -0,0 +1,4 @@
[package]
name = "step03_rust_ownership_borrow_task"
version = "0.1.0"
edition = "2021"

View 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);
}

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

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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");
}
}

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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");
}
}

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

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}

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

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}

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

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}

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

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}

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

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

View File

@@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
components = ["rustfmt"]
targets = ["thumbv7m-none-eabi"]

View 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();
}
}