Hardware
Mittlerweile sollten Sie mit den Werkzeugen und dem Entwicklungsprozess einigermaßen vertraut sein. In diesem Abschnitt wechseln wir zur echten Hardware; der Prozess bleibt im Großen und Ganzen gleich. Tauchen wir ein.
Kennen Sie Ihre Hardware
Bevor wir beginnen, müssen Sie einige Eigenschaften des Zielgeräts identifizieren da diese zur Konfiguration des Projekts verwendet werden:
- Der ARM-Kern, z.B. Cortex-M3.
- Enthält der ARM-Kern eine FPU? Cortex-M4F und Cortex-M7F-Kerne tun dies.
- Wie viel Flash-Speicher und RAM hat das Zielgerät? z.B. 256 KiB von Flash und 32 KiB RAM.
- Wo werden Flash-Speicher und RAM im Adressraum abgebildet? zB RAM ist befindet sich üblicherweise an der Adresse
0x2000_0000.
Diese Informationen finden Sie im Datenblatt oder im Referenzhandbuch Ihres/Ihrer Microcontrollers/Microcontrollerplatine.
In diesem Abschnitt verwenden wir unsere Referenzhardware, die STM32F3DISCOVERY. Diese Platine enthält einen Mikrocontroller STM32F303VCT6. Dieser Mikrocontroller verfügt über:
- Ein Cortex-M4F-Kern, der eine einzelne Präzisions-FPU enthält
- 256 KiB Flash befinden sich an der Adresse 0x0800_0000.
- 440 KiB RAM an der Adresse 0x2000_0000. (Es gibt noch eine weitere RAM-Region, aber der Einfachheit halber ignorieren wir das).
Konfigurieren
Wir beginnen bei Null mit einer neuen Vorlageninstanz. Zur Auffrischung siehe vorheriger Abschnitt zu QEMU, wie man das ohne cargo-generate macht.
$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
Project Name: app
Creating project called `app`...
Done! New project created /tmp/app
$ cd appSchritt Nummer eins besteht darin, ein Standardkompilierungsziel in .cargo/config.toml festzulegen.
tail -n5 .cargo/config.toml# Waehlen Sie EINES von diesen Kompilierungszielen aus
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
Wir verwenden thumbv7em-none-eabihf, da es den Cortex-M4F-Kern abdeckt.
HINWEIS: Wie Sie sich vielleicht aus dem vorherigen Kapitel erinnern, müssen wir alle Ziele installieren, dies ist ein neues Ziel. Vergessen Sie also nicht, die Installationsprozess rustup target add thumbv7em-none-eabihf für dieses Ziel auszuführen.Der zweite Schritt besteht darin, die Speicherbereichsinformationen in die Datei memory.x einzugeben
$ cat memory.x
/* Linker script for the STM32F303VCT6 */
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 40K
}NOTE: Wenn Sie aus irgendeinem Grund die Dateimemory.xgeändert haben, nachdem Sie den ersten Build für ein bestimmtes Build-Ziel erstellt haben, dann führen Siecargo cleanvorcargo buildaus, dacargo buildkeine Änderungen vonmemory.xverfolgt.
Wir beginnen noch einmal mit dem Hallo-Beispiel, aber zuerst müssen wir eine kleine Änderung vornehmen.
Stellen Sie in examples/hello.rssicher, daß debug::exit() auskommentiert oder entfernt wurde. Es wird nur zum Ausführen in QEMU verwendet.
#[entry]
fn main() -> ! {
hprintln!("Hello, world!").unwrap();
// exit QEMU
// HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den
// OpenOCD-Zustand beschaedigen.
// debug::exit(debug::EXIT_SUCCESS);
loop {}
}
Sie können nun Programme mittels cargo build cross-kompilieren und die Binärdateien mit cargo-binutils untersuchen, genau wie zuvor. Das cortex-m-rt-Crate übernimmt die gesamte für den Betrieb des Chips erforderliche „Magie“, da erfreulicherweise so gut wie alle Cortex-M-CPUs auf die gleiche Weise booten.
cargo build --example helloFehlerbehebung (Debugging)
Das Debugging gestaltet sich etwas anders. Tatsächlich können sich bereits die ersten Schritte je nach Zielgerät unterscheiden. In diesem Abschnitt zeigen wir die Schritte, die zum Debuggen eines auf dem STM32F3DISCOVERY laufenden Programms erforderlich sind. Dies soll als Referenz dienen; für gerätespezifische Informationen zum Debugging konsultieren Sie bitte das Debugonomicon.
Wie zuvor führen wir Remote-Debugging durch, wobei der Client ein GDB-Prozess ist. Diesmal ist der Server jedoch OpenOCD.
Verbinden Sie, wie im Abschnitt “Die Installation überprüfen” beschrieben, das Discovery Board mit Ihrem Laptop/PC und prüfen Sie, ob der ST-LINK-Header ausgefüllt ist.
Führen Sie in einem Terminal openocd aus, um eine Verbindung zum ST-LINK auf dem Discovery-Board herzustellen. Führen Sie diesen Befehl im Stammverzeichnis der Vorlage aus; openocd greift dabei auf die Datei openocd.cfg zu, in der festgelegt ist, welche Schnittstellen- und Zieldateien verwendet werden sollen.
cat openocd.cfg# Beispiel-OpenOCD-Konfiguration für das STM32F3DISCOVERY-Entwicklungsboard
# Je nach der vorliegenden Hardware-Revision muessen Sie eine dieser
# Schnittstellen auswaehlen. Es sollte jeweils nur eine Schnittstelle
# auskommentiert sein.
# Revision C (neuere Revision)
source [find interface/stlink.cfg]
# Revision A and B (aeltere Revisionen)
# source [find interface/stlink-v2.cfg]
source [find target/stm32f3x.cfg]
HINWEIS Falls Sie im Abschnitt “Die Installation überprüfen” feststellen, dass Sie eine ältere Revision des Discovery-Boards besitzen, sollten Sie an dieser Stelle die Dateiopenocd.cfgso anpassen, dassinterface/stlink-v2.cfgverwendet wird.
$ openocd
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 2.913879
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpointsStarten Sie in einem weiteren Terminal GDB, ebenfalls vom Stammverzeichnis des Templates aus.
gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/helloHINWEIS: Wie bereits erwähnt, benötigen Sie möglicherweise anstelle von gdb-multiarch eine andere Version von gdb, je nachdem, welche Version Sie im Installationskapitel installiert haben. Dies könnte beispielsweise arm-none-eabi-gdb oder einfach gdb sein.
Verbinden Sie nun GDB mit OpenOCD, das auf eine TCP-Verbindung an Port 3333 wartet.
(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()Fahren Sie nun damit fort, das Programm mithilfe des Befehls load auf den Mikrocontroller zu flashen (zu laden).
(gdb) load
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x1518 lma 0x8000400
Loading section .rodata, size 0x414 lma 0x8001918
Start address 0x08000400, load size 7468
Transfer rate: 13 KB/sec, 2489 bytes/write.Das Programm ist nun geladen. Da dieses Programm Semihosting verwendet, müssen wir OpenOCD anweisen, Semihosting zu aktivieren, bevor wir einen entsprechenden Aufruf tätigen. Befehle können mithilfe des Befehls monitor an OpenOCD gesendet werden.
(gdb) monitor arm semihosting enable
semihosting is enabledSie können sich alle OpenOCD-Befehle anzeigen lassen, indem Sie den Befehl monitor help aufrufen.Wie zuvor können wir mithilfe eines Breakpoints und des Befehls continue direkt zu main springen.
(gdb) break main
Breakpoint 1 at 0x8000490: file examples/hello.rs, line 11.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) continue
Continuing.
Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11
11 #[entry]HINWEIS Falls GDB das Terminal blockiert, anstatt am Haltepunkt (Breakpoint) anzuhalten, nachdem Sie den oben genannten Befehlcontinueeingegeben haben, sollten Sie überprüfen, ob die Angaben zum Speicherbereich in der Dateimemory.xfür Ihr Gerät korrekt konfiguriert sind (sowohl die Startadressen als auch die Längen).
Springen Sie mit step in die main-Funktion.
(gdb) step
halted: PC: 0x08000496
hello::__cortex_m_rt_main () at examples/hello.rs:13
13 hprintln!("Hello, world!").unwrap();Nachdem Sie das Programm mit next weitergeführt haben, sollten Sie unter anderem „Hello, world!“ auf der OpenOCD-Konsole ausgegeben sehen.
$ openocd
(..)
Info : halted: PC: 0x08000502
Hello, world!
Info : halted: PC: 0x080004ac
Info : halted: PC: 0x080004ae
Info : halted: PC: 0x080004b0
Info : halted: PC: 0x080004b4
Info : halted: PC: 0x080004b8
Info : halted: PC: 0x080004bcDie Meldung wird nur einmal angezeigt, kurz bevor das Programm in die in Zeile 19 definierte Endlosschleife eintritt: loop {}
Sie können GDB nun mit dem Befehl quit beenden.
(gdb) quit
A debugging session is active.
Inferior 1 [Remote target] will be detached.
Quit anyway? (y or n)Das Debugging erfordert nun einige zusätzliche Schritte; daher haben wir all diese Schritte in einem einzigen GDB-Skript namens openocd.gdb zusammengefasst. Die Datei wurde während des Schritts cargo generate erstellt und sollte ohne Änderungen funktionieren. Werfen wir einen Blick darauf:
cat openocd.gdbtarget extended-remote :3333
# print demangled symbols
set print asm-demangle on
# detect unhandled exceptions, hard faults and panics
break DefaultHandler
break HardFault
break rust_begin_unwind
monitor arm semihosting enable
load
# start the process but immediately halt the processor
stepiWenn Sie nun den Befehl <gdb> -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello ausführen, verbindet sich GDB sofort mit OpenOCD, aktiviert Semihosting, lädt das Programm und startet den Prozess.
Alternativ können Sie <gdb> -x openocd.gdb als benutzerdefinierten Runner einrichten, sodass cargo run das Programm baut und eine GDB-Sitzung startet. Dieser Runner ist bereits in der Datei .cargo/config.toml enthalten, jedoch auskommentiert.
head -n10 .cargo/config.toml[target.thumbv7m-none-eabi]
# Dies auskommentieren, damit `cargo run` Programme auf QEMU ausfuehrt.
# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# Heben Sie die Auskommentierung einer dieser drei Optionen auf, damit
# `cargo run` eine GDB-Sitzung startet.
# Welche Option Sie waehlen sollten, haengt von Ihrem System ab.
runner = "arm-none-eabi-gdb -x openocd.gdb"
# runner = "gdb-multiarch -x openocd.gdb"
# runner = "gdb -x openocd.gdb"
$ cargo run --example hello
(..)
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x1e70 lma 0x8000400
Loading section .rodata, size 0x61c lma 0x8002270
Start address 0x800144e, load size 10380
Transfer rate: 17 KB/sec, 3460 bytes/write.
(gdb)