<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://satyria.de/arm/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Satyria</id>
	<title>C und Assembler mit Raspberry - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://satyria.de/arm/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Satyria"/>
	<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Spezial:Beitr%C3%A4ge/Satyria"/>
	<updated>2026-08-25T18:58:36Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1342</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1342"/>
		<updated>2026-07-27T12:30:57Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die Seiten werden zur Zeit überarbeitet und sind zukünftig über https://www.satyria.de/raspi erreichbar. Dieser Schritt war nötig, um folgende Kapitel (USB) funktionsfähig zu machen. Hierzu sind einige Teile des Sourcecodes anzupassen. Dennoch sind die Beispiele hier voll funktionsfähig.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;strong&amp;gt;C und ARM-Assemblerprogrammierung mit dem Raspberry&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[Datei:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © by 2020-2025&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vorwort ===&lt;br /&gt;
&lt;br /&gt;
Herzlich willkommen auf unserer Seite zur Bare-Metal-Programmierung für den Raspberry Pi 4 und Pi 5! Diese Webseite richtet sich sowohl an Anfänger als auch an fortgeschrittene Programmierer, die tief in die Welt der hardwarenahen Programmierung eintauchen möchten.&lt;br /&gt;
&lt;br /&gt;
Ursprünglich sollte diese Seite sich ausschließlich mit Assembler beschäftigen, wurde jedoch im Laufe der Zeit um C erweitert. Daher finden Sie hier zunächst grundlegende Informationen zu Assembler. Wenn Ihr Interesse in der C-Programmierung liegt und Sie mit Windows arbeiten, empfehlen wir Ihnen mit dem Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen (Konsole)]]&#039;&#039;&#039; zu beginnen. Für Linux-Nutzer ist das Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen unter Linux]]&#039;&#039;&#039; der richtige Startpunkt.&lt;br /&gt;
&lt;br /&gt;
Im Kapitel &#039;&#039;&#039;Raspberry Pi 4 Assembler 32-Bit&#039;&#039;&#039; beschreiben wir die Assembler-Programmierung für den Raspberry Pi 4. Dieses Kapitel hat zwar bereits einige Jahre auf dem Buckel, bleibt aber weiterhin relevant und wertvoll. Hier finden Sie detaillierte Erklärungen der ARM-32-Bit-Befehle mit anschaulichen Beschreibungen und Beispielen, was besonders Anfängern den Einstieg erleichtern soll.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 4 C (64-Bit)&#039;&#039;&#039; entstand im Laufe unserer Arbeit am Raspberry Pi 5 und bietet eine umfassende Beschreibung der 64-Bit-C-Programmierung für den Raspberry Pi 4. Dies wird auch später die USB-Programmierung einschließen, eine Funktion, die wir später auf den Raspberry Pi 5 übertragen werden. So haben alle Projekte einen gemeinsamen Ausgangspunkt.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 5&#039;&#039;&#039; ist der ideale Startpunkt für die Programmierung des Raspberry Pi 5 in Assembler und C. Es führt Sie von der Installation des Hostsystems (Windows/Linux) bis zur abschließenden Programmierung in C und Assembler.&lt;br /&gt;
&lt;br /&gt;
Im Anhang finden Sie viele nützliche Informationen, wie eine Beschreibung der &#039;&#039;&#039;GNU Compiler Collection&#039;&#039;&#039;, eine Auflistung des &#039;&#039;&#039;ARM-Befehlssatzes&#039;&#039;&#039; (im Aufbau), verschiedene &#039;&#039;&#039;Raspberry Pi Modelle&#039;&#039;&#039; und einige hilfreiche &#039;&#039;&#039;Links&#039;&#039;&#039; zu anderen Projekten. Ein aktuelles Projekt zum Thema &#039;&#039;&#039;Debugging&#039;&#039;&#039; ist ebenfalls dort zu finden.&lt;br /&gt;
&lt;br /&gt;
Wir hoffen, dass Sie auf unserer Seite viel Freude haben und sich gut zurechtfinden! Bei Fragen oder Kommentaren schicken Sie uns gerne eine E-Mail an assem@satyria.de. Wir freuen uns über Ihre Rückmeldungen und Anregungen.&lt;br /&gt;
&lt;br /&gt;
Viel Spaß beim Programmieren!&lt;br /&gt;
&lt;br /&gt;
== Sponsor ==&lt;br /&gt;
Wenn ihr uns etwas unterstützen möchtet, schaut einfach mal hier vorbei:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Logo hexenlaedle.png|frameless|200x200px|link=https://www.hexenlaedle.de]]&lt;br /&gt;
&lt;br /&gt;
== Inhalt ==&lt;br /&gt;
*[[Vorwort]]&lt;br /&gt;
*[[Grundlegendes zu Assembler]]&lt;br /&gt;
**[[Grundlegendes zu Assembler#Der ARM-Assembler|Der ARM-Assembler]]&lt;br /&gt;
*[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
*[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5]]&lt;br /&gt;
**[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5#Warum C und Assembler?|Warum C und Assembler?]]&lt;br /&gt;
*[[Programm auf dem Raspberry Pi 5 ausführen]]&lt;br /&gt;
*[[Arbeiten mit Make und Linker-Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Unser erstes Programm (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung]]&lt;br /&gt;
*[[Grafik (PI5)]]&lt;br /&gt;
*[[Chars (PI5)]]&lt;br /&gt;
*Das Terminal (PI5)&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI5)]]&lt;br /&gt;
*[[Grafik in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)]]&lt;br /&gt;
*[[Das Terminal in C (PI5)]]&lt;br /&gt;
*[[printf in BareMetal (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Programmierumgebung erstellen (32-Bit)]]&lt;br /&gt;
*[[Systemprogrammierung / Bare Metal]]&lt;br /&gt;
*[[Das erste Programm]]&lt;br /&gt;
*[[General Purpose I/O]]&lt;br /&gt;
*[[System Timer]]&lt;br /&gt;
*[[UART]]&lt;br /&gt;
*[[Die Anzeige]]&lt;br /&gt;
*[[Zeichenfunktionen]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 C (64-Bit) ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI4)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI4)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI4)]]&lt;br /&gt;
*[[Grafik in C (PI4)]]&lt;br /&gt;
*[[Chars in C (PI4)]]&lt;br /&gt;
*[[Das Terminal in C (PI4)]]&lt;br /&gt;
*[[Printf in BareMetal (PI4)]]&lt;br /&gt;
*[[Systeminformationen (PI4)]] (noch kein Inhalt)&lt;br /&gt;
*[[Interrupts (PI4)]]&lt;br /&gt;
**[[Beispiel Timer-Interrupt (PI4)]]&lt;br /&gt;
*[[Interrupt Teil 2 (PI4)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--*[[USB (PI4)]]--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ARM64 Assembler ==&lt;br /&gt;
*[[Programmieren mit ARM64 Assembler]]&lt;br /&gt;
&lt;br /&gt;
== Zusätzliches Material ==&lt;br /&gt;
*[[Links der verwendeten Software]]&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
*[[GNU Compiler Collection]]&lt;br /&gt;
*[[ARM-Befehlssatz]]&lt;br /&gt;
**[[Grundlegende Befehle]]&lt;br /&gt;
&amp;lt;!--**[[ARMv8-Übersicht]]--&amp;gt;&lt;br /&gt;
*[[Raspberry PI Modelle]]&lt;br /&gt;
**[[Basisadressen der Modelle]]&lt;br /&gt;
*[[LINKS]]&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging (JTAG, RPI 4)]]&lt;br /&gt;
&amp;lt;!--*[[Bare-Metal Debugging]]--&amp;gt;&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4]]&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5]]&lt;br /&gt;
----&lt;br /&gt;
*[[Die wichtigsten Linux-Terminal-Befehle]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[English]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Grafik_in_C_(PI4)&amp;diff=1341</id>
		<title>Grafik in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Grafik_in_C_(PI4)&amp;diff=1341"/>
		<updated>2026-07-23T11:37:58Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Anwendung der DrawPixel-Funktion */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die bisherigen Programme auf dem Raspberry Pi waren vergleichsweise einfach und ermöglichten lediglich die Steuerung einer LED. Der Raspberry Pi kann jedoch weitaus mehr, insbesondere im Bereich der Grafik, da er über einen HDMI-Ausgang verfügt, der für fortgeschrittene Anwendungen genutzt werden kann. In diesem Abschnitt wollen wir den HDMI-Ausgang des Raspberry Pi verwenden, um grafische Ergebnisse direkt anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
Zu Beginn schließen wir einen Monitor an den Raspberry Pi an, um die Ergebnisse direkt auf dem Bildschirm sehen zu können. Da wir Bare Metal programmieren, müssen wir sämtliche Routinen selbst schreiben, da der Raspberry Pi keine vorinstallierten Weisungen hat, wie er Bilder auf dem Monitor anzeigt.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi 4 besitzt zwei HDMI-Ausgänge, die jeweils Bildschirme mit bis zu UHD-Auflösung (4096 x 2160 Pixel) bei 60 Hz unterstützen. Bei Verwendung beider HDMI-Ports beträgt die Bildwiederholrate maximal 30 Hz. Ältere Raspberry Pi-Modelle unterstützen Bildschirme bis zu einer Auflösung von 1920 x 1200 Pixel bei 60 Hz.&lt;br /&gt;
&lt;br /&gt;
Interessanterweise ist es die GPU des Raspberry Pi, die den Startvorgang initiiert. Sie lädt die Firmware in den Speicher und übergibt danach die Kontrolle an die CPU. Dies ist während des Startvorgangs deutlich sichtbar, wenn ein Bildschirm angeschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Um mit der Grafikprogrammierung zu beginnen, müssen wir zunächst verstehen, wie ein Bild auf dem Bildschirm dargestellt wird. Ein Bildschirm besteht aus einzelnen Punkten, den Pixeln, die jeweils eine eigene Farbe haben. Normalerweise wird der Bildschirm in einem Koordinatensystem (x, y) dargestellt. Der erste Pixel oben links hat die Koordinaten (0,0). Der nachfolgende Pixel rechts davon hat die Koordinate (1,0) und so weiter. Nach unten wird die y-Koordinate jeweils um eins erhöht: (0,1), (0,2) usw.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten1.png|rand]]&lt;br /&gt;
&lt;br /&gt;
== Auflösungen ==&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt viele verschiedene Auflösungen. Eine Übersicht über die gängigsten Auflösungen findest du beispielsweise unter https://de.wikipedia.org/wiki/Bildaufl%C3%B6sung#Computer. Hier ein paar Auflösungen, die verwendet werden könnten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! Auflösung&lt;br /&gt;
|-&lt;br /&gt;
| VGA || 640 x 480&lt;br /&gt;
|-&lt;br /&gt;
| XGA || 1024 x 768&lt;br /&gt;
|-&lt;br /&gt;
| HD || 1920 x 1080&lt;br /&gt;
|-&lt;br /&gt;
| UHD (only Pi 4 or Pi 5) || 4096 x 2160&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Auflösung allein reicht jedoch nicht aus, um komplexe, farbige Bilder darzustellen. Da Computersysteme nur binäre Zahlen kennen, also nur &amp;quot;1&amp;quot; oder &amp;quot;0&amp;quot;, könnte man jedes Pixel lediglich ein- oder ausschalten, was nur ein Schwarz-Weiß-Bild erzeugen würde. Um die Vielzahl der Farben, die wir auf Bildschirmen sehen, darzustellen, werden zusätzliche Standards definiert.&lt;br /&gt;
&lt;br /&gt;
Wird die Anzeige mehrmals hintereinander vertieft, entsteht eine größere Farbtiefe. Wenn ein Bild beispielsweise zwei Bitmaps verwendet, stehen für jeden Punkt zwei Bits zur Verfügung. Das ergibt vier Zustände bzw. vier Farben. Je mehr Bitmaps verwendet werden, desto mehr Farben können angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
== Farbtiefe ==&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt vier verschiedene Farbtiefen: 8-Bit, 16-Bit, 24-Bit und 32-Bit. In der Praxis wird jedoch von der Verwendung der 8-Bit und 24-Bit Farbtiefe abgeraten, da sie zu Darstellungsfehlern führen können. Siehe hierzu auch die Dokumentation unter https://www.raspberrypi.org/documentation/configuration/config-txt/video.md im Abschnitt framebuffer_depth. Um es einfach zu halten, konzentrieren wir uns auf die 32-Bit Farbtiefe.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Speicheraufwand für Bilder&lt;br /&gt;
Um den Speicheraufwand zu veranschaulichen, betrachten wir ein Beispiel. Ein monochromes Bild mit einer Auflösung von 20 x 20 Pixel benötigt folgenden Speicherbedarf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Speicherbedarf (Byte) = (x * y) / 8         (20*20)/8 = 50 Bytes  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten2.png|rand|400x400px]]&lt;br /&gt;
&lt;br /&gt;
Für ein Bild mit 4 Bitmaps beträgt der Speicherbedarf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Speicherbedarf (Byte) = ((x * y) * tiefe) / 8      ((20*20)*4)/8 = 200 Bytes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten3.png|rand|500x500px]]&lt;br /&gt;
&lt;br /&gt;
Nun ein Beispiel mit einer HD-Auflösung und RGBA32-Farbtiefe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Speicherbedarf (Bytes) = ((1920 * 1080) * 32) / 8 = 8294400 Bytes&lt;br /&gt;
Das entspricht etwa 8,3 Megabytes.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abbildung der Bitmaps im Speicher ==&lt;br /&gt;
&lt;br /&gt;
Bitmaps werden nicht als eine Reihe von Einzelbildern (Bitmaps) im Speicher abgelegt, sondern die Grafikdaten sind wie in einem dreidimensionalen Raum organisiert. Betrachten wir ein Beispiel mit einer Auflösung von 20 x 20 Pixel und einer Farbtiefe von 16 Bit:&lt;br /&gt;
&lt;br /&gt;
Jeder Bildschirmpunkt wird bei einer Farbtiefe von 16 Bit durch 16 aufeinanderfolgende Bits dargestellt. Der erste Punkt verwendet die Bits 0-15, der nächste die Bits 16-31, und so weiter. Das müssen wir bei den Berechnungen für jeden einzelnen Punkt berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten4.png|rand|600x600px]]&lt;br /&gt;
&lt;br /&gt;
== Der Postbote und TAGs ==&lt;br /&gt;
Wie bereits beschrieben, wird die Grafik nicht von der CPU, sondern von der GPU (Graphics Processing Unit) dargestellt. Damit die GPU weiß, was sie tun soll, benötigt sie bestimmte Informationen zur Darstellung, etwa wie der Bildschirm aussehen soll. Eine direkte Verbindung zwischen CPU und GPU existiert jedoch nicht, weshalb die Kommunikation über einen Vermittler erfolgt, der hier als &amp;quot;Postbote&amp;quot; bezeichnet wird. Die CPU sendet eine Nachricht an den Postboten, und dieser leitet die Nachricht an die GPU weiter. Ebenso kann die GPU Nachrichten an die CPU schicken.&lt;br /&gt;
&lt;br /&gt;
Der Postbote hat die Basisadresse 0xB880 und verwendet &amp;quot;TAGs&amp;quot; für die Informationsübermittlung. Die Dokumentation zu diesen TAGs ist leider spärlich, doch es gibt eine Gemeinschaft, die Informationen gesammelt und bereitgestellt hat. Weitere Informationen findest du hier:&lt;br /&gt;
&lt;br /&gt;
https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface&lt;br /&gt;
&lt;br /&gt;
Diese Informationen habe ich in dem zugehörigen Sourcecode zusammengefasst und in der base.h Datei eingefügt.&lt;br /&gt;
&lt;br /&gt;
Zunächst definieren wir in der base.h die neuen Register, die dafür verantwortlich sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// Mailbox&lt;br /&gt;
//&lt;br /&gt;
#define MAILBOX_BASE RPI_BASE + 0xB880 &lt;br /&gt;
#define MAILBOX0_READ       MAILBOX_BASE + 0x00&lt;br /&gt;
#define MAILBOX0_STATUS     MAILBOX_BASE + 0x18&lt;br /&gt;
#define MAILBOX_STATUS_EMPTY    0x40000000&lt;br /&gt;
#define MAILBOX1_WRITE      MAILBOX_BASE + 0x20&lt;br /&gt;
#define MAILBOX1_STATUS     MAILBOX_BASE + 0x38&lt;br /&gt;
#define MAILBOX_STATUS_FULL 0x80000000&lt;br /&gt;
&lt;br /&gt;
#define MAIL_WRITE MAILBOX_BASE + 0x20&lt;br /&gt;
#define MAIL_TAG_WRITE MAIL_WRITE + 0x8&lt;br /&gt;
&lt;br /&gt;
#define MAILBOX_CHANNEL_PM  0       // power management&lt;br /&gt;
#define MAILBOX_CHANNEL_FB  1       // frame buffer&lt;br /&gt;
#define BCM_MAILBOX_PROP_OUT    8       // property tags (ARM to VC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Kommunikation verwenden wir &amp;quot;TAGs&amp;quot;. Diese TAGs sind in einer bestimmten Struktur organisiert, die von der GPU verstanden wird.&lt;br /&gt;
&lt;br /&gt;
=== Aufbau der TAG-Struktur ===&lt;br /&gt;
TAG-Listen haben eine festgelegte Struktur, die die GPU versteht. Eine TAG-Struktur beginnt immer mit einer 32-Bit Zahl, die die Länge der gesamten Struktur angibt. Es folgt ein 32-Bit Puffer, in den eine Antwort geschrieben werden kann. Danach kommen die einzelnen TAGs, die wiederum eine Grundstruktur aufweisen: Der Name des TAGs, die Puffergröße in Bytes, ein Antwortpuffer und die Werte des TAGs. Die Struktur endet mit einem NULL-TAG.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Mailbox Länge&lt;br /&gt;
Mailbox Puffer&lt;br /&gt;
   TAG1 Name&lt;br /&gt;
   TAG1 Länge&lt;br /&gt;
   TAG1 Puffer&lt;br /&gt;
      TAG1 Wert1&lt;br /&gt;
      TAG1 Wert2&lt;br /&gt;
      ...&lt;br /&gt;
   TAG2 Name&lt;br /&gt;
   ...&lt;br /&gt;
   TAGx NULL (End TAG)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jeder TAG muss auf 32-Bit ausgerichtet sein.&lt;br /&gt;
&lt;br /&gt;
== Aufbau unserer Struktur ==&lt;br /&gt;
Wir erstellen unsere TAG-Struktur und richten sie auf 16 Byte aus, da nur die oberen 28 Bits der Adresse an die Mailbox übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
u32 pScreen[36] ALIGN(16) = {&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Struktur beginnt mit der Angabe der Länge und einem Puffer für mögliche Antworten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
u32 pScreen[36] ALIGN(16) = {&lt;br /&gt;
    34*4, //34 Einträge * 4 Bytes&lt;br /&gt;
    0,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt fügen wir die Daten hinzu, beginnend mit der Auflösung des Bildschirms. Der TAG Set_physical_display hat folgende Struktur:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_physical_display)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Breite des Bildschirms in Pixel&lt;br /&gt;
Höhe des Bildschirms in Pixel&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_physical_display, //Tag Identifier&lt;br /&gt;
    0x8,0x8,              //Buffer size in bytes&lt;br /&gt;
    SCREEN_X,             //Width in pixels&lt;br /&gt;
    SCREEN_Y,             //Height in pixels&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variablen SCREEN_X und SCREEN_Y definieren wir in der config.h Datei, hier für eine Auflösung von 1920 x 1080 Pixel.&lt;br /&gt;
&lt;br /&gt;
Als nächstes definieren wir den Set_virtual_buffer TAG:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_virtual_buffer)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Breite des Bildschirms in Pixel&lt;br /&gt;
Höhe des Bildschirms in Pixel&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_virtual_buffer,&lt;br /&gt;
    0x8,0x8,&lt;br /&gt;
    SCREEN_X,&lt;br /&gt;
    SCREEN_Y,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Set_depth TAG definiert die Farbtiefe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_depth)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Tiefe der Bitplanes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
  Set_depth,            //Tag Identifier&lt;br /&gt;
  0x4,                   //Buffer size in bytes&lt;br /&gt;
  0x4,                  //Response buffer&lt;br /&gt;
  BITS_PER_PIXEL,       //depth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verwenden wir eine Variable BITS_PER_PIXEL, die wir in der config.h Datei auf 32 Bit definieren.&lt;br /&gt;
&lt;br /&gt;
Der Set_virtual_offset TAG erzeugt Offset-Werte:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_virtual_offset)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Offset x&lt;br /&gt;
Offset y&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_virtual_offset,&lt;br /&gt;
    0x8,0x8,&lt;br /&gt;
    0x0,0x0,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Set_palette TAG definiert die Farbpalette:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_palette)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
erster eingestellter Palettenindex&lt;br /&gt;
Anzahl der einzustellenden Paletteneinträge&lt;br /&gt;
   RGBA Palettenwerte&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_palette,&lt;br /&gt;
    0x10,0x10,&lt;br /&gt;
    0x0,&lt;br /&gt;
    0x2,&lt;br /&gt;
    0x00000000,&lt;br /&gt;
    0xffffffff,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da wir hauptsächlich 16-Bit oder 32-Bit Farbtiefen verwenden, ist der Eintrag Set_palette weniger wichtig, muss aber dennoch definiert werden.&lt;br /&gt;
&lt;br /&gt;
Der Allocate_buffer TAG sorgt dafür, dass die GPU Speicher reserviert und diesen anzeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Allocate_buffer)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Frame Buffer Basisadresse in Bytes&lt;br /&gt;
Bildpuffergröße in Bytes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Allocate_buffer,&lt;br /&gt;
    0x8,0x8,&lt;br /&gt;
    0x0,          //Bufferadresse (pScreen[31])&lt;br /&gt;
    0x0,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der TAG erzeugt einen Puffer, dessen Adresse in pScreen[31] abgelegt wird, sobald die GPU dies durchgeführt hat. Dies ist die Adresse, auf die wir später zugreifen können.&lt;br /&gt;
&lt;br /&gt;
Unsere TAG-Liste endet mit einem NULL-TAG:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  0x00000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Struktur beschreibt, was wir darstellen möchten. Die GPU interpretiert diese Werte und versucht, sie darzustellen. Wenn die GPU die Angaben nicht exakt umsetzen kann, erzeugt sie einen ähnlichen Zustand. Wir sollten die von der GPU zurückgemeldeten Werte überprüfen, um sicherzustellen, dass sie unseren Erwartungen entsprechen.&lt;br /&gt;
&lt;br /&gt;
=== Der Postbote ===&lt;br /&gt;
&lt;br /&gt;
Nun bringt der Postbote unsere TAG-Liste zur GPU, um den Bildschirm zu erzeugen. Wir verwenden zunächst eine einfache Methode und erweitern später die Funktionen.&lt;br /&gt;
&lt;br /&gt;
Zuerst geben wir dem Postboten den Auftrag, die Informationen an die GPU zu übermitteln. Da die CPU und die GPU nicht synchron laufen, wissen wir zunächst nicht, ob die Anzeige tatsächlich erstellt wurde. Die GPU benötigt Zeit, um unseren Wunsch umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Um sicherzustellen, dass die Anzeige erfolgreich erzeugt wurde, fragen wir kontinuierlich ab, ob eine Adresse für den Bildschirm existiert. Diese Adresse wird in unserer Struktur unter pScreen[31] abgelegt. Wenn die GPU erfolgreich war, schreibt sie die Adresse in dieses Feld. Solange dies nicht der Fall ist, wiederholen wir die Abfrage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uintptr pScreenAddress = (uintptr)pScreen;&lt;br /&gt;
while (TRUE) {&lt;br /&gt;
    BcmMailBox_Write(BCM_MAILBOX_PROP_OUT, (u32)pScreenAddress);&lt;br /&gt;
    &lt;br /&gt;
    if (pScreen[31] != 0) {&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls die GPU nie eine Adresse zurückgibt, würden wir in einer Endlosschleife festhängen. Später werden wir weitere Funktionen einführen, um solche Probleme zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Die Adresse, die wir zurückbekommen, ist ein Byte-Wert, mit dem wir so nicht direkt arbeiten können. Wir wandeln diesen Wert um und speichern ihn für spätere Zwecke.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define ADDRESS_MASK 0x3FFFFFFF&lt;br /&gt;
graphicsAddress = pScreen[31] &amp;amp; ADDRESS_MASK;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Auf den Bildschirm zeichnen ==&lt;br /&gt;
&lt;br /&gt;
Nun haben wir einen Screen, der allerdings leer ist. Wir benötigen eine Methode, um etwas auf den Bildschirm zu zeichnen.&lt;br /&gt;
&lt;br /&gt;
Wir entwickeln eine Funktion, die einen einzelnen Bildpunkt (Pixel) anzeigt. Diese grundlegende Funktion bildet die Basis für weitere grafische Funktionen.&lt;br /&gt;
&lt;br /&gt;
Wir nennen die Funktion DrawPixel und übergeben ihr die Koordinaten x und y.&lt;br /&gt;
&lt;br /&gt;
Für verschiedene Farbtiefen sind unterschiedliche Berechnungen notwendig. Zunächst halten wir es einfach und gehen von einer Farbtiefe von 32 Bit aus.&lt;br /&gt;
&lt;br /&gt;
Zuerst überprüfen wir, ob der Pixel im sichtbaren Bereich liegt, um unerwünschte Nebeneffekte zu vermeiden. Danach berechnen wir die Position nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
((Gesamtbreite des Screens * Position y) + Position x) * Farbtiefe in Bytes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Screen-Adresse wird zu dieser Position hinzuaddiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DrawPixel(u32 x, u32 y) {&lt;br /&gt;
    if ((x &amp;lt; SCREEN_X) &amp;amp;&amp;amp; (y &amp;lt; SCREEN_Y)) {&lt;br /&gt;
        write32(DrawColor, graphicsAddress + (((SCREEN_X * y) + x) * 4));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird bereits der Pixel angezeigt. &lt;br /&gt;
&lt;br /&gt;
== Header-Datei für DrawPixel ==&lt;br /&gt;
&lt;br /&gt;
Wir erstellen eine Header-Datei, in der wir unsere Funktionen beschreiben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// screen.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _screen_h&lt;br /&gt;
#define _screen_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void Init_Screen(void);&lt;br /&gt;
void DrawPixel(u32 x, u32 y);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung der DrawPixel-Funktion ==&lt;br /&gt;
&lt;br /&gt;
Wir haben nun eine Funktion, die es ermöglicht, etwas auf dem Bildschirm zu sehen. Nun verwenden wir die neuen Funktionen im Hauptprogramm, um ein Ergebnis anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
Zuerst definieren wir die Variablen, die wir beim Erstellen der TAG-Struktur verwendet haben, in der config.h Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#define SCREEN_X       1920&lt;br /&gt;
#define SCREEN_Y       1080&lt;br /&gt;
#define BITS_PER_PIXEL 32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Werte definieren die Breite, Höhe und Tiefe des Bildschirms.&lt;br /&gt;
&lt;br /&gt;
Die erste Funktion, die wir aufrufen, ist die Bildschirminitialisierung:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
   Init_Screen();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein einzelner Punkt auf dem Bildschirm ist schwer zu sehen. Daher setzen wir mehrere Punkte nebeneinander und verwenden eine Schleife, die die Koordinaten verändert und jeden Punkt zeichnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
    u32 i;&lt;br /&gt;
    for (i=100;i&amp;lt;=500;i++)&lt;br /&gt;
    {&lt;br /&gt;
        DrawPixel(i,i);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Quellcode für dieses Beispiel ist [https://www.satyria.de/arm/sources/RPI4/C/4.zip hier] verfügbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Hier im Tutorial wird eine sehr einfache Mailbox-Abfrage verwendet. Dies ist für die jetzige Umsetzung ausreichend. Wenn allerdings später zum Beispiel Multitasking oder mehrere Cores erlaubt werden, ist diese Methode nicht mehr ausreichend. Es ist wichtig, dass nur eine Abfrage gleichzeitig durchgeführt werden darf. Später wird diese Funktion durch eine von Broadcom offizielle Version ersetzt. &lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Fehlerbehandlung in C (PI4)|&amp;lt; Zurück (Fehlerbehandlung in C (PI4))]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Chars in C (PI4)|Weiter (Chars in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Grafik_in_C_(PI4)&amp;diff=1340</id>
		<title>Grafik in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Grafik_in_C_(PI4)&amp;diff=1340"/>
		<updated>2026-07-23T11:27:10Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Anwendung der DrawPixel-Funktion */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die bisherigen Programme auf dem Raspberry Pi waren vergleichsweise einfach und ermöglichten lediglich die Steuerung einer LED. Der Raspberry Pi kann jedoch weitaus mehr, insbesondere im Bereich der Grafik, da er über einen HDMI-Ausgang verfügt, der für fortgeschrittene Anwendungen genutzt werden kann. In diesem Abschnitt wollen wir den HDMI-Ausgang des Raspberry Pi verwenden, um grafische Ergebnisse direkt anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
Zu Beginn schließen wir einen Monitor an den Raspberry Pi an, um die Ergebnisse direkt auf dem Bildschirm sehen zu können. Da wir Bare Metal programmieren, müssen wir sämtliche Routinen selbst schreiben, da der Raspberry Pi keine vorinstallierten Weisungen hat, wie er Bilder auf dem Monitor anzeigt.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi 4 besitzt zwei HDMI-Ausgänge, die jeweils Bildschirme mit bis zu UHD-Auflösung (4096 x 2160 Pixel) bei 60 Hz unterstützen. Bei Verwendung beider HDMI-Ports beträgt die Bildwiederholrate maximal 30 Hz. Ältere Raspberry Pi-Modelle unterstützen Bildschirme bis zu einer Auflösung von 1920 x 1200 Pixel bei 60 Hz.&lt;br /&gt;
&lt;br /&gt;
Interessanterweise ist es die GPU des Raspberry Pi, die den Startvorgang initiiert. Sie lädt die Firmware in den Speicher und übergibt danach die Kontrolle an die CPU. Dies ist während des Startvorgangs deutlich sichtbar, wenn ein Bildschirm angeschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Um mit der Grafikprogrammierung zu beginnen, müssen wir zunächst verstehen, wie ein Bild auf dem Bildschirm dargestellt wird. Ein Bildschirm besteht aus einzelnen Punkten, den Pixeln, die jeweils eine eigene Farbe haben. Normalerweise wird der Bildschirm in einem Koordinatensystem (x, y) dargestellt. Der erste Pixel oben links hat die Koordinaten (0,0). Der nachfolgende Pixel rechts davon hat die Koordinate (1,0) und so weiter. Nach unten wird die y-Koordinate jeweils um eins erhöht: (0,1), (0,2) usw.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten1.png|rand]]&lt;br /&gt;
&lt;br /&gt;
== Auflösungen ==&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt viele verschiedene Auflösungen. Eine Übersicht über die gängigsten Auflösungen findest du beispielsweise unter https://de.wikipedia.org/wiki/Bildaufl%C3%B6sung#Computer. Hier ein paar Auflösungen, die verwendet werden könnten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! Auflösung&lt;br /&gt;
|-&lt;br /&gt;
| VGA || 640 x 480&lt;br /&gt;
|-&lt;br /&gt;
| XGA || 1024 x 768&lt;br /&gt;
|-&lt;br /&gt;
| HD || 1920 x 1080&lt;br /&gt;
|-&lt;br /&gt;
| UHD (only Pi 4 or Pi 5) || 4096 x 2160&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Auflösung allein reicht jedoch nicht aus, um komplexe, farbige Bilder darzustellen. Da Computersysteme nur binäre Zahlen kennen, also nur &amp;quot;1&amp;quot; oder &amp;quot;0&amp;quot;, könnte man jedes Pixel lediglich ein- oder ausschalten, was nur ein Schwarz-Weiß-Bild erzeugen würde. Um die Vielzahl der Farben, die wir auf Bildschirmen sehen, darzustellen, werden zusätzliche Standards definiert.&lt;br /&gt;
&lt;br /&gt;
Wird die Anzeige mehrmals hintereinander vertieft, entsteht eine größere Farbtiefe. Wenn ein Bild beispielsweise zwei Bitmaps verwendet, stehen für jeden Punkt zwei Bits zur Verfügung. Das ergibt vier Zustände bzw. vier Farben. Je mehr Bitmaps verwendet werden, desto mehr Farben können angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
== Farbtiefe ==&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt vier verschiedene Farbtiefen: 8-Bit, 16-Bit, 24-Bit und 32-Bit. In der Praxis wird jedoch von der Verwendung der 8-Bit und 24-Bit Farbtiefe abgeraten, da sie zu Darstellungsfehlern führen können. Siehe hierzu auch die Dokumentation unter https://www.raspberrypi.org/documentation/configuration/config-txt/video.md im Abschnitt framebuffer_depth. Um es einfach zu halten, konzentrieren wir uns auf die 32-Bit Farbtiefe.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Speicheraufwand für Bilder&lt;br /&gt;
Um den Speicheraufwand zu veranschaulichen, betrachten wir ein Beispiel. Ein monochromes Bild mit einer Auflösung von 20 x 20 Pixel benötigt folgenden Speicherbedarf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Speicherbedarf (Byte) = (x * y) / 8         (20*20)/8 = 50 Bytes  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten2.png|rand|400x400px]]&lt;br /&gt;
&lt;br /&gt;
Für ein Bild mit 4 Bitmaps beträgt der Speicherbedarf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Speicherbedarf (Byte) = ((x * y) * tiefe) / 8      ((20*20)*4)/8 = 200 Bytes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten3.png|rand|500x500px]]&lt;br /&gt;
&lt;br /&gt;
Nun ein Beispiel mit einer HD-Auflösung und RGBA32-Farbtiefe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Speicherbedarf (Bytes) = ((1920 * 1080) * 32) / 8 = 8294400 Bytes&lt;br /&gt;
Das entspricht etwa 8,3 Megabytes.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Abbildung der Bitmaps im Speicher ==&lt;br /&gt;
&lt;br /&gt;
Bitmaps werden nicht als eine Reihe von Einzelbildern (Bitmaps) im Speicher abgelegt, sondern die Grafikdaten sind wie in einem dreidimensionalen Raum organisiert. Betrachten wir ein Beispiel mit einer Auflösung von 20 x 20 Pixel und einer Farbtiefe von 16 Bit:&lt;br /&gt;
&lt;br /&gt;
Jeder Bildschirmpunkt wird bei einer Farbtiefe von 16 Bit durch 16 aufeinanderfolgende Bits dargestellt. Der erste Punkt verwendet die Bits 0-15, der nächste die Bits 16-31, und so weiter. Das müssen wir bei den Berechnungen für jeden einzelnen Punkt berücksichtigen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Koordinaten4.png|rand|600x600px]]&lt;br /&gt;
&lt;br /&gt;
== Der Postbote und TAGs ==&lt;br /&gt;
Wie bereits beschrieben, wird die Grafik nicht von der CPU, sondern von der GPU (Graphics Processing Unit) dargestellt. Damit die GPU weiß, was sie tun soll, benötigt sie bestimmte Informationen zur Darstellung, etwa wie der Bildschirm aussehen soll. Eine direkte Verbindung zwischen CPU und GPU existiert jedoch nicht, weshalb die Kommunikation über einen Vermittler erfolgt, der hier als &amp;quot;Postbote&amp;quot; bezeichnet wird. Die CPU sendet eine Nachricht an den Postboten, und dieser leitet die Nachricht an die GPU weiter. Ebenso kann die GPU Nachrichten an die CPU schicken.&lt;br /&gt;
&lt;br /&gt;
Der Postbote hat die Basisadresse 0xB880 und verwendet &amp;quot;TAGs&amp;quot; für die Informationsübermittlung. Die Dokumentation zu diesen TAGs ist leider spärlich, doch es gibt eine Gemeinschaft, die Informationen gesammelt und bereitgestellt hat. Weitere Informationen findest du hier:&lt;br /&gt;
&lt;br /&gt;
https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface&lt;br /&gt;
&lt;br /&gt;
Diese Informationen habe ich in dem zugehörigen Sourcecode zusammengefasst und in der base.h Datei eingefügt.&lt;br /&gt;
&lt;br /&gt;
Zunächst definieren wir in der base.h die neuen Register, die dafür verantwortlich sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// Mailbox&lt;br /&gt;
//&lt;br /&gt;
#define MAILBOX_BASE RPI_BASE + 0xB880 &lt;br /&gt;
#define MAILBOX0_READ       MAILBOX_BASE + 0x00&lt;br /&gt;
#define MAILBOX0_STATUS     MAILBOX_BASE + 0x18&lt;br /&gt;
#define MAILBOX_STATUS_EMPTY    0x40000000&lt;br /&gt;
#define MAILBOX1_WRITE      MAILBOX_BASE + 0x20&lt;br /&gt;
#define MAILBOX1_STATUS     MAILBOX_BASE + 0x38&lt;br /&gt;
#define MAILBOX_STATUS_FULL 0x80000000&lt;br /&gt;
&lt;br /&gt;
#define MAIL_WRITE MAILBOX_BASE + 0x20&lt;br /&gt;
#define MAIL_TAG_WRITE MAIL_WRITE + 0x8&lt;br /&gt;
&lt;br /&gt;
#define MAILBOX_CHANNEL_PM  0       // power management&lt;br /&gt;
#define MAILBOX_CHANNEL_FB  1       // frame buffer&lt;br /&gt;
#define BCM_MAILBOX_PROP_OUT    8       // property tags (ARM to VC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Kommunikation verwenden wir &amp;quot;TAGs&amp;quot;. Diese TAGs sind in einer bestimmten Struktur organisiert, die von der GPU verstanden wird.&lt;br /&gt;
&lt;br /&gt;
=== Aufbau der TAG-Struktur ===&lt;br /&gt;
TAG-Listen haben eine festgelegte Struktur, die die GPU versteht. Eine TAG-Struktur beginnt immer mit einer 32-Bit Zahl, die die Länge der gesamten Struktur angibt. Es folgt ein 32-Bit Puffer, in den eine Antwort geschrieben werden kann. Danach kommen die einzelnen TAGs, die wiederum eine Grundstruktur aufweisen: Der Name des TAGs, die Puffergröße in Bytes, ein Antwortpuffer und die Werte des TAGs. Die Struktur endet mit einem NULL-TAG.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
Mailbox Länge&lt;br /&gt;
Mailbox Puffer&lt;br /&gt;
   TAG1 Name&lt;br /&gt;
   TAG1 Länge&lt;br /&gt;
   TAG1 Puffer&lt;br /&gt;
      TAG1 Wert1&lt;br /&gt;
      TAG1 Wert2&lt;br /&gt;
      ...&lt;br /&gt;
   TAG2 Name&lt;br /&gt;
   ...&lt;br /&gt;
   TAGx NULL (End TAG)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jeder TAG muss auf 32-Bit ausgerichtet sein.&lt;br /&gt;
&lt;br /&gt;
== Aufbau unserer Struktur ==&lt;br /&gt;
Wir erstellen unsere TAG-Struktur und richten sie auf 16 Byte aus, da nur die oberen 28 Bits der Adresse an die Mailbox übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
u32 pScreen[36] ALIGN(16) = {&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Struktur beginnt mit der Angabe der Länge und einem Puffer für mögliche Antworten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
u32 pScreen[36] ALIGN(16) = {&lt;br /&gt;
    34*4, //34 Einträge * 4 Bytes&lt;br /&gt;
    0,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt fügen wir die Daten hinzu, beginnend mit der Auflösung des Bildschirms. Der TAG Set_physical_display hat folgende Struktur:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_physical_display)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Breite des Bildschirms in Pixel&lt;br /&gt;
Höhe des Bildschirms in Pixel&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_physical_display, //Tag Identifier&lt;br /&gt;
    0x8,0x8,              //Buffer size in bytes&lt;br /&gt;
    SCREEN_X,             //Width in pixels&lt;br /&gt;
    SCREEN_Y,             //Height in pixels&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variablen SCREEN_X und SCREEN_Y definieren wir in der config.h Datei, hier für eine Auflösung von 1920 x 1080 Pixel.&lt;br /&gt;
&lt;br /&gt;
Als nächstes definieren wir den Set_virtual_buffer TAG:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_virtual_buffer)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Breite des Bildschirms in Pixel&lt;br /&gt;
Höhe des Bildschirms in Pixel&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_virtual_buffer,&lt;br /&gt;
    0x8,0x8,&lt;br /&gt;
    SCREEN_X,&lt;br /&gt;
    SCREEN_Y,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Set_depth TAG definiert die Farbtiefe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_depth)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Tiefe der Bitplanes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
  Set_depth,            //Tag Identifier&lt;br /&gt;
  0x4,                   //Buffer size in bytes&lt;br /&gt;
  0x4,                  //Response buffer&lt;br /&gt;
  BITS_PER_PIXEL,       //depth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier verwenden wir eine Variable BITS_PER_PIXEL, die wir in der config.h Datei auf 32 Bit definieren.&lt;br /&gt;
&lt;br /&gt;
Der Set_virtual_offset TAG erzeugt Offset-Werte:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_virtual_offset)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Offset x&lt;br /&gt;
Offset y&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_virtual_offset,&lt;br /&gt;
    0x8,0x8,&lt;br /&gt;
    0x0,0x0,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Set_palette TAG definiert die Farbpalette:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Set_palette)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
erster eingestellter Palettenindex&lt;br /&gt;
Anzahl der einzustellenden Paletteneinträge&lt;br /&gt;
   RGBA Palettenwerte&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Set_palette,&lt;br /&gt;
    0x10,0x10,&lt;br /&gt;
    0x0,&lt;br /&gt;
    0x2,&lt;br /&gt;
    0x00000000,&lt;br /&gt;
    0xffffffff,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da wir hauptsächlich 16-Bit oder 32-Bit Farbtiefen verwenden, ist der Eintrag Set_palette weniger wichtig, muss aber dennoch definiert werden.&lt;br /&gt;
&lt;br /&gt;
Der Allocate_buffer TAG sorgt dafür, dass die GPU Speicher reserviert und diesen anzeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
TAG Name (Allocate_buffer)&lt;br /&gt;
Puffergröße in Bytes&lt;br /&gt;
Antwort Puffer&lt;br /&gt;
Frame Buffer Basisadresse in Bytes&lt;br /&gt;
Bildpuffergröße in Bytes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In unsere Struktur übertragen sieht es so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    Allocate_buffer,&lt;br /&gt;
    0x8,0x8,&lt;br /&gt;
    0x0,          //Bufferadresse (pScreen[31])&lt;br /&gt;
    0x0,&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der TAG erzeugt einen Puffer, dessen Adresse in pScreen[31] abgelegt wird, sobald die GPU dies durchgeführt hat. Dies ist die Adresse, auf die wir später zugreifen können.&lt;br /&gt;
&lt;br /&gt;
Unsere TAG-Liste endet mit einem NULL-TAG:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  0x00000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Struktur beschreibt, was wir darstellen möchten. Die GPU interpretiert diese Werte und versucht, sie darzustellen. Wenn die GPU die Angaben nicht exakt umsetzen kann, erzeugt sie einen ähnlichen Zustand. Wir sollten die von der GPU zurückgemeldeten Werte überprüfen, um sicherzustellen, dass sie unseren Erwartungen entsprechen.&lt;br /&gt;
&lt;br /&gt;
=== Der Postbote ===&lt;br /&gt;
&lt;br /&gt;
Nun bringt der Postbote unsere TAG-Liste zur GPU, um den Bildschirm zu erzeugen. Wir verwenden zunächst eine einfache Methode und erweitern später die Funktionen.&lt;br /&gt;
&lt;br /&gt;
Zuerst geben wir dem Postboten den Auftrag, die Informationen an die GPU zu übermitteln. Da die CPU und die GPU nicht synchron laufen, wissen wir zunächst nicht, ob die Anzeige tatsächlich erstellt wurde. Die GPU benötigt Zeit, um unseren Wunsch umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Um sicherzustellen, dass die Anzeige erfolgreich erzeugt wurde, fragen wir kontinuierlich ab, ob eine Adresse für den Bildschirm existiert. Diese Adresse wird in unserer Struktur unter pScreen[31] abgelegt. Wenn die GPU erfolgreich war, schreibt sie die Adresse in dieses Feld. Solange dies nicht der Fall ist, wiederholen wir die Abfrage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uintptr pScreenAddress = (uintptr)pScreen;&lt;br /&gt;
while (TRUE) {&lt;br /&gt;
    BcmMailBox_Write(BCM_MAILBOX_PROP_OUT, (u32)pScreenAddress);&lt;br /&gt;
    &lt;br /&gt;
    if (pScreen[31] != 0) {&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls die GPU nie eine Adresse zurückgibt, würden wir in einer Endlosschleife festhängen. Später werden wir weitere Funktionen einführen, um solche Probleme zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Die Adresse, die wir zurückbekommen, ist ein Byte-Wert, mit dem wir so nicht direkt arbeiten können. Wir wandeln diesen Wert um und speichern ihn für spätere Zwecke.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define ADDRESS_MASK 0x3FFFFFFF&lt;br /&gt;
graphicsAddress = pScreen[31] &amp;amp; ADDRESS_MASK;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Auf den Bildschirm zeichnen ==&lt;br /&gt;
&lt;br /&gt;
Nun haben wir einen Screen, der allerdings leer ist. Wir benötigen eine Methode, um etwas auf den Bildschirm zu zeichnen.&lt;br /&gt;
&lt;br /&gt;
Wir entwickeln eine Funktion, die einen einzelnen Bildpunkt (Pixel) anzeigt. Diese grundlegende Funktion bildet die Basis für weitere grafische Funktionen.&lt;br /&gt;
&lt;br /&gt;
Wir nennen die Funktion DrawPixel und übergeben ihr die Koordinaten x und y.&lt;br /&gt;
&lt;br /&gt;
Für verschiedene Farbtiefen sind unterschiedliche Berechnungen notwendig. Zunächst halten wir es einfach und gehen von einer Farbtiefe von 32 Bit aus.&lt;br /&gt;
&lt;br /&gt;
Zuerst überprüfen wir, ob der Pixel im sichtbaren Bereich liegt, um unerwünschte Nebeneffekte zu vermeiden. Danach berechnen wir die Position nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
((Gesamtbreite des Screens * Position y) + Position x) * Farbtiefe in Bytes&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Screen-Adresse wird zu dieser Position hinzuaddiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void DrawPixel(u32 x, u32 y) {&lt;br /&gt;
    if ((x &amp;lt; SCREEN_X) &amp;amp;&amp;amp; (y &amp;lt; SCREEN_Y)) {&lt;br /&gt;
        write32(DrawColor, graphicsAddress + (((SCREEN_X * y) + x) * 4));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird bereits der Pixel angezeigt. &lt;br /&gt;
&lt;br /&gt;
== Header-Datei für DrawPixel ==&lt;br /&gt;
&lt;br /&gt;
Wir erstellen eine Header-Datei, in der wir unsere Funktionen beschreiben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// screen.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _screen_h&lt;br /&gt;
#define _screen_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void Init_Screen(void);&lt;br /&gt;
void DrawPixel(u32 x, u32 y);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung der DrawPixel-Funktion ==&lt;br /&gt;
&lt;br /&gt;
Wir haben nun eine Funktion, die es ermöglicht, etwas auf dem Bildschirm zu sehen. Nun verwenden wir die neuen Funktionen im Hauptprogramm, um ein Ergebnis anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
Zuerst definieren wir die Variablen, die wir beim Erstellen der TAG-Struktur verwendet haben, in der config.h Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#define SCREEN_X       1920&lt;br /&gt;
#define SCREEN_Y       1080&lt;br /&gt;
#define BITS_PER_PIXEL 32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Werte definieren die Breite, Höhe und Tiefe des Bildschirms.&lt;br /&gt;
&lt;br /&gt;
Die erste Funktion, die wir aufrufen, ist die Bildschirminitialisierung:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
   Init_Screen();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein einzelner Punkt auf dem Bildschirm ist schwer zu sehen. Daher setzen wir mehrere Punkte nebeneinander und verwenden eine Schleife, die die Koordinaten verändert und jeden Punkt zeichnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
    u32 i;&lt;br /&gt;
    for (i=100;i&amp;lt;=500;i++)&lt;br /&gt;
    {&lt;br /&gt;
        DrawPixel(i,i);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Quellcode für dieses Beispiel ist [https://www.satyria.de/arm/sources/RPI4/C/4.zip hier] verfügbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Hier im Tutorial wird eine sehr einfache Mailbox-Abfrage durchgeführt. Dies ist für die jetzige Umsetzung ausreichend. Wenn allerdings später zum Beispiel Multitasking oder mehrere Cores erlaubt werden, ist diese Methode nicht mehr ausreichend. Es ist wichtig, dass nur eine Abfrage gleichzeitig durchgeführt werden darf. Später wird diese Funktion durch eine von Broadcom offizielle Version ersetzt. &lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Fehlerbehandlung in C (PI4)|&amp;lt; Zurück (Fehlerbehandlung in C (PI4))]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Chars in C (PI4)|Weiter (Chars in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1339</id>
		<title>Interrupts (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1339"/>
		<updated>2026-07-22T10:50:16Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span style=&amp;quot;background:#FF0000; color:#FFFFFF;&amp;quot;&amp;gt; Wichtig! Durch die Entwicklung zur USB-Unterstützung wurde einige Funktionen, die wir bisher genutzt haben, überarbeitet. Diese &amp;quot;neuen&amp;quot; Funktionen sind ab hier eingebaut. Darunter zählt zum Beispiel die printf-Funktion, die hier komplett überarbeitet ist. Zusätzlich wurden einige Screen-Funktionen überarbeitet. &amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Interrupts und Exceptions auf dem Raspberry Pi 4 (Bare Metal) ==&lt;br /&gt;
&lt;br /&gt;
Interrupts (Unterbrechungen) und Exceptions (Ausnahmen) sind das Nervensystem jedes Betriebssystems. &lt;br /&gt;
Sie sorgen dafür, dass der Prozessor nicht blind Code abarbeitet, sondern auf Ereignisse reagieren kann – sei es,&lt;br /&gt;
dass ein Timer abläuft, eine Taste gedrückt wird oder ein schwerwiegender Programmierfehler auftritt.&lt;br /&gt;
&lt;br /&gt;
In diesem Kapitel lernst du, wie die Ausnahmebehandlung im ARMv8 (AArch64) funktioniert,&lt;br /&gt;
wie wir die Vektortabelle einrichten und wie wir mit einem einfachen &amp;quot;Blindtest&amp;quot; sicherstellen,&lt;br /&gt;
dass unser System bereit für echte Hardware-Interrupts ist.&lt;br /&gt;
&lt;br /&gt;
== Exceptions vs. Interrupts: Was ist der Unterschied? ==&lt;br /&gt;
&lt;br /&gt;
Obwohl die Begriffe oft synonym verwendet werden, gibt es einen feinen, aber wichtigen Unterschied:&lt;br /&gt;
* Exceptions (Synchron): Treten &#039;&#039;&#039;direkt&#039;&#039;&#039; durch die Ausführung eines Befehls auf. Beispiele: Ein ungültiger Speicherzugriff, eine Division durch Null oder ein bewusster Systemaufruf (wie `svc #0`). Der Prozessor weiß &#039;&#039;&#039;genau&#039;&#039;&#039;, welcher Befehl die Ausnahme ausgelöst hat.&lt;br /&gt;
* Interrupts (Asynchron): Treten &#039;&#039;&#039;unabhängig&#039;&#039;&#039; vom aktuellen Befehl auf, ausgelöst durch externe Hardware. Beispiele: Der System-Timer, eine USB-Maus oder ein GPIO-Pin.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tipp für die OS-Entwicklung&#039;&#039;: Synchronous Exceptions (wie unsere `SynchError`-Handler) sind deine besten Freunde beim Debuggen! Wenn dein Code versehentlich in einen `SynchError`-Handler springt, weißt du sofort: Hier ist ein schwerer Fehler (z. B. Sprung an eine ungültige Adresse oder falscher Befehl). Wir werden das gleich gezielt ausnutzen.&lt;br /&gt;
&lt;br /&gt;
== Die Spielregeln: Exception Levels (EL) und DAIF ==&lt;br /&gt;
&lt;br /&gt;
Der ARM Cortex-A72 kennt verschiedene Privilegienstufen, die &#039;&#039;&#039;Exception Levels (EL)&#039;&#039;&#039;:&lt;br /&gt;
* &#039;&#039;&#039;EL0:&#039;&#039;&#039; User-Mode (Anwendungen, wenig Rechte)&lt;br /&gt;
* &#039;&#039;&#039;EL1:&#039;&#039;&#039; Kernel-Mode (Unser Bare-Metal-Code, volle Hardware-Kontrolle)&lt;br /&gt;
* &#039;&#039;&#039;EL2:&#039;&#039;&#039; Hypervisor (Virtualisierung, wird vom Bootloader kurz genutzt)&lt;br /&gt;
* &#039;&#039;&#039;EL3:&#039;&#039;&#039; Secure Monitor (Highest Privilege, z. B. ARM Trusted Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Um Interrupts korrekt zu verarbeiten, müssen wir uns in &#039;&#039;&#039;EL1&#039;&#039;&#039; befinden. Da der Raspberry Pi 4 oft in EL2 startet, ist unser erster Schritt der Wechsel nach EL1.&lt;br /&gt;
&lt;br /&gt;
Zudem kontrolliert das &#039;&#039;&#039;DAIF&#039;&#039;&#039;-Register, welche Interrupts gerade erlaubt sind:&lt;br /&gt;
* &#039;&#039;&#039;D&#039;&#039;&#039; (Debug): Debug-Exceptions sperren&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; (SError): Asynchrone Systemfehler sperren&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; (IRQ): Normale Interrupts sperren&lt;br /&gt;
* &#039;&#039;&#039;F&#039;&#039;&#039; (FIQ): Schnelle Interrupts (Fast IRQs) sperren&lt;br /&gt;
&lt;br /&gt;
Ist ein Bit auf `1` gesetzt, ist der Interrupt &#039;&#039;&#039;gesperrt&#039;&#039;&#039;. Um IRQs zu erlauben, müssen wir das I-Bit auf `0` setzen (clear).&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Nur ein Kern darf laufen ==&lt;br /&gt;
&lt;br /&gt;
Der Pi 4 hat 4 Kerne. Beim Start führen alle denselben Code aus. Würden alle versuchen, Interrupts zu bearbeiten oder den Stack zu nutzen, würde das System sofort abstürzen. Wir lassen nur &#039;&#039;&#039;Core 0&#039;&#039;&#039; arbeiten und schicken die anderen in einen energiesparenden Schlafmodus (`WFE` = Wait For Event).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In boot.S&lt;br /&gt;
_start:&lt;br /&gt;
    mrs x1, mpidr_el1    // Core-ID lesen&lt;br /&gt;
    and x1, x1, #3       // Nur die unteren 2 Bits (0-3) behalten&lt;br /&gt;
    cbz x1, core0        // Wenn Core 0, weiter zum Setup&lt;br /&gt;
&lt;br /&gt;
core_sleep:&lt;br /&gt;
    wfe                  // Warte auf ein Event (Sleep)&lt;br /&gt;
    b core_sleep         // Endlosschleife für Core 1-3&lt;br /&gt;
&lt;br /&gt;
core0:&lt;br /&gt;
    // Ab hier läuft nur noch Core 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Der Wechsel von EL2 nach EL1 ==&lt;br /&gt;
&lt;br /&gt;
Da wir ein Kernel schreiben, gehört unser Platz in EL1. Der folgende Code konfiguriert EL1, setzt den Stackpointer und bereitet den Rücksprung vor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    // 1. Prüfen, ob wir schon in EL1 sind (CurrentEL = 8 für EL1)&lt;br /&gt;
    mrs x0, CurrentEL&lt;br /&gt;
    cmp x0, #8&lt;br /&gt;
    beq switch_to_el1&lt;br /&gt;
&lt;br /&gt;
    // 2. EL1 Stackpointer setzen&lt;br /&gt;
    ldr x0, =EXCEPTION_STACK&lt;br /&gt;
    msr sp_el1, x0&lt;br /&gt;
&lt;br /&gt;
    // 3. Vektortabelle für EL2 setzen (für den Fall, dass während des Wechsels was passiert)&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el2, x0&lt;br /&gt;
&lt;br /&gt;
    // ... (Hier folgen Timer- und Virtualisierungs-Einstellungen, siehe boot.S) ...&lt;br /&gt;
&lt;br /&gt;
    // 4. Rücksprung-Adresse und Status für EL1 vorbereiten&lt;br /&gt;
    mov x0, #0x3c4             // SPSR_EL2: EL1h, Interrupts (DAIF) sind hier noch maskiert&lt;br /&gt;
    msr spsr_el2, x0&lt;br /&gt;
    adr x0, el1_return         // Adresse, zu der wir nach dem Wechsel springen&lt;br /&gt;
    msr elr_el2, x0&lt;br /&gt;
&lt;br /&gt;
    eret                       // Exception Return: Wechselt nach EL1 zu &#039;el1_return&#039;&lt;br /&gt;
&lt;br /&gt;
el1_return:&lt;br /&gt;
switch_to_el1:&lt;br /&gt;
    // Wir sind jetzt in EL1!&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el1, x0           // Vektortabelle für EL1 aktivieren&lt;br /&gt;
&lt;br /&gt;
    // BSS-Segment (uninitialisierte Variablen) mit Nullen füllen&lt;br /&gt;
    ldr x1, =__bss_start&lt;br /&gt;
    ldr w2, =__bss_size&lt;br /&gt;
clean_bss_loop:&lt;br /&gt;
    cbz w2, bss_clean_done&lt;br /&gt;
    str xzr, [x1], #8&lt;br /&gt;
    sub w2, w2, #1&lt;br /&gt;
    cbnz w2, clean_bss_loop&lt;br /&gt;
bss_clean_done:&lt;br /&gt;
&lt;br /&gt;
    b main                     // Ab ins C-Hauptprogramm!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Exception Vector Table (VBT) ==&lt;br /&gt;
&lt;br /&gt;
Die VBT ist das &amp;quot;Telefonbuch&amp;quot; des Prozessors. Bei einer Ausnahme schaut die CPU in dieses Register (`vbar_el1`) und springt zu der dort hinterlegten Adresse. &lt;br /&gt;
&lt;br /&gt;
Die Tabelle muss exakt &#039;&#039;&#039;2048 Byte (2 KB)&#039;&#039;&#039; groß sein (&#039;&#039;.align 11&#039;&#039;). Sie ist in 16 Einträge unterteilt (4 Ausnahmetypen × 4 Ausführungsstatus). Jeder Eintrag muss exakt &#039;&#039;&#039;128 Byte&#039;&#039;&#039; (&#039;&#039;.align 7&#039;&#039;) voneinander entfernt sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S&lt;br /&gt;
.align 11                      // 2^11 = 2048 Byte Ausrichtung für die ganze Tabelle&lt;br /&gt;
.globl VectorTable&lt;br /&gt;
VectorTable:&lt;br /&gt;
    // 1. Aktuelles EL, Stackpointer ist SP_EL0 (User-Mode, nutzen wir nicht)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub1&lt;br /&gt;
&lt;br /&gt;
    // 2. Aktuelles EL, Stackpointer ist SP_ELx (Kernel-Mode, EL1h) -&amp;gt; DAS NUTZEN WIR!&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError2              // Synchronous Exception (z.B. svc #0, Memory Abort)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler        // Normaler Hardware-Interrupt (IRQ)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler     // FIQ&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub2              // System Error&lt;br /&gt;
&lt;br /&gt;
    // 3. Niedrigeres EL (EL0), 64-Bit (Auch hier leiten wir auf unsere Handler um)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    // ... (weitere Einträge siehe vector.S)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
🛠️ &#039;&#039;&#039;Debugging-Hinweis:&#039;&#039;&#039; Die &#039;&#039;SynchError&#039;&#039;-Handler sind extrem wertvoll. Wenn dein Programm unerwartet in `SynchError2` landet, weißt du: Ein Befehl in EL1 war ungültig oder hat einen Fehler verursacht. Wir werden das gleich gezielt provozieren, um zu prüfen, ob die Tabelle funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Der Blindtest: `svc #0` (Supervisor Call) ==&lt;br /&gt;
&lt;br /&gt;
Bevor wir uns mit komplexer Hardware (wie dem GIC-Interrupt-Controller) herumschlagen, müssen wir sicherstellen, dass unser Exception-Mechanismus grundlegend funktioniert. &lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;svc #0&#039;&#039; (Supervisor Call) löst absichtlich eine &#039;&#039;&#039;synchrone Exception&#039;&#039;&#039; aus. Da wir uns in EL1 befinden, sollte die CPU in den Eintrag &#039;&#039;SynchError2&#039;&#039; unserer Vektortabelle springen.&lt;br /&gt;
&lt;br /&gt;
Füge diesen Test vorübergehend in deine `main.c` ein, *bevor* du `printf` oder andere Initialisierungen machst:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// In main.c&lt;br /&gt;
#include &amp;quot;printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;sync.h&amp;quot; // Für Stop()&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    // --- BLINDTEST FÜR EXCEPTIONS ---&lt;br /&gt;
    printf(&amp;quot;Starte Exception-Blindtest...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Dieser Befehl löst absichtlich eine synchrone Exception aus.&lt;br /&gt;
    // Die CPU sollte in vector.S -&amp;gt; SynchError2 -&amp;gt; ExceptionHandler springen.&lt;br /&gt;
    asm volatile(&amp;quot;svc #0&amp;quot;); &lt;br /&gt;
    &lt;br /&gt;
    // Diese Zeile sollte NICHT erreicht werden!&lt;br /&gt;
    printf(&amp;quot;FEHLER: svc #0 wurde nicht abgefangen!\n&amp;quot;);&lt;br /&gt;
    Stop();&lt;br /&gt;
    // ----------------------------------&lt;br /&gt;
&lt;br /&gt;
    // ... Rest deiner Initialisierung (Interrupt_Initialize, Timer, etc.) ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Erwartetes Ergebnis:**&lt;br /&gt;
Auf dem Bildschirm sollte erscheinen:&lt;br /&gt;
`Starte Exception-Blindtest...`&lt;br /&gt;
`Error: Exception &#039;SynchError2&#039; ausgelöst`&lt;br /&gt;
Und das Programm bleibt stehen (dank `Stop()` im `ExceptionHandler`).&lt;br /&gt;
&lt;br /&gt;
**Wenn das funktioniert, hast du den schwersten Teil bereits geschafft:** Der Wechsel nach EL1, das Setzen von `vbar_el1` und die korrekte Ausrichtung der Vektortabelle sind verifiziert! Du kannst den `svc #0`-Test danach wieder entfernen.&lt;br /&gt;
&lt;br /&gt;
## 7. Der IRQ-Handler: Den Zustand retten und wiederherstellen&lt;br /&gt;
&lt;br /&gt;
Wenn ein echter Hardware-Interrupt (z. B. vom Timer) kommt, springt die CPU zu `GenericIRQHandler`. Da der Interrupt *jederzeit* passieren kann, weiß der Handler nicht, welche Register gerade benutzt wurden. &lt;br /&gt;
&lt;br /&gt;
Die goldene Regel lautet: **Rette alles, was du veränderst, und stelle es exakt so wieder her.**&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S (vereinfachte Darstellung des GenericIRQHandler)&lt;br /&gt;
.globl GenericIRQHandler&lt;br /&gt;
GenericIRQHandler:&lt;br /&gt;
    // 1. Frame-Pointer und Link-Register retten&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 2. Rücksprungadresse (elr_el1) und Status (spsr_el1) retten&lt;br /&gt;
    mrs x29, elr_el1&lt;br /&gt;
    mrs x30, spsr_el1&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 3. FIQs erlauben (falls sie gesperrt waren), IRQs bleiben durch Eintritt automatisch gesperrt&lt;br /&gt;
    msr DAIFClr, #1&lt;br /&gt;
    &lt;br /&gt;
    // 4. ALLE General Purpose Register (x0-x28) auf den Stack sichern&lt;br /&gt;
    stp x27, x28, [sp, #-16]!&lt;br /&gt;
    // ... (weitere stp Befehle bis x0) ...&lt;br /&gt;
    str x0, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 5. ALLE NEON/FP-Register (q0-q31) sichern (wichtig für C-Code mit Floats!)&lt;br /&gt;
    stp q30, q31, [sp, #-32]!&lt;br /&gt;
    // ... (weitere stp Befehle bis q0) ...&lt;br /&gt;
    &lt;br /&gt;
    // 6. Jetzt ist der Stack sauber. Wir können sicher C-Code aufrufen!&lt;br /&gt;
    bl InterruptHandler&lt;br /&gt;
    &lt;br /&gt;
    // 7. Epilog: Alles in EXAKT umgekehrter Reihenfolge wiederherstellen!&lt;br /&gt;
    ldp q0, q1, [sp], #32&lt;br /&gt;
    // ... (bis q30, q31)&lt;br /&gt;
    &lt;br /&gt;
    ldr x0, [sp], #16&lt;br /&gt;
    ldp x1, x2, [sp], #16&lt;br /&gt;
    // ... (bis x27, x28)&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    msr elr_el1, x29&lt;br /&gt;
    msr spsr_el1, x30&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    &lt;br /&gt;
    // 8. Zurück zum unterbrochenen Code&lt;br /&gt;
    eret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
## 8. Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
1. **Nur ein Kern:** Wir schicken Core 1-3 in den Schlaf (`WFE`), um Chaos zu vermeiden.&lt;br /&gt;
2. **EL1 ist Pflicht:** Wir wechseln von EL2 nach EL1, um Kernel-Rechte zu haben.&lt;br /&gt;
3. **Vektortabelle:** Sie muss auf 2 KB ausgerichtet sein und zeigt der CPU den Weg zu den Handlern.&lt;br /&gt;
4. **Blindtest:** Ein `svc #0` ist der perfekte, sichere Weg, um zu prüfen, ob Exceptions korrekt abgefangen werden, bevor man Hardware-IRQs konfiguriert.&lt;br /&gt;
5. **State Saving:** Der Assembly-Handler muss *jedes* Register sichern, bevor er C-Code (`InterruptHandler`) aufruft, und es danach exakt wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
## Ausblick&lt;br /&gt;
&lt;br /&gt;
Nun, da das Fundament steht und wir wissen, dass unsere Exception-Tabelle funktioniert, können wir uns im nächsten Kapitel dem **Generic Interrupt Controller (GIC-400)** und dem **System Timer** widmen. Dort werden wir lernen, wie wir einen echten Hardware-Interrupt (IRQ 27) aktivieren, um unseren ersten periodischen &amp;quot;Rotor&amp;quot; auf dem Bildschirm zu animieren!&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Systeminformationen (PI4)|&amp;lt; Zurück (Systeminformationen)]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Beispiel Timer-Interrupt (PI4)|Weiter (Beispiel Timer-Interrupt) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1338</id>
		<title>Interrupts (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1338"/>
		<updated>2026-07-22T10:48:09Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{|&lt;br /&gt;
!Wichtig! Durch die Entwicklung zur USB-Unterstützung wurde einige Funktionen, die wir bisher genutzt haben, überarbeitet. Diese &amp;quot;neuen&amp;quot; Funktionen sind ab hier eingebaut. Darunter zählt zum Beispiel die printf-Funktion, die hier komplett überarbeitet ist. Zusätzlich wurden einige Screen-Funktionen überarbeitet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Interrupts und Exceptions auf dem Raspberry Pi 4 (Bare Metal) ==&lt;br /&gt;
&lt;br /&gt;
Interrupts (Unterbrechungen) und Exceptions (Ausnahmen) sind das Nervensystem jedes Betriebssystems. &lt;br /&gt;
Sie sorgen dafür, dass der Prozessor nicht blind Code abarbeitet, sondern auf Ereignisse reagieren kann – sei es,&lt;br /&gt;
dass ein Timer abläuft, eine Taste gedrückt wird oder ein schwerwiegender Programmierfehler auftritt.&lt;br /&gt;
&lt;br /&gt;
In diesem Kapitel lernst du, wie die Ausnahmebehandlung im ARMv8 (AArch64) funktioniert,&lt;br /&gt;
wie wir die Vektortabelle einrichten und wie wir mit einem einfachen &amp;quot;Blindtest&amp;quot; sicherstellen,&lt;br /&gt;
dass unser System bereit für echte Hardware-Interrupts ist.&lt;br /&gt;
&lt;br /&gt;
== Exceptions vs. Interrupts: Was ist der Unterschied? ==&lt;br /&gt;
&lt;br /&gt;
Obwohl die Begriffe oft synonym verwendet werden, gibt es einen feinen, aber wichtigen Unterschied:&lt;br /&gt;
* Exceptions (Synchron): Treten &#039;&#039;&#039;direkt&#039;&#039;&#039; durch die Ausführung eines Befehls auf. Beispiele: Ein ungültiger Speicherzugriff, eine Division durch Null oder ein bewusster Systemaufruf (wie `svc #0`). Der Prozessor weiß &#039;&#039;&#039;genau&#039;&#039;&#039;, welcher Befehl die Ausnahme ausgelöst hat.&lt;br /&gt;
* Interrupts (Asynchron): Treten &#039;&#039;&#039;unabhängig&#039;&#039;&#039; vom aktuellen Befehl auf, ausgelöst durch externe Hardware. Beispiele: Der System-Timer, eine USB-Maus oder ein GPIO-Pin.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tipp für die OS-Entwicklung&#039;&#039;: Synchronous Exceptions (wie unsere `SynchError`-Handler) sind deine besten Freunde beim Debuggen! Wenn dein Code versehentlich in einen `SynchError`-Handler springt, weißt du sofort: Hier ist ein schwerer Fehler (z. B. Sprung an eine ungültige Adresse oder falscher Befehl). Wir werden das gleich gezielt ausnutzen.&lt;br /&gt;
&lt;br /&gt;
== Die Spielregeln: Exception Levels (EL) und DAIF ==&lt;br /&gt;
&lt;br /&gt;
Der ARM Cortex-A72 kennt verschiedene Privilegienstufen, die &#039;&#039;&#039;Exception Levels (EL)&#039;&#039;&#039;:&lt;br /&gt;
* &#039;&#039;&#039;EL0:&#039;&#039;&#039; User-Mode (Anwendungen, wenig Rechte)&lt;br /&gt;
* &#039;&#039;&#039;EL1:&#039;&#039;&#039; Kernel-Mode (Unser Bare-Metal-Code, volle Hardware-Kontrolle)&lt;br /&gt;
* &#039;&#039;&#039;EL2:&#039;&#039;&#039; Hypervisor (Virtualisierung, wird vom Bootloader kurz genutzt)&lt;br /&gt;
* &#039;&#039;&#039;EL3:&#039;&#039;&#039; Secure Monitor (Highest Privilege, z. B. ARM Trusted Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Um Interrupts korrekt zu verarbeiten, müssen wir uns in &#039;&#039;&#039;EL1&#039;&#039;&#039; befinden. Da der Raspberry Pi 4 oft in EL2 startet, ist unser erster Schritt der Wechsel nach EL1.&lt;br /&gt;
&lt;br /&gt;
Zudem kontrolliert das &#039;&#039;&#039;DAIF&#039;&#039;&#039;-Register, welche Interrupts gerade erlaubt sind:&lt;br /&gt;
* &#039;&#039;&#039;D&#039;&#039;&#039; (Debug): Debug-Exceptions sperren&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; (SError): Asynchrone Systemfehler sperren&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; (IRQ): Normale Interrupts sperren&lt;br /&gt;
* &#039;&#039;&#039;F&#039;&#039;&#039; (FIQ): Schnelle Interrupts (Fast IRQs) sperren&lt;br /&gt;
&lt;br /&gt;
Ist ein Bit auf `1` gesetzt, ist der Interrupt &#039;&#039;&#039;gesperrt&#039;&#039;&#039;. Um IRQs zu erlauben, müssen wir das I-Bit auf `0` setzen (clear).&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Nur ein Kern darf laufen ==&lt;br /&gt;
&lt;br /&gt;
Der Pi 4 hat 4 Kerne. Beim Start führen alle denselben Code aus. Würden alle versuchen, Interrupts zu bearbeiten oder den Stack zu nutzen, würde das System sofort abstürzen. Wir lassen nur &#039;&#039;&#039;Core 0&#039;&#039;&#039; arbeiten und schicken die anderen in einen energiesparenden Schlafmodus (`WFE` = Wait For Event).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In boot.S&lt;br /&gt;
_start:&lt;br /&gt;
    mrs x1, mpidr_el1    // Core-ID lesen&lt;br /&gt;
    and x1, x1, #3       // Nur die unteren 2 Bits (0-3) behalten&lt;br /&gt;
    cbz x1, core0        // Wenn Core 0, weiter zum Setup&lt;br /&gt;
&lt;br /&gt;
core_sleep:&lt;br /&gt;
    wfe                  // Warte auf ein Event (Sleep)&lt;br /&gt;
    b core_sleep         // Endlosschleife für Core 1-3&lt;br /&gt;
&lt;br /&gt;
core0:&lt;br /&gt;
    // Ab hier läuft nur noch Core 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Der Wechsel von EL2 nach EL1 ==&lt;br /&gt;
&lt;br /&gt;
Da wir ein Kernel schreiben, gehört unser Platz in EL1. Der folgende Code konfiguriert EL1, setzt den Stackpointer und bereitet den Rücksprung vor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    // 1. Prüfen, ob wir schon in EL1 sind (CurrentEL = 8 für EL1)&lt;br /&gt;
    mrs x0, CurrentEL&lt;br /&gt;
    cmp x0, #8&lt;br /&gt;
    beq switch_to_el1&lt;br /&gt;
&lt;br /&gt;
    // 2. EL1 Stackpointer setzen&lt;br /&gt;
    ldr x0, =EXCEPTION_STACK&lt;br /&gt;
    msr sp_el1, x0&lt;br /&gt;
&lt;br /&gt;
    // 3. Vektortabelle für EL2 setzen (für den Fall, dass während des Wechsels was passiert)&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el2, x0&lt;br /&gt;
&lt;br /&gt;
    // ... (Hier folgen Timer- und Virtualisierungs-Einstellungen, siehe boot.S) ...&lt;br /&gt;
&lt;br /&gt;
    // 4. Rücksprung-Adresse und Status für EL1 vorbereiten&lt;br /&gt;
    mov x0, #0x3c4             // SPSR_EL2: EL1h, Interrupts (DAIF) sind hier noch maskiert&lt;br /&gt;
    msr spsr_el2, x0&lt;br /&gt;
    adr x0, el1_return         // Adresse, zu der wir nach dem Wechsel springen&lt;br /&gt;
    msr elr_el2, x0&lt;br /&gt;
&lt;br /&gt;
    eret                       // Exception Return: Wechselt nach EL1 zu &#039;el1_return&#039;&lt;br /&gt;
&lt;br /&gt;
el1_return:&lt;br /&gt;
switch_to_el1:&lt;br /&gt;
    // Wir sind jetzt in EL1!&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el1, x0           // Vektortabelle für EL1 aktivieren&lt;br /&gt;
&lt;br /&gt;
    // BSS-Segment (uninitialisierte Variablen) mit Nullen füllen&lt;br /&gt;
    ldr x1, =__bss_start&lt;br /&gt;
    ldr w2, =__bss_size&lt;br /&gt;
clean_bss_loop:&lt;br /&gt;
    cbz w2, bss_clean_done&lt;br /&gt;
    str xzr, [x1], #8&lt;br /&gt;
    sub w2, w2, #1&lt;br /&gt;
    cbnz w2, clean_bss_loop&lt;br /&gt;
bss_clean_done:&lt;br /&gt;
&lt;br /&gt;
    b main                     // Ab ins C-Hauptprogramm!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Exception Vector Table (VBT) ==&lt;br /&gt;
&lt;br /&gt;
Die VBT ist das &amp;quot;Telefonbuch&amp;quot; des Prozessors. Bei einer Ausnahme schaut die CPU in dieses Register (`vbar_el1`) und springt zu der dort hinterlegten Adresse. &lt;br /&gt;
&lt;br /&gt;
Die Tabelle muss exakt &#039;&#039;&#039;2048 Byte (2 KB)&#039;&#039;&#039; groß sein (&#039;&#039;.align 11&#039;&#039;). Sie ist in 16 Einträge unterteilt (4 Ausnahmetypen × 4 Ausführungsstatus). Jeder Eintrag muss exakt &#039;&#039;&#039;128 Byte&#039;&#039;&#039; (&#039;&#039;.align 7&#039;&#039;) voneinander entfernt sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S&lt;br /&gt;
.align 11                      // 2^11 = 2048 Byte Ausrichtung für die ganze Tabelle&lt;br /&gt;
.globl VectorTable&lt;br /&gt;
VectorTable:&lt;br /&gt;
    // 1. Aktuelles EL, Stackpointer ist SP_EL0 (User-Mode, nutzen wir nicht)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub1&lt;br /&gt;
&lt;br /&gt;
    // 2. Aktuelles EL, Stackpointer ist SP_ELx (Kernel-Mode, EL1h) -&amp;gt; DAS NUTZEN WIR!&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError2              // Synchronous Exception (z.B. svc #0, Memory Abort)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler        // Normaler Hardware-Interrupt (IRQ)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler     // FIQ&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub2              // System Error&lt;br /&gt;
&lt;br /&gt;
    // 3. Niedrigeres EL (EL0), 64-Bit (Auch hier leiten wir auf unsere Handler um)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    // ... (weitere Einträge siehe vector.S)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
🛠️ &#039;&#039;&#039;Debugging-Hinweis:&#039;&#039;&#039; Die &#039;&#039;SynchError&#039;&#039;-Handler sind extrem wertvoll. Wenn dein Programm unerwartet in `SynchError2` landet, weißt du: Ein Befehl in EL1 war ungültig oder hat einen Fehler verursacht. Wir werden das gleich gezielt provozieren, um zu prüfen, ob die Tabelle funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Der Blindtest: `svc #0` (Supervisor Call) ==&lt;br /&gt;
&lt;br /&gt;
Bevor wir uns mit komplexer Hardware (wie dem GIC-Interrupt-Controller) herumschlagen, müssen wir sicherstellen, dass unser Exception-Mechanismus grundlegend funktioniert. &lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;svc #0&#039;&#039; (Supervisor Call) löst absichtlich eine &#039;&#039;&#039;synchrone Exception&#039;&#039;&#039; aus. Da wir uns in EL1 befinden, sollte die CPU in den Eintrag &#039;&#039;SynchError2&#039;&#039; unserer Vektortabelle springen.&lt;br /&gt;
&lt;br /&gt;
Füge diesen Test vorübergehend in deine `main.c` ein, *bevor* du `printf` oder andere Initialisierungen machst:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// In main.c&lt;br /&gt;
#include &amp;quot;printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;sync.h&amp;quot; // Für Stop()&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    // --- BLINDTEST FÜR EXCEPTIONS ---&lt;br /&gt;
    printf(&amp;quot;Starte Exception-Blindtest...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Dieser Befehl löst absichtlich eine synchrone Exception aus.&lt;br /&gt;
    // Die CPU sollte in vector.S -&amp;gt; SynchError2 -&amp;gt; ExceptionHandler springen.&lt;br /&gt;
    asm volatile(&amp;quot;svc #0&amp;quot;); &lt;br /&gt;
    &lt;br /&gt;
    // Diese Zeile sollte NICHT erreicht werden!&lt;br /&gt;
    printf(&amp;quot;FEHLER: svc #0 wurde nicht abgefangen!\n&amp;quot;);&lt;br /&gt;
    Stop();&lt;br /&gt;
    // ----------------------------------&lt;br /&gt;
&lt;br /&gt;
    // ... Rest deiner Initialisierung (Interrupt_Initialize, Timer, etc.) ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Erwartetes Ergebnis:**&lt;br /&gt;
Auf dem Bildschirm sollte erscheinen:&lt;br /&gt;
`Starte Exception-Blindtest...`&lt;br /&gt;
`Error: Exception &#039;SynchError2&#039; ausgelöst`&lt;br /&gt;
Und das Programm bleibt stehen (dank `Stop()` im `ExceptionHandler`).&lt;br /&gt;
&lt;br /&gt;
**Wenn das funktioniert, hast du den schwersten Teil bereits geschafft:** Der Wechsel nach EL1, das Setzen von `vbar_el1` und die korrekte Ausrichtung der Vektortabelle sind verifiziert! Du kannst den `svc #0`-Test danach wieder entfernen.&lt;br /&gt;
&lt;br /&gt;
## 7. Der IRQ-Handler: Den Zustand retten und wiederherstellen&lt;br /&gt;
&lt;br /&gt;
Wenn ein echter Hardware-Interrupt (z. B. vom Timer) kommt, springt die CPU zu `GenericIRQHandler`. Da der Interrupt *jederzeit* passieren kann, weiß der Handler nicht, welche Register gerade benutzt wurden. &lt;br /&gt;
&lt;br /&gt;
Die goldene Regel lautet: **Rette alles, was du veränderst, und stelle es exakt so wieder her.**&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S (vereinfachte Darstellung des GenericIRQHandler)&lt;br /&gt;
.globl GenericIRQHandler&lt;br /&gt;
GenericIRQHandler:&lt;br /&gt;
    // 1. Frame-Pointer und Link-Register retten&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 2. Rücksprungadresse (elr_el1) und Status (spsr_el1) retten&lt;br /&gt;
    mrs x29, elr_el1&lt;br /&gt;
    mrs x30, spsr_el1&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 3. FIQs erlauben (falls sie gesperrt waren), IRQs bleiben durch Eintritt automatisch gesperrt&lt;br /&gt;
    msr DAIFClr, #1&lt;br /&gt;
    &lt;br /&gt;
    // 4. ALLE General Purpose Register (x0-x28) auf den Stack sichern&lt;br /&gt;
    stp x27, x28, [sp, #-16]!&lt;br /&gt;
    // ... (weitere stp Befehle bis x0) ...&lt;br /&gt;
    str x0, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 5. ALLE NEON/FP-Register (q0-q31) sichern (wichtig für C-Code mit Floats!)&lt;br /&gt;
    stp q30, q31, [sp, #-32]!&lt;br /&gt;
    // ... (weitere stp Befehle bis q0) ...&lt;br /&gt;
    &lt;br /&gt;
    // 6. Jetzt ist der Stack sauber. Wir können sicher C-Code aufrufen!&lt;br /&gt;
    bl InterruptHandler&lt;br /&gt;
    &lt;br /&gt;
    // 7. Epilog: Alles in EXAKT umgekehrter Reihenfolge wiederherstellen!&lt;br /&gt;
    ldp q0, q1, [sp], #32&lt;br /&gt;
    // ... (bis q30, q31)&lt;br /&gt;
    &lt;br /&gt;
    ldr x0, [sp], #16&lt;br /&gt;
    ldp x1, x2, [sp], #16&lt;br /&gt;
    // ... (bis x27, x28)&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    msr elr_el1, x29&lt;br /&gt;
    msr spsr_el1, x30&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    &lt;br /&gt;
    // 8. Zurück zum unterbrochenen Code&lt;br /&gt;
    eret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
## 8. Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
1. **Nur ein Kern:** Wir schicken Core 1-3 in den Schlaf (`WFE`), um Chaos zu vermeiden.&lt;br /&gt;
2. **EL1 ist Pflicht:** Wir wechseln von EL2 nach EL1, um Kernel-Rechte zu haben.&lt;br /&gt;
3. **Vektortabelle:** Sie muss auf 2 KB ausgerichtet sein und zeigt der CPU den Weg zu den Handlern.&lt;br /&gt;
4. **Blindtest:** Ein `svc #0` ist der perfekte, sichere Weg, um zu prüfen, ob Exceptions korrekt abgefangen werden, bevor man Hardware-IRQs konfiguriert.&lt;br /&gt;
5. **State Saving:** Der Assembly-Handler muss *jedes* Register sichern, bevor er C-Code (`InterruptHandler`) aufruft, und es danach exakt wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
## Ausblick&lt;br /&gt;
&lt;br /&gt;
Nun, da das Fundament steht und wir wissen, dass unsere Exception-Tabelle funktioniert, können wir uns im nächsten Kapitel dem **Generic Interrupt Controller (GIC-400)** und dem **System Timer** widmen. Dort werden wir lernen, wie wir einen echten Hardware-Interrupt (IRQ 27) aktivieren, um unseren ersten periodischen &amp;quot;Rotor&amp;quot; auf dem Bildschirm zu animieren!&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Systeminformationen (PI4)|&amp;lt; Zurück (Systeminformationen)]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Beispiel Timer-Interrupt (PI4)|Weiter (Beispiel Timer-Interrupt) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1337</id>
		<title>Interrupts (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1337"/>
		<updated>2026-07-22T10:47:25Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{|&lt;br /&gt;
!Wichtig! Durch die Entwicklung zur USB-Unterstützung wurde einige Funktionen, die wir bisher genutzt haben, überarbeitet. Diese &amp;quot;neuen&amp;quot; Funktionen sind ab hier eingebaut. Darunter zählt zum Beispiel die printf-Funktion, die hier komplett überarbeitet ist. Zusätzlich wurden einige Screen-Funktionen überarbeitet.&lt;br /&gt;
!&lt;br /&gt;
!&lt;br /&gt;
!&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Interrupts und Exceptions auf dem Raspberry Pi 4 (Bare Metal) ==&lt;br /&gt;
&lt;br /&gt;
Interrupts (Unterbrechungen) und Exceptions (Ausnahmen) sind das Nervensystem jedes Betriebssystems. &lt;br /&gt;
Sie sorgen dafür, dass der Prozessor nicht blind Code abarbeitet, sondern auf Ereignisse reagieren kann – sei es,&lt;br /&gt;
dass ein Timer abläuft, eine Taste gedrückt wird oder ein schwerwiegender Programmierfehler auftritt.&lt;br /&gt;
&lt;br /&gt;
In diesem Kapitel lernst du, wie die Ausnahmebehandlung im ARMv8 (AArch64) funktioniert,&lt;br /&gt;
wie wir die Vektortabelle einrichten und wie wir mit einem einfachen &amp;quot;Blindtest&amp;quot; sicherstellen,&lt;br /&gt;
dass unser System bereit für echte Hardware-Interrupts ist.&lt;br /&gt;
&lt;br /&gt;
== Exceptions vs. Interrupts: Was ist der Unterschied? ==&lt;br /&gt;
&lt;br /&gt;
Obwohl die Begriffe oft synonym verwendet werden, gibt es einen feinen, aber wichtigen Unterschied:&lt;br /&gt;
* Exceptions (Synchron): Treten &#039;&#039;&#039;direkt&#039;&#039;&#039; durch die Ausführung eines Befehls auf. Beispiele: Ein ungültiger Speicherzugriff, eine Division durch Null oder ein bewusster Systemaufruf (wie `svc #0`). Der Prozessor weiß &#039;&#039;&#039;genau&#039;&#039;&#039;, welcher Befehl die Ausnahme ausgelöst hat.&lt;br /&gt;
* Interrupts (Asynchron): Treten &#039;&#039;&#039;unabhängig&#039;&#039;&#039; vom aktuellen Befehl auf, ausgelöst durch externe Hardware. Beispiele: Der System-Timer, eine USB-Maus oder ein GPIO-Pin.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tipp für die OS-Entwicklung&#039;&#039;: Synchronous Exceptions (wie unsere `SynchError`-Handler) sind deine besten Freunde beim Debuggen! Wenn dein Code versehentlich in einen `SynchError`-Handler springt, weißt du sofort: Hier ist ein schwerer Fehler (z. B. Sprung an eine ungültige Adresse oder falscher Befehl). Wir werden das gleich gezielt ausnutzen.&lt;br /&gt;
&lt;br /&gt;
== Die Spielregeln: Exception Levels (EL) und DAIF ==&lt;br /&gt;
&lt;br /&gt;
Der ARM Cortex-A72 kennt verschiedene Privilegienstufen, die &#039;&#039;&#039;Exception Levels (EL)&#039;&#039;&#039;:&lt;br /&gt;
* &#039;&#039;&#039;EL0:&#039;&#039;&#039; User-Mode (Anwendungen, wenig Rechte)&lt;br /&gt;
* &#039;&#039;&#039;EL1:&#039;&#039;&#039; Kernel-Mode (Unser Bare-Metal-Code, volle Hardware-Kontrolle)&lt;br /&gt;
* &#039;&#039;&#039;EL2:&#039;&#039;&#039; Hypervisor (Virtualisierung, wird vom Bootloader kurz genutzt)&lt;br /&gt;
* &#039;&#039;&#039;EL3:&#039;&#039;&#039; Secure Monitor (Highest Privilege, z. B. ARM Trusted Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Um Interrupts korrekt zu verarbeiten, müssen wir uns in &#039;&#039;&#039;EL1&#039;&#039;&#039; befinden. Da der Raspberry Pi 4 oft in EL2 startet, ist unser erster Schritt der Wechsel nach EL1.&lt;br /&gt;
&lt;br /&gt;
Zudem kontrolliert das &#039;&#039;&#039;DAIF&#039;&#039;&#039;-Register, welche Interrupts gerade erlaubt sind:&lt;br /&gt;
* &#039;&#039;&#039;D&#039;&#039;&#039; (Debug): Debug-Exceptions sperren&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; (SError): Asynchrone Systemfehler sperren&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; (IRQ): Normale Interrupts sperren&lt;br /&gt;
* &#039;&#039;&#039;F&#039;&#039;&#039; (FIQ): Schnelle Interrupts (Fast IRQs) sperren&lt;br /&gt;
&lt;br /&gt;
Ist ein Bit auf `1` gesetzt, ist der Interrupt &#039;&#039;&#039;gesperrt&#039;&#039;&#039;. Um IRQs zu erlauben, müssen wir das I-Bit auf `0` setzen (clear).&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Nur ein Kern darf laufen ==&lt;br /&gt;
&lt;br /&gt;
Der Pi 4 hat 4 Kerne. Beim Start führen alle denselben Code aus. Würden alle versuchen, Interrupts zu bearbeiten oder den Stack zu nutzen, würde das System sofort abstürzen. Wir lassen nur &#039;&#039;&#039;Core 0&#039;&#039;&#039; arbeiten und schicken die anderen in einen energiesparenden Schlafmodus (`WFE` = Wait For Event).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In boot.S&lt;br /&gt;
_start:&lt;br /&gt;
    mrs x1, mpidr_el1    // Core-ID lesen&lt;br /&gt;
    and x1, x1, #3       // Nur die unteren 2 Bits (0-3) behalten&lt;br /&gt;
    cbz x1, core0        // Wenn Core 0, weiter zum Setup&lt;br /&gt;
&lt;br /&gt;
core_sleep:&lt;br /&gt;
    wfe                  // Warte auf ein Event (Sleep)&lt;br /&gt;
    b core_sleep         // Endlosschleife für Core 1-3&lt;br /&gt;
&lt;br /&gt;
core0:&lt;br /&gt;
    // Ab hier läuft nur noch Core 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Der Wechsel von EL2 nach EL1 ==&lt;br /&gt;
&lt;br /&gt;
Da wir ein Kernel schreiben, gehört unser Platz in EL1. Der folgende Code konfiguriert EL1, setzt den Stackpointer und bereitet den Rücksprung vor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    // 1. Prüfen, ob wir schon in EL1 sind (CurrentEL = 8 für EL1)&lt;br /&gt;
    mrs x0, CurrentEL&lt;br /&gt;
    cmp x0, #8&lt;br /&gt;
    beq switch_to_el1&lt;br /&gt;
&lt;br /&gt;
    // 2. EL1 Stackpointer setzen&lt;br /&gt;
    ldr x0, =EXCEPTION_STACK&lt;br /&gt;
    msr sp_el1, x0&lt;br /&gt;
&lt;br /&gt;
    // 3. Vektortabelle für EL2 setzen (für den Fall, dass während des Wechsels was passiert)&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el2, x0&lt;br /&gt;
&lt;br /&gt;
    // ... (Hier folgen Timer- und Virtualisierungs-Einstellungen, siehe boot.S) ...&lt;br /&gt;
&lt;br /&gt;
    // 4. Rücksprung-Adresse und Status für EL1 vorbereiten&lt;br /&gt;
    mov x0, #0x3c4             // SPSR_EL2: EL1h, Interrupts (DAIF) sind hier noch maskiert&lt;br /&gt;
    msr spsr_el2, x0&lt;br /&gt;
    adr x0, el1_return         // Adresse, zu der wir nach dem Wechsel springen&lt;br /&gt;
    msr elr_el2, x0&lt;br /&gt;
&lt;br /&gt;
    eret                       // Exception Return: Wechselt nach EL1 zu &#039;el1_return&#039;&lt;br /&gt;
&lt;br /&gt;
el1_return:&lt;br /&gt;
switch_to_el1:&lt;br /&gt;
    // Wir sind jetzt in EL1!&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el1, x0           // Vektortabelle für EL1 aktivieren&lt;br /&gt;
&lt;br /&gt;
    // BSS-Segment (uninitialisierte Variablen) mit Nullen füllen&lt;br /&gt;
    ldr x1, =__bss_start&lt;br /&gt;
    ldr w2, =__bss_size&lt;br /&gt;
clean_bss_loop:&lt;br /&gt;
    cbz w2, bss_clean_done&lt;br /&gt;
    str xzr, [x1], #8&lt;br /&gt;
    sub w2, w2, #1&lt;br /&gt;
    cbnz w2, clean_bss_loop&lt;br /&gt;
bss_clean_done:&lt;br /&gt;
&lt;br /&gt;
    b main                     // Ab ins C-Hauptprogramm!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Exception Vector Table (VBT) ==&lt;br /&gt;
&lt;br /&gt;
Die VBT ist das &amp;quot;Telefonbuch&amp;quot; des Prozessors. Bei einer Ausnahme schaut die CPU in dieses Register (`vbar_el1`) und springt zu der dort hinterlegten Adresse. &lt;br /&gt;
&lt;br /&gt;
Die Tabelle muss exakt &#039;&#039;&#039;2048 Byte (2 KB)&#039;&#039;&#039; groß sein (&#039;&#039;.align 11&#039;&#039;). Sie ist in 16 Einträge unterteilt (4 Ausnahmetypen × 4 Ausführungsstatus). Jeder Eintrag muss exakt &#039;&#039;&#039;128 Byte&#039;&#039;&#039; (&#039;&#039;.align 7&#039;&#039;) voneinander entfernt sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S&lt;br /&gt;
.align 11                      // 2^11 = 2048 Byte Ausrichtung für die ganze Tabelle&lt;br /&gt;
.globl VectorTable&lt;br /&gt;
VectorTable:&lt;br /&gt;
    // 1. Aktuelles EL, Stackpointer ist SP_EL0 (User-Mode, nutzen wir nicht)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub1&lt;br /&gt;
&lt;br /&gt;
    // 2. Aktuelles EL, Stackpointer ist SP_ELx (Kernel-Mode, EL1h) -&amp;gt; DAS NUTZEN WIR!&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError2              // Synchronous Exception (z.B. svc #0, Memory Abort)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler        // Normaler Hardware-Interrupt (IRQ)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler     // FIQ&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub2              // System Error&lt;br /&gt;
&lt;br /&gt;
    // 3. Niedrigeres EL (EL0), 64-Bit (Auch hier leiten wir auf unsere Handler um)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    // ... (weitere Einträge siehe vector.S)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
🛠️ &#039;&#039;&#039;Debugging-Hinweis:&#039;&#039;&#039; Die &#039;&#039;SynchError&#039;&#039;-Handler sind extrem wertvoll. Wenn dein Programm unerwartet in `SynchError2` landet, weißt du: Ein Befehl in EL1 war ungültig oder hat einen Fehler verursacht. Wir werden das gleich gezielt provozieren, um zu prüfen, ob die Tabelle funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Der Blindtest: `svc #0` (Supervisor Call) ==&lt;br /&gt;
&lt;br /&gt;
Bevor wir uns mit komplexer Hardware (wie dem GIC-Interrupt-Controller) herumschlagen, müssen wir sicherstellen, dass unser Exception-Mechanismus grundlegend funktioniert. &lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;svc #0&#039;&#039; (Supervisor Call) löst absichtlich eine &#039;&#039;&#039;synchrone Exception&#039;&#039;&#039; aus. Da wir uns in EL1 befinden, sollte die CPU in den Eintrag &#039;&#039;SynchError2&#039;&#039; unserer Vektortabelle springen.&lt;br /&gt;
&lt;br /&gt;
Füge diesen Test vorübergehend in deine `main.c` ein, *bevor* du `printf` oder andere Initialisierungen machst:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// In main.c&lt;br /&gt;
#include &amp;quot;printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;sync.h&amp;quot; // Für Stop()&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    // --- BLINDTEST FÜR EXCEPTIONS ---&lt;br /&gt;
    printf(&amp;quot;Starte Exception-Blindtest...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Dieser Befehl löst absichtlich eine synchrone Exception aus.&lt;br /&gt;
    // Die CPU sollte in vector.S -&amp;gt; SynchError2 -&amp;gt; ExceptionHandler springen.&lt;br /&gt;
    asm volatile(&amp;quot;svc #0&amp;quot;); &lt;br /&gt;
    &lt;br /&gt;
    // Diese Zeile sollte NICHT erreicht werden!&lt;br /&gt;
    printf(&amp;quot;FEHLER: svc #0 wurde nicht abgefangen!\n&amp;quot;);&lt;br /&gt;
    Stop();&lt;br /&gt;
    // ----------------------------------&lt;br /&gt;
&lt;br /&gt;
    // ... Rest deiner Initialisierung (Interrupt_Initialize, Timer, etc.) ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Erwartetes Ergebnis:**&lt;br /&gt;
Auf dem Bildschirm sollte erscheinen:&lt;br /&gt;
`Starte Exception-Blindtest...`&lt;br /&gt;
`Error: Exception &#039;SynchError2&#039; ausgelöst`&lt;br /&gt;
Und das Programm bleibt stehen (dank `Stop()` im `ExceptionHandler`).&lt;br /&gt;
&lt;br /&gt;
**Wenn das funktioniert, hast du den schwersten Teil bereits geschafft:** Der Wechsel nach EL1, das Setzen von `vbar_el1` und die korrekte Ausrichtung der Vektortabelle sind verifiziert! Du kannst den `svc #0`-Test danach wieder entfernen.&lt;br /&gt;
&lt;br /&gt;
## 7. Der IRQ-Handler: Den Zustand retten und wiederherstellen&lt;br /&gt;
&lt;br /&gt;
Wenn ein echter Hardware-Interrupt (z. B. vom Timer) kommt, springt die CPU zu `GenericIRQHandler`. Da der Interrupt *jederzeit* passieren kann, weiß der Handler nicht, welche Register gerade benutzt wurden. &lt;br /&gt;
&lt;br /&gt;
Die goldene Regel lautet: **Rette alles, was du veränderst, und stelle es exakt so wieder her.**&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S (vereinfachte Darstellung des GenericIRQHandler)&lt;br /&gt;
.globl GenericIRQHandler&lt;br /&gt;
GenericIRQHandler:&lt;br /&gt;
    // 1. Frame-Pointer und Link-Register retten&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 2. Rücksprungadresse (elr_el1) und Status (spsr_el1) retten&lt;br /&gt;
    mrs x29, elr_el1&lt;br /&gt;
    mrs x30, spsr_el1&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 3. FIQs erlauben (falls sie gesperrt waren), IRQs bleiben durch Eintritt automatisch gesperrt&lt;br /&gt;
    msr DAIFClr, #1&lt;br /&gt;
    &lt;br /&gt;
    // 4. ALLE General Purpose Register (x0-x28) auf den Stack sichern&lt;br /&gt;
    stp x27, x28, [sp, #-16]!&lt;br /&gt;
    // ... (weitere stp Befehle bis x0) ...&lt;br /&gt;
    str x0, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 5. ALLE NEON/FP-Register (q0-q31) sichern (wichtig für C-Code mit Floats!)&lt;br /&gt;
    stp q30, q31, [sp, #-32]!&lt;br /&gt;
    // ... (weitere stp Befehle bis q0) ...&lt;br /&gt;
    &lt;br /&gt;
    // 6. Jetzt ist der Stack sauber. Wir können sicher C-Code aufrufen!&lt;br /&gt;
    bl InterruptHandler&lt;br /&gt;
    &lt;br /&gt;
    // 7. Epilog: Alles in EXAKT umgekehrter Reihenfolge wiederherstellen!&lt;br /&gt;
    ldp q0, q1, [sp], #32&lt;br /&gt;
    // ... (bis q30, q31)&lt;br /&gt;
    &lt;br /&gt;
    ldr x0, [sp], #16&lt;br /&gt;
    ldp x1, x2, [sp], #16&lt;br /&gt;
    // ... (bis x27, x28)&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    msr elr_el1, x29&lt;br /&gt;
    msr spsr_el1, x30&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    &lt;br /&gt;
    // 8. Zurück zum unterbrochenen Code&lt;br /&gt;
    eret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
## 8. Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
1. **Nur ein Kern:** Wir schicken Core 1-3 in den Schlaf (`WFE`), um Chaos zu vermeiden.&lt;br /&gt;
2. **EL1 ist Pflicht:** Wir wechseln von EL2 nach EL1, um Kernel-Rechte zu haben.&lt;br /&gt;
3. **Vektortabelle:** Sie muss auf 2 KB ausgerichtet sein und zeigt der CPU den Weg zu den Handlern.&lt;br /&gt;
4. **Blindtest:** Ein `svc #0` ist der perfekte, sichere Weg, um zu prüfen, ob Exceptions korrekt abgefangen werden, bevor man Hardware-IRQs konfiguriert.&lt;br /&gt;
5. **State Saving:** Der Assembly-Handler muss *jedes* Register sichern, bevor er C-Code (`InterruptHandler`) aufruft, und es danach exakt wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
## Ausblick&lt;br /&gt;
&lt;br /&gt;
Nun, da das Fundament steht und wir wissen, dass unsere Exception-Tabelle funktioniert, können wir uns im nächsten Kapitel dem **Generic Interrupt Controller (GIC-400)** und dem **System Timer** widmen. Dort werden wir lernen, wie wir einen echten Hardware-Interrupt (IRQ 27) aktivieren, um unseren ersten periodischen &amp;quot;Rotor&amp;quot; auf dem Bildschirm zu animieren!&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Systeminformationen (PI4)|&amp;lt; Zurück (Systeminformationen)]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Beispiel Timer-Interrupt (PI4)|Weiter (Beispiel Timer-Interrupt) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Beispiel_Timer-Interrupt_(PI4)&amp;diff=1336</id>
		<title>Beispiel Timer-Interrupt (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Beispiel_Timer-Interrupt_(PI4)&amp;diff=1336"/>
		<updated>2026-07-21T13:38:23Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
# Kapitel: Der erste echte Hardware-Interrupt – Der System-Timer (Rotor)&lt;br /&gt;
&lt;br /&gt;
Nachdem wir im letzten Kapitel die Vektortabelle (`vector.S`) aufgebaut und den Blindtest mit `svc #0` erfolgreich bestanden haben, wissen wir: Unser Prozessor kann auf Ausnahmen reagieren. &lt;br /&gt;
&lt;br /&gt;
Jetzt machen wir den nächsten großen Schritt: Wir verbinden eine **echte Hardware** mit unserem Code. Wir richten den ARM System Timer so ein, dass er alle 250 Millisekunden einen Interrupt auslöst. Als visuelle Bestätigung zeichnen wir einen kleinen &amp;quot;Rotor&amp;quot; (`/`, `-`, `\`, `|`) in die obere rechte Ecke des Bildschirms.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel zeigt dir, wie die drei Komponenten zusammenspielen:&lt;br /&gt;
1. Der **ARM Virtual Timer** (erzeugt das Signal).&lt;br /&gt;
2. Der **GIC-400** (Generic Interrupt Controller, verteilt das Signal an die CPU).&lt;br /&gt;
3. Unser **C-Handler** (verarbeitet das Signal und zeichnet den Rotor).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 1. Der Generic Interrupt Controller (GIC-400)&lt;br /&gt;
&lt;br /&gt;
Der ARM-Prozessor selbst weiß nicht, welches Peripheral (Timer, USB, UART) einen Interrupt auslöst. Dafür gibt es den GIC. Er sitzt zwischen der Hardware und der CPU. &lt;br /&gt;
&lt;br /&gt;
In deiner `interrupt.c` gibt es bereits die Funktion `Interrupt_Initialize()`. Sie macht Folgendes:&lt;br /&gt;
1. Sie schaltet den GIC-Distributor kurz ab.&lt;br /&gt;
2. Sie setzt alle Interrupts auf &amp;quot;Level-Triggered&amp;quot; und weist sie standardmäßig CPU 0 zu.&lt;br /&gt;
3. Sie aktiviert die CPU-Schnittstelle (`GICC_CTLR`) und erlaubt Interrupts.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; 💡 **Merke:** Der GIC muss *einmalig* zu Beginn initialisiert werden. Danach können wir gezielt einzelne Interrupt-Quellen &amp;quot;freischalten&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 2. Den Virtual Timer aktivieren&lt;br /&gt;
&lt;br /&gt;
ARMv8-Prozessoren haben zwei Timer pro Kern: den *Physical Timer* (`cntp`) und den *Virtual Timer* (`cntv`). Für Betriebssysteme (EL1) wird fast immer der **Virtual Timer** verwendet, da das Hypervisor (EL2) ihn transparent verwalten kann, ohne dass unser Kernel-Code davon etwas merkt.&lt;br /&gt;
&lt;br /&gt;
In `timer.c` findest du zwei entscheidende Funktionen:&lt;br /&gt;
&lt;br /&gt;
```c&lt;br /&gt;
void EnableVirtualTimer(void)&lt;br /&gt;
{&lt;br /&gt;
    u64 cntkctl;&lt;br /&gt;
    asm volatile(&amp;quot;mrs %0, cntkctl_el1&amp;quot; : &amp;quot;=r&amp;quot;(cntkctl));&lt;br /&gt;
    &lt;br /&gt;
    // Erlaube EL1 den Zugriff auf den Physical Count und den Virtual Timer&lt;br /&gt;
    cntkctl |= (1 &amp;lt;&amp;lt; 0);  // EL1PCTEN&lt;br /&gt;
    cntkctl |= (1 &amp;lt;&amp;lt; 1);  // EL1PCEN&lt;br /&gt;
    cntkctl |= (1 &amp;lt;&amp;lt; 2);  // EL1VTEN&lt;br /&gt;
    &lt;br /&gt;
    asm volatile(&amp;quot;msr cntkctl_el1, %0&amp;quot; :: &amp;quot;r&amp;quot;(cntkctl));&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
Diese Funktion schaltet quasi den &amp;quot;Hahn&amp;quot; auf, damit unser Code den Timer überhaupt benutzen darf.&lt;br /&gt;
&lt;br /&gt;
Als Nächstes konfigurieren wir die Auslösezeit:&lt;br /&gt;
&lt;br /&gt;
```c&lt;br /&gt;
void InitRotorTimer(void)&lt;br /&gt;
{&lt;br /&gt;
    // 1. Frequenz des Timers auslesen (beim Pi 4 meist 54.000.000 Hz)&lt;br /&gt;
    u64 freq;&lt;br /&gt;
    asm volatile(&amp;quot;mrs %0, cntfrq_el0&amp;quot; : &amp;quot;=r&amp;quot;(freq));&lt;br /&gt;
    &lt;br /&gt;
    // 2. Ziel: Alle 250 ms (freq / 4 Takte)&lt;br /&gt;
    u64 ticks_per_250ms = freq / 4;&lt;br /&gt;
    &lt;br /&gt;
    // 3. Timer-Wert setzen (Countdown beginnt)&lt;br /&gt;
    asm volatile(&amp;quot;msr cntv_tval_el0, %0&amp;quot; :: &amp;quot;r&amp;quot;(ticks_per_250ms));&lt;br /&gt;
    &lt;br /&gt;
    // 4. Timer aktivieren: Bit 0 = Enable, Bit 1 = Interrupt Mask (0 = erlaubt)&lt;br /&gt;
    asm volatile(&amp;quot;msr cntv_ctl_el0, %0&amp;quot; :: &amp;quot;r&amp;quot;(1));&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&amp;gt; ⚠️ **Wichtig:** Der ARM Timer ist ein *Countdown*-Timer. Wenn er 0 erreicht, löst er den Interrupt aus und **stoppt**. Wir müssen ihn im Handler also immer wieder neu laden!&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 3. Die Interrupt-Verbindung herstellen (IRQ 27)&lt;br /&gt;
&lt;br /&gt;
Der Virtual Timer löst standardmäßig den Interrupt mit der ID **27** (`ARM_IRQLOCAL0_CNTPNS`) aus. Wir müssen dem GIC nun zwei Dinge sagen:&lt;br /&gt;
1. &amp;quot;Schicke IRQ 27 an CPU 0.&amp;quot;&lt;br /&gt;
2. &amp;quot;Wenn IRQ 27 kommt, rufe diese C-Funktion auf.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das erledigen wir mit diesen beiden Funktionen:&lt;br /&gt;
&lt;br /&gt;
```c&lt;br /&gt;
void SetIRQ27Target(void)&lt;br /&gt;
{&lt;br /&gt;
    // Lese das Target-Register für IRQs 24-31&lt;br /&gt;
    u32 reg = read32(GICD_ITARGETSR0 + 4*(27/4));  &lt;br /&gt;
    &lt;br /&gt;
    // Lösche die Bits für IRQ 27 und setze sie auf CPU 0 (Wert 1)&lt;br /&gt;
    reg &amp;amp;= ~(0xFF &amp;lt;&amp;lt; 24);            &lt;br /&gt;
    reg |= (1 &amp;lt;&amp;lt; 24);                &lt;br /&gt;
    &lt;br /&gt;
    // Zurückschreiben und sicherstellen, dass die Änderung wirksam wird&lt;br /&gt;
    write32(reg, GICD_ITARGETSR0 + 4*(27/4));&lt;br /&gt;
    DataSyncBarrier();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Und das Verbinden der Funktion:&lt;br /&gt;
```c&lt;br /&gt;
// Registriere RotorTimerHandler für IRQ 27&lt;br /&gt;
Interrupt_ConnectIRQ(27, RotorTimerHandler, NULL);&lt;br /&gt;
&lt;br /&gt;
// Schalte IRQ 27 im GIC Distributor scharf&lt;br /&gt;
write32(1 &amp;lt;&amp;lt; 27, GICD_ISENABLER0);&lt;br /&gt;
DataSyncBarrier();&lt;br /&gt;
```&lt;br /&gt;
*Hinweis:* `Interrupt_ConnectIRQ` speichert den Zeiger auf `RotorTimerHandler` in einem Array (`s_Interrupt_apIRQHandler`). Wenn der Interrupt kommt, schaut dein zentraler `InterruptHandler` in `interrupt.c` genau dort nach und ruft deine Funktion auf.&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 4. Der Interrupt-Handler: `RotorTimerHandler`&lt;br /&gt;
&lt;br /&gt;
Hier passiert die Magie. Diese Funktion wird asynchron aufgerufen, egal was die `main()`-Schleife gerade tut.&lt;br /&gt;
&lt;br /&gt;
```c&lt;br /&gt;
void RotorTimerHandler(void* pParam)&lt;br /&gt;
{&lt;br /&gt;
    // 1. Die Rotor-Animation zeichnen&lt;br /&gt;
    static int rotor = 0;&lt;br /&gt;
    char ch;&lt;br /&gt;
    &lt;br /&gt;
    switch (rotor % 4) {&lt;br /&gt;
        case 0: ch = &#039;/&#039;; break;&lt;br /&gt;
        case 1: ch = &#039;-&#039;; break;&lt;br /&gt;
        case 2: ch = &#039;\\&#039;; break; // Beachte das Escape-Zeichen \&lt;br /&gt;
        case 3: ch = &#039;|&#039;; break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Zeichne an Position X-8, Y=0 (oben rechts)&lt;br /&gt;
    DrawChar(ch, SCREEN_X - 8, 0);&lt;br /&gt;
    rotor++;&lt;br /&gt;
    &lt;br /&gt;
    // 2. WICHTIG: Timer für die nächste Runde neu laden!&lt;br /&gt;
    u64 freq;&lt;br /&gt;
    asm volatile(&amp;quot;mrs %0, cntfrq_el0&amp;quot; : &amp;quot;=r&amp;quot;(freq));&lt;br /&gt;
    asm volatile(&amp;quot;msr cntv_tval_el0, %0&amp;quot; :: &amp;quot;r&amp;quot;(freq / 4));&lt;br /&gt;
    &lt;br /&gt;
    // Hinweis: Das Senden des &amp;quot;End of Interrupt&amp;quot; (EOIR) an den GIC &lt;br /&gt;
    // wird bereits automatisch von unserem zentralen InterruptHandler &lt;br /&gt;
    // in interrupt.c erledigt, bevor diese Funktion aufgerufen wird!&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
&amp;gt; 🛡️ **Warum kein `GICC_EOIR` hier?**  &lt;br /&gt;
&amp;gt; In älteren Tutorials siehst du oft `write32(irq, GICC_EOIR);` im Handler. Unser Code ist besser: Die Assembly-Routine `GenericIRQHandler` in `vector.S` ruft `InterruptHandler` auf, und *dieser* erledigt das `EOIR` zentral, *nachdem* deine `RotorTimerHandler`-Funktion fertig ist. Das verhindert, dass du es vergisst (was zu einem sofortigen Systemabsturz durch &amp;quot;Interrupt Storms&amp;quot; führen würde).&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 5. Alles in `main.c` zusammenfügen&lt;br /&gt;
&lt;br /&gt;
Damit der Rotor sich dreht, müssen die Initialisierungen in der richtigen Reihenfolge in deiner `main.c` (oder `Kernel_Initialize`) stehen:&lt;br /&gt;
&lt;br /&gt;
```c&lt;br /&gt;
void Kernel_Initialize(CKernel *pKernel)&lt;br /&gt;
{&lt;br /&gt;
    // 1. Basis-Initialisierung (Bildschirm, Terminal, printf)&lt;br /&gt;
    Init_Screen();&lt;br /&gt;
    InitTerminal();&lt;br /&gt;
    SetFrontColor(satyr);&lt;br /&gt;
    printf(&amp;quot;System startet...\n&amp;quot;);&lt;br /&gt;
    SetFrontColor(white);&lt;br /&gt;
&lt;br /&gt;
    // 2. Interrupt-Subsystem initialisieren (GIC-400)&lt;br /&gt;
    Interrupt_Initialize();&lt;br /&gt;
    &lt;br /&gt;
    // 3. Timer vorbereiten&lt;br /&gt;
    EnableVirtualTimer();&lt;br /&gt;
    &lt;br /&gt;
    // 4. IRQ 27 an CPU 0 routen und scharf schalten&lt;br /&gt;
    SetIRQ27Target();&lt;br /&gt;
    write32(1 &amp;lt;&amp;lt; 27, GICD_ISENABLER0);&lt;br /&gt;
    DataSyncBarrier();&lt;br /&gt;
    &lt;br /&gt;
    // 5. Handler registrieren&lt;br /&gt;
    Interrupt_ConnectIRQ(27, RotorTimerHandler, NULL);&lt;br /&gt;
    &lt;br /&gt;
    // 6. Timer starten (ab jetzt kommen alle 250ms Interrupts)&lt;br /&gt;
    InitRotorTimer();&lt;br /&gt;
    &lt;br /&gt;
    printf(&amp;quot;Rotor sollte oben rechts rotieren...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // 7. Hauptschleife (kann leer sein, der Interrupt läuft im Hintergrund!)&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Optional: CPU in energiesparenden Zustand versetzen, &lt;br /&gt;
        // bis der nächste Interrupt kommt&lt;br /&gt;
        asm volatile(&amp;quot;wfi&amp;quot;); // Wait For Interrupt&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## Zusammenfassung &amp;amp; Lernkontrolle&lt;br /&gt;
&lt;br /&gt;
Du hast soeben einen vollständigen, produktionsreifen Interrupt-Pfad implementiert:&lt;br /&gt;
1. **Hardware** (Timer) zählt herunter.&lt;br /&gt;
2. **GIC** fängt das Signal ab und reicht es an CPU 0 weiter.&lt;br /&gt;
3. **CPU** unterbricht den aktuellen Code, springt in `vector.S` -&amp;gt; `GenericIRQHandler`.&lt;br /&gt;
4. **Handler** sichert alle Register (inkl. NEON), ruft `InterruptHandler` auf.&lt;br /&gt;
5. **Dispatcher** findet `RotorTimerHandler` in der Tabelle und führt ihn aus.&lt;br /&gt;
6. **Rotor** wird gezeichnet, Timer neu geladen.&lt;br /&gt;
7. **Rücksprung:** Register werden wiederhergestellt, `eret` kehrt zum unterbrochenen Code zurück.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Interrupts (PI4)|&amp;lt; Zurück (Interrupt)]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Interrupt Teil 2 (PI4)|Weiter (Interrupt Teil 2 (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1335</id>
		<title>Interrupts (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1335"/>
		<updated>2026-07-21T05:33:20Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Interrupts und Exceptions auf dem Raspberry Pi 4 (Bare Metal) ==&lt;br /&gt;
&lt;br /&gt;
Interrupts (Unterbrechungen) und Exceptions (Ausnahmen) sind das Nervensystem jedes Betriebssystems. &lt;br /&gt;
Sie sorgen dafür, dass der Prozessor nicht blind Code abarbeitet, sondern auf Ereignisse reagieren kann – sei es,&lt;br /&gt;
dass ein Timer abläuft, eine Taste gedrückt wird oder ein schwerwiegender Programmierfehler auftritt.&lt;br /&gt;
&lt;br /&gt;
In diesem Kapitel lernst du, wie die Ausnahmebehandlung im ARMv8 (AArch64) funktioniert,&lt;br /&gt;
wie wir die Vektortabelle einrichten und wie wir mit einem einfachen &amp;quot;Blindtest&amp;quot; sicherstellen,&lt;br /&gt;
dass unser System bereit für echte Hardware-Interrupts ist.&lt;br /&gt;
&lt;br /&gt;
== Exceptions vs. Interrupts: Was ist der Unterschied? ==&lt;br /&gt;
&lt;br /&gt;
Obwohl die Begriffe oft synonym verwendet werden, gibt es einen feinen, aber wichtigen Unterschied:&lt;br /&gt;
* Exceptions (Synchron): Treten &#039;&#039;&#039;direkt&#039;&#039;&#039; durch die Ausführung eines Befehls auf. Beispiele: Ein ungültiger Speicherzugriff, eine Division durch Null oder ein bewusster Systemaufruf (wie `svc #0`). Der Prozessor weiß &#039;&#039;&#039;genau&#039;&#039;&#039;, welcher Befehl die Ausnahme ausgelöst hat.&lt;br /&gt;
* Interrupts (Asynchron): Treten &#039;&#039;&#039;unabhängig&#039;&#039;&#039; vom aktuellen Befehl auf, ausgelöst durch externe Hardware. Beispiele: Der System-Timer, eine USB-Maus oder ein GPIO-Pin.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tipp für die OS-Entwicklung&#039;&#039;: Synchronous Exceptions (wie unsere `SynchError`-Handler) sind deine besten Freunde beim Debuggen! Wenn dein Code versehentlich in einen `SynchError`-Handler springt, weißt du sofort: Hier ist ein schwerer Fehler (z. B. Sprung an eine ungültige Adresse oder falscher Befehl). Wir werden das gleich gezielt ausnutzen.&lt;br /&gt;
&lt;br /&gt;
== Die Spielregeln: Exception Levels (EL) und DAIF ==&lt;br /&gt;
&lt;br /&gt;
Der ARM Cortex-A72 kennt verschiedene Privilegienstufen, die &#039;&#039;&#039;Exception Levels (EL)&#039;&#039;&#039;:&lt;br /&gt;
* &#039;&#039;&#039;EL0:&#039;&#039;&#039; User-Mode (Anwendungen, wenig Rechte)&lt;br /&gt;
* &#039;&#039;&#039;EL1:&#039;&#039;&#039; Kernel-Mode (Unser Bare-Metal-Code, volle Hardware-Kontrolle)&lt;br /&gt;
* &#039;&#039;&#039;EL2:&#039;&#039;&#039; Hypervisor (Virtualisierung, wird vom Bootloader kurz genutzt)&lt;br /&gt;
* &#039;&#039;&#039;EL3:&#039;&#039;&#039; Secure Monitor (Highest Privilege, z. B. ARM Trusted Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Um Interrupts korrekt zu verarbeiten, müssen wir uns in &#039;&#039;&#039;EL1&#039;&#039;&#039; befinden. Da der Raspberry Pi 4 oft in EL2 startet, ist unser erster Schritt der Wechsel nach EL1.&lt;br /&gt;
&lt;br /&gt;
Zudem kontrolliert das &#039;&#039;&#039;DAIF&#039;&#039;&#039;-Register, welche Interrupts gerade erlaubt sind:&lt;br /&gt;
* &#039;&#039;&#039;D&#039;&#039;&#039; (Debug): Debug-Exceptions sperren&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; (SError): Asynchrone Systemfehler sperren&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; (IRQ): Normale Interrupts sperren&lt;br /&gt;
* &#039;&#039;&#039;F&#039;&#039;&#039; (FIQ): Schnelle Interrupts (Fast IRQs) sperren&lt;br /&gt;
&lt;br /&gt;
Ist ein Bit auf `1` gesetzt, ist der Interrupt &#039;&#039;&#039;gesperrt&#039;&#039;&#039;. Um IRQs zu erlauben, müssen wir das I-Bit auf `0` setzen (clear).&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Nur ein Kern darf laufen ==&lt;br /&gt;
&lt;br /&gt;
Der Pi 4 hat 4 Kerne. Beim Start führen alle denselben Code aus. Würden alle versuchen, Interrupts zu bearbeiten oder den Stack zu nutzen, würde das System sofort abstürzen. Wir lassen nur &#039;&#039;&#039;Core 0&#039;&#039;&#039; arbeiten und schicken die anderen in einen energiesparenden Schlafmodus (`WFE` = Wait For Event).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In boot.S&lt;br /&gt;
_start:&lt;br /&gt;
    mrs x1, mpidr_el1    // Core-ID lesen&lt;br /&gt;
    and x1, x1, #3       // Nur die unteren 2 Bits (0-3) behalten&lt;br /&gt;
    cbz x1, core0        // Wenn Core 0, weiter zum Setup&lt;br /&gt;
&lt;br /&gt;
core_sleep:&lt;br /&gt;
    wfe                  // Warte auf ein Event (Sleep)&lt;br /&gt;
    b core_sleep         // Endlosschleife für Core 1-3&lt;br /&gt;
&lt;br /&gt;
core0:&lt;br /&gt;
    // Ab hier läuft nur noch Core 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Der Wechsel von EL2 nach EL1 ==&lt;br /&gt;
&lt;br /&gt;
Da wir ein Kernel schreiben, gehört unser Platz in EL1. Der folgende Code konfiguriert EL1, setzt den Stackpointer und bereitet den Rücksprung vor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    // 1. Prüfen, ob wir schon in EL1 sind (CurrentEL = 8 für EL1)&lt;br /&gt;
    mrs x0, CurrentEL&lt;br /&gt;
    cmp x0, #8&lt;br /&gt;
    beq switch_to_el1&lt;br /&gt;
&lt;br /&gt;
    // 2. EL1 Stackpointer setzen&lt;br /&gt;
    ldr x0, =EXCEPTION_STACK&lt;br /&gt;
    msr sp_el1, x0&lt;br /&gt;
&lt;br /&gt;
    // 3. Vektortabelle für EL2 setzen (für den Fall, dass während des Wechsels was passiert)&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el2, x0&lt;br /&gt;
&lt;br /&gt;
    // ... (Hier folgen Timer- und Virtualisierungs-Einstellungen, siehe boot.S) ...&lt;br /&gt;
&lt;br /&gt;
    // 4. Rücksprung-Adresse und Status für EL1 vorbereiten&lt;br /&gt;
    mov x0, #0x3c4             // SPSR_EL2: EL1h, Interrupts (DAIF) sind hier noch maskiert&lt;br /&gt;
    msr spsr_el2, x0&lt;br /&gt;
    adr x0, el1_return         // Adresse, zu der wir nach dem Wechsel springen&lt;br /&gt;
    msr elr_el2, x0&lt;br /&gt;
&lt;br /&gt;
    eret                       // Exception Return: Wechselt nach EL1 zu &#039;el1_return&#039;&lt;br /&gt;
&lt;br /&gt;
el1_return:&lt;br /&gt;
switch_to_el1:&lt;br /&gt;
    // Wir sind jetzt in EL1!&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el1, x0           // Vektortabelle für EL1 aktivieren&lt;br /&gt;
&lt;br /&gt;
    // BSS-Segment (uninitialisierte Variablen) mit Nullen füllen&lt;br /&gt;
    ldr x1, =__bss_start&lt;br /&gt;
    ldr w2, =__bss_size&lt;br /&gt;
clean_bss_loop:&lt;br /&gt;
    cbz w2, bss_clean_done&lt;br /&gt;
    str xzr, [x1], #8&lt;br /&gt;
    sub w2, w2, #1&lt;br /&gt;
    cbnz w2, clean_bss_loop&lt;br /&gt;
bss_clean_done:&lt;br /&gt;
&lt;br /&gt;
    b main                     // Ab ins C-Hauptprogramm!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Exception Vector Table (VBT) ==&lt;br /&gt;
&lt;br /&gt;
Die VBT ist das &amp;quot;Telefonbuch&amp;quot; des Prozessors. Bei einer Ausnahme schaut die CPU in dieses Register (`vbar_el1`) und springt zu der dort hinterlegten Adresse. &lt;br /&gt;
&lt;br /&gt;
Die Tabelle muss exakt &#039;&#039;&#039;2048 Byte (2 KB)&#039;&#039;&#039; groß sein (&#039;&#039;.align 11&#039;&#039;). Sie ist in 16 Einträge unterteilt (4 Ausnahmetypen × 4 Ausführungsstatus). Jeder Eintrag muss exakt &#039;&#039;&#039;128 Byte&#039;&#039;&#039; (&#039;&#039;.align 7&#039;&#039;) voneinander entfernt sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S&lt;br /&gt;
.align 11                      // 2^11 = 2048 Byte Ausrichtung für die ganze Tabelle&lt;br /&gt;
.globl VectorTable&lt;br /&gt;
VectorTable:&lt;br /&gt;
    // 1. Aktuelles EL, Stackpointer ist SP_EL0 (User-Mode, nutzen wir nicht)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub1&lt;br /&gt;
&lt;br /&gt;
    // 2. Aktuelles EL, Stackpointer ist SP_ELx (Kernel-Mode, EL1h) -&amp;gt; DAS NUTZEN WIR!&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError2              // Synchronous Exception (z.B. svc #0, Memory Abort)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler        // Normaler Hardware-Interrupt (IRQ)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler     // FIQ&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub2              // System Error&lt;br /&gt;
&lt;br /&gt;
    // 3. Niedrigeres EL (EL0), 64-Bit (Auch hier leiten wir auf unsere Handler um)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    // ... (weitere Einträge siehe vector.S)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
🛠️ &#039;&#039;&#039;Debugging-Hinweis:&#039;&#039;&#039; Die &#039;&#039;SynchError&#039;&#039;-Handler sind extrem wertvoll. Wenn dein Programm unerwartet in `SynchError2` landet, weißt du: Ein Befehl in EL1 war ungültig oder hat einen Fehler verursacht. Wir werden das gleich gezielt provozieren, um zu prüfen, ob die Tabelle funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Der Blindtest: `svc #0` (Supervisor Call) ==&lt;br /&gt;
&lt;br /&gt;
Bevor wir uns mit komplexer Hardware (wie dem GIC-Interrupt-Controller) herumschlagen, müssen wir sicherstellen, dass unser Exception-Mechanismus grundlegend funktioniert. &lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;svc #0&#039;&#039; (Supervisor Call) löst absichtlich eine &#039;&#039;&#039;synchrone Exception&#039;&#039;&#039; aus. Da wir uns in EL1 befinden, sollte die CPU in den Eintrag &#039;&#039;SynchError2&#039;&#039; unserer Vektortabelle springen.&lt;br /&gt;
&lt;br /&gt;
Füge diesen Test vorübergehend in deine `main.c` ein, *bevor* du `printf` oder andere Initialisierungen machst:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// In main.c&lt;br /&gt;
#include &amp;quot;printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;sync.h&amp;quot; // Für Stop()&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    // --- BLINDTEST FÜR EXCEPTIONS ---&lt;br /&gt;
    printf(&amp;quot;Starte Exception-Blindtest...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Dieser Befehl löst absichtlich eine synchrone Exception aus.&lt;br /&gt;
    // Die CPU sollte in vector.S -&amp;gt; SynchError2 -&amp;gt; ExceptionHandler springen.&lt;br /&gt;
    asm volatile(&amp;quot;svc #0&amp;quot;); &lt;br /&gt;
    &lt;br /&gt;
    // Diese Zeile sollte NICHT erreicht werden!&lt;br /&gt;
    printf(&amp;quot;FEHLER: svc #0 wurde nicht abgefangen!\n&amp;quot;);&lt;br /&gt;
    Stop();&lt;br /&gt;
    // ----------------------------------&lt;br /&gt;
&lt;br /&gt;
    // ... Rest deiner Initialisierung (Interrupt_Initialize, Timer, etc.) ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Erwartetes Ergebnis:**&lt;br /&gt;
Auf dem Bildschirm sollte erscheinen:&lt;br /&gt;
`Starte Exception-Blindtest...`&lt;br /&gt;
`Error: Exception &#039;SynchError2&#039; ausgelöst`&lt;br /&gt;
Und das Programm bleibt stehen (dank `Stop()` im `ExceptionHandler`).&lt;br /&gt;
&lt;br /&gt;
**Wenn das funktioniert, hast du den schwersten Teil bereits geschafft:** Der Wechsel nach EL1, das Setzen von `vbar_el1` und die korrekte Ausrichtung der Vektortabelle sind verifiziert! Du kannst den `svc #0`-Test danach wieder entfernen.&lt;br /&gt;
&lt;br /&gt;
## 7. Der IRQ-Handler: Den Zustand retten und wiederherstellen&lt;br /&gt;
&lt;br /&gt;
Wenn ein echter Hardware-Interrupt (z. B. vom Timer) kommt, springt die CPU zu `GenericIRQHandler`. Da der Interrupt *jederzeit* passieren kann, weiß der Handler nicht, welche Register gerade benutzt wurden. &lt;br /&gt;
&lt;br /&gt;
Die goldene Regel lautet: **Rette alles, was du veränderst, und stelle es exakt so wieder her.**&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S (vereinfachte Darstellung des GenericIRQHandler)&lt;br /&gt;
.globl GenericIRQHandler&lt;br /&gt;
GenericIRQHandler:&lt;br /&gt;
    // 1. Frame-Pointer und Link-Register retten&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 2. Rücksprungadresse (elr_el1) und Status (spsr_el1) retten&lt;br /&gt;
    mrs x29, elr_el1&lt;br /&gt;
    mrs x30, spsr_el1&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 3. FIQs erlauben (falls sie gesperrt waren), IRQs bleiben durch Eintritt automatisch gesperrt&lt;br /&gt;
    msr DAIFClr, #1&lt;br /&gt;
    &lt;br /&gt;
    // 4. ALLE General Purpose Register (x0-x28) auf den Stack sichern&lt;br /&gt;
    stp x27, x28, [sp, #-16]!&lt;br /&gt;
    // ... (weitere stp Befehle bis x0) ...&lt;br /&gt;
    str x0, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 5. ALLE NEON/FP-Register (q0-q31) sichern (wichtig für C-Code mit Floats!)&lt;br /&gt;
    stp q30, q31, [sp, #-32]!&lt;br /&gt;
    // ... (weitere stp Befehle bis q0) ...&lt;br /&gt;
    &lt;br /&gt;
    // 6. Jetzt ist der Stack sauber. Wir können sicher C-Code aufrufen!&lt;br /&gt;
    bl InterruptHandler&lt;br /&gt;
    &lt;br /&gt;
    // 7. Epilog: Alles in EXAKT umgekehrter Reihenfolge wiederherstellen!&lt;br /&gt;
    ldp q0, q1, [sp], #32&lt;br /&gt;
    // ... (bis q30, q31)&lt;br /&gt;
    &lt;br /&gt;
    ldr x0, [sp], #16&lt;br /&gt;
    ldp x1, x2, [sp], #16&lt;br /&gt;
    // ... (bis x27, x28)&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    msr elr_el1, x29&lt;br /&gt;
    msr spsr_el1, x30&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    &lt;br /&gt;
    // 8. Zurück zum unterbrochenen Code&lt;br /&gt;
    eret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
## 8. Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
1. **Nur ein Kern:** Wir schicken Core 1-3 in den Schlaf (`WFE`), um Chaos zu vermeiden.&lt;br /&gt;
2. **EL1 ist Pflicht:** Wir wechseln von EL2 nach EL1, um Kernel-Rechte zu haben.&lt;br /&gt;
3. **Vektortabelle:** Sie muss auf 2 KB ausgerichtet sein und zeigt der CPU den Weg zu den Handlern.&lt;br /&gt;
4. **Blindtest:** Ein `svc #0` ist der perfekte, sichere Weg, um zu prüfen, ob Exceptions korrekt abgefangen werden, bevor man Hardware-IRQs konfiguriert.&lt;br /&gt;
5. **State Saving:** Der Assembly-Handler muss *jedes* Register sichern, bevor er C-Code (`InterruptHandler`) aufruft, und es danach exakt wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
## Ausblick&lt;br /&gt;
&lt;br /&gt;
Nun, da das Fundament steht und wir wissen, dass unsere Exception-Tabelle funktioniert, können wir uns im nächsten Kapitel dem **Generic Interrupt Controller (GIC-400)** und dem **System Timer** widmen. Dort werden wir lernen, wie wir einen echten Hardware-Interrupt (IRQ 27) aktivieren, um unseren ersten periodischen &amp;quot;Rotor&amp;quot; auf dem Bildschirm zu animieren!&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Systeminformationen (PI4)|&amp;lt; Zurück (Systeminformationen)]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Beispiel Timer-Interrupt (PI4)|Weiter (Beispiel Timer-Interrupt) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1334</id>
		<title>Interrupts (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Interrupts_(PI4)&amp;diff=1334"/>
		<updated>2026-07-21T05:32:22Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Interrupts und Exceptions auf dem Raspberry Pi 4 (Bare Metal) ==&lt;br /&gt;
&lt;br /&gt;
Interrupts (Unterbrechungen) und Exceptions (Ausnahmen) sind das Nervensystem jedes Betriebssystems. &lt;br /&gt;
Sie sorgen dafür, dass der Prozessor nicht blind Code abarbeitet, sondern auf Ereignisse reagieren kann – sei es,&lt;br /&gt;
dass ein Timer abläuft, eine Taste gedrückt wird oder ein schwerwiegender Programmierfehler auftritt.&lt;br /&gt;
&lt;br /&gt;
In diesem Kapitel lernst du, wie die Ausnahmebehandlung im ARMv8 (AArch64) funktioniert,&lt;br /&gt;
wie wir die Vektortabelle einrichten und wie wir mit einem einfachen &amp;quot;Blindtest&amp;quot; sicherstellen,&lt;br /&gt;
dass unser System bereit für echte Hardware-Interrupts ist.&lt;br /&gt;
&lt;br /&gt;
== Exceptions vs. Interrupts: Was ist der Unterschied? ==&lt;br /&gt;
&lt;br /&gt;
Obwohl die Begriffe oft synonym verwendet werden, gibt es einen feinen, aber wichtigen Unterschied:&lt;br /&gt;
* Exceptions (Synchron): Treten &#039;&#039;&#039;direkt&#039;&#039;&#039; durch die Ausführung eines Befehls auf. Beispiele: Ein ungültiger Speicherzugriff, eine Division durch Null oder ein bewusster Systemaufruf (wie `svc #0`). Der Prozessor weiß &#039;&#039;&#039;genau&#039;&#039;&#039;, welcher Befehl die Ausnahme ausgelöst hat.&lt;br /&gt;
* Interrupts (Asynchron): Treten &#039;&#039;&#039;unabhängig&#039;&#039;&#039; vom aktuellen Befehl auf, ausgelöst durch externe Hardware. Beispiele: Der System-Timer, eine USB-Maus oder ein GPIO-Pin.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tipp für die OS-Entwicklung&#039;&#039;: Synchronous Exceptions (wie unsere `SynchError`-Handler) sind deine besten Freunde beim Debuggen! Wenn dein Code versehentlich in einen `SynchError`-Handler springt, weißt du sofort: Hier ist ein schwerer Fehler (z. B. Sprung an eine ungültige Adresse oder falscher Befehl). Wir werden das gleich gezielt ausnutzen.&lt;br /&gt;
&lt;br /&gt;
== Die Spielregeln: Exception Levels (EL) und DAIF ==&lt;br /&gt;
&lt;br /&gt;
Der ARM Cortex-A72 kennt verschiedene Privilegienstufen, die &#039;&#039;&#039;Exception Levels (EL)&#039;&#039;&#039;:&lt;br /&gt;
* &#039;&#039;&#039;EL0:&#039;&#039;&#039; User-Mode (Anwendungen, wenig Rechte)&lt;br /&gt;
* &#039;&#039;&#039;EL1:&#039;&#039;&#039; Kernel-Mode (Unser Bare-Metal-Code, volle Hardware-Kontrolle)&lt;br /&gt;
* &#039;&#039;&#039;EL2:&#039;&#039;&#039; Hypervisor (Virtualisierung, wird vom Bootloader kurz genutzt)&lt;br /&gt;
* &#039;&#039;&#039;EL3:&#039;&#039;&#039; Secure Monitor (Highest Privilege, z. B. ARM Trusted Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Um Interrupts korrekt zu verarbeiten, müssen wir uns in &#039;&#039;&#039;EL1&#039;&#039;&#039; befinden. Da der Raspberry Pi 4 oft in EL2 startet, ist unser erster Schritt der Wechsel nach EL1.&lt;br /&gt;
&lt;br /&gt;
Zudem kontrolliert das &#039;&#039;&#039;DAIF&#039;&#039;&#039;-Register, welche Interrupts gerade erlaubt sind:&lt;br /&gt;
* &#039;&#039;&#039;D&#039;&#039;&#039; (Debug): Debug-Exceptions sperren&lt;br /&gt;
* &#039;&#039;&#039;A&#039;&#039;&#039; (SError): Asynchrone Systemfehler sperren&lt;br /&gt;
* &#039;&#039;&#039;I&#039;&#039;&#039; (IRQ): Normale Interrupts sperren&lt;br /&gt;
* &#039;&#039;&#039;F&#039;&#039;&#039; (FIQ): Schnelle Interrupts (Fast IRQs) sperren&lt;br /&gt;
&lt;br /&gt;
Ist ein Bit auf `1` gesetzt, ist der Interrupt &#039;&#039;&#039;gesperrt&#039;&#039;&#039;. Um IRQs zu erlauben, müssen wir das I-Bit auf `0` setzen (clear).&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung: Nur ein Kern darf laufen ==&lt;br /&gt;
&lt;br /&gt;
Der Pi 4 hat 4 Kerne. Beim Start führen alle denselben Code aus. Würden alle versuchen, Interrupts zu bearbeiten oder den Stack zu nutzen, würde das System sofort abstürzen. Wir lassen nur &#039;&#039;&#039;Core 0&#039;&#039;&#039; arbeiten und schicken die anderen in einen energiesparenden Schlafmodus (`WFE` = Wait For Event).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In boot.S&lt;br /&gt;
_start:&lt;br /&gt;
    mrs x1, mpidr_el1    // Core-ID lesen&lt;br /&gt;
    and x1, x1, #3       // Nur die unteren 2 Bits (0-3) behalten&lt;br /&gt;
    cbz x1, core0        // Wenn Core 0, weiter zum Setup&lt;br /&gt;
&lt;br /&gt;
core_sleep:&lt;br /&gt;
    wfe                  // Warte auf ein Event (Sleep)&lt;br /&gt;
    b core_sleep         // Endlosschleife für Core 1-3&lt;br /&gt;
&lt;br /&gt;
core0:&lt;br /&gt;
    // Ab hier läuft nur noch Core 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Der Wechsel von EL2 nach EL1 ==&lt;br /&gt;
&lt;br /&gt;
Da wir ein Kernel schreiben, gehört unser Platz in EL1. Der folgende Code konfiguriert EL1, setzt den Stackpointer und bereitet den Rücksprung vor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
    // 1. Prüfen, ob wir schon in EL1 sind (CurrentEL = 8 für EL1)&lt;br /&gt;
    mrs x0, CurrentEL&lt;br /&gt;
    cmp x0, #8&lt;br /&gt;
    beq switch_to_el1&lt;br /&gt;
&lt;br /&gt;
    // 2. EL1 Stackpointer setzen&lt;br /&gt;
    ldr x0, =EXCEPTION_STACK&lt;br /&gt;
    msr sp_el1, x0&lt;br /&gt;
&lt;br /&gt;
    // 3. Vektortabelle für EL2 setzen (für den Fall, dass während des Wechsels was passiert)&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el2, x0&lt;br /&gt;
&lt;br /&gt;
    // ... (Hier folgen Timer- und Virtualisierungs-Einstellungen, siehe boot.S) ...&lt;br /&gt;
&lt;br /&gt;
    // 4. Rücksprung-Adresse und Status für EL1 vorbereiten&lt;br /&gt;
    mov x0, #0x3c4             // SPSR_EL2: EL1h, Interrupts (DAIF) sind hier noch maskiert&lt;br /&gt;
    msr spsr_el2, x0&lt;br /&gt;
    adr x0, el1_return         // Adresse, zu der wir nach dem Wechsel springen&lt;br /&gt;
    msr elr_el2, x0&lt;br /&gt;
&lt;br /&gt;
    eret                       // Exception Return: Wechselt nach EL1 zu &#039;el1_return&#039;&lt;br /&gt;
&lt;br /&gt;
el1_return:&lt;br /&gt;
switch_to_el1:&lt;br /&gt;
    // Wir sind jetzt in EL1!&lt;br /&gt;
    ldr x0, =VectorTable&lt;br /&gt;
    msr vbar_el1, x0           // Vektortabelle für EL1 aktivieren&lt;br /&gt;
&lt;br /&gt;
    // BSS-Segment (uninitialisierte Variablen) mit Nullen füllen&lt;br /&gt;
    ldr x1, =__bss_start&lt;br /&gt;
    ldr w2, =__bss_size&lt;br /&gt;
clean_bss_loop:&lt;br /&gt;
    cbz w2, bss_clean_done&lt;br /&gt;
    str xzr, [x1], #8&lt;br /&gt;
    sub w2, w2, #1&lt;br /&gt;
    cbnz w2, clean_bss_loop&lt;br /&gt;
bss_clean_done:&lt;br /&gt;
&lt;br /&gt;
    b main                     // Ab ins C-Hauptprogramm!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Exception Vector Table (VBT) ==&lt;br /&gt;
&lt;br /&gt;
Die VBT ist das &amp;quot;Telefonbuch&amp;quot; des Prozessors. Bei einer Ausnahme schaut die CPU in dieses Register (`vbar_el1`) und springt zu der dort hinterlegten Adresse. &lt;br /&gt;
&lt;br /&gt;
Die Tabelle muss exakt &#039;&#039;&#039;2048 Byte (2 KB)&#039;&#039;&#039; groß sein (&#039;&#039;.align 11&#039;&#039;). Sie ist in 16 Einträge unterteilt (4 Ausnahmetypen × 4 Ausführungsstatus). Jeder Eintrag muss exakt &#039;&#039;&#039;128 Byte&#039;&#039;&#039; (&#039;&#039;.align 7&#039;&#039;) voneinander entfernt sein.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S&lt;br /&gt;
.align 11                      // 2^11 = 2048 Byte Ausrichtung für die ganze Tabelle&lt;br /&gt;
.globl VectorTable&lt;br /&gt;
VectorTable:&lt;br /&gt;
    // 1. Aktuelles EL, Stackpointer ist SP_EL0 (User-Mode, nutzen wir nicht)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub1&lt;br /&gt;
&lt;br /&gt;
    // 2. Aktuelles EL, Stackpointer ist SP_ELx (Kernel-Mode, EL1h) -&amp;gt; DAS NUTZEN WIR!&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError2              // Synchronous Exception (z.B. svc #0, Memory Abort)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler        // Normaler Hardware-Interrupt (IRQ)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b FastInterruptHandler     // FIQ&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SErrorStub2              // System Error&lt;br /&gt;
&lt;br /&gt;
    // 3. Niedrigeres EL (EL0), 64-Bit (Auch hier leiten wir auf unsere Handler um)&lt;br /&gt;
    .align 7&lt;br /&gt;
    b SynchError1&lt;br /&gt;
    .align 7&lt;br /&gt;
    b GenericIRQHandler&lt;br /&gt;
    // ... (weitere Einträge siehe vector.S)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
🛠️ &#039;&#039;&#039;Debugging-Hinweis:&#039;&#039;&#039; Die &#039;&#039;SynchError&#039;&#039;-Handler sind extrem wertvoll. Wenn dein Programm unerwartet in `SynchError2` landet, weißt du: Ein Befehl in EL1 war ungültig oder hat einen Fehler verursacht. Wir werden das gleich gezielt provozieren, um zu prüfen, ob die Tabelle funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Der Blindtest: `svc #0` (Supervisor Call) ==&lt;br /&gt;
&lt;br /&gt;
Bevor wir uns mit komplexer Hardware (wie dem GIC-Interrupt-Controller) herumschlagen, müssen wir sicherstellen, dass unser Exception-Mechanismus grundlegend funktioniert. &lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;svc #0&#039;&#039; (Supervisor Call) löst absichtlich eine &#039;&#039;&#039;synchrone Exception&#039;&#039;&#039; aus. Da wir uns in EL1 befinden, sollte die CPU in den Eintrag &#039;&#039;SynchError2&#039;&#039; unserer Vektortabelle springen.&lt;br /&gt;
&lt;br /&gt;
Füge diesen Test vorübergehend in deine `main.c` ein, *bevor* du `printf` oder andere Initialisierungen machst:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// In main.c&lt;br /&gt;
#include &amp;quot;printf.h&amp;quot;&lt;br /&gt;
#include &amp;quot;sync.h&amp;quot; // Für Stop()&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    // --- BLINDTEST FÜR EXCEPTIONS ---&lt;br /&gt;
    printf(&amp;quot;Starte Exception-Blindtest...\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Dieser Befehl löst absichtlich eine synchrone Exception aus.&lt;br /&gt;
    // Die CPU sollte in vector.S -&amp;gt; SynchError2 -&amp;gt; ExceptionHandler springen.&lt;br /&gt;
    asm volatile(&amp;quot;svc #0&amp;quot;); &lt;br /&gt;
    &lt;br /&gt;
    // Diese Zeile sollte NICHT erreicht werden!&lt;br /&gt;
    printf(&amp;quot;FEHLER: svc #0 wurde nicht abgefangen!\n&amp;quot;);&lt;br /&gt;
    Stop();&lt;br /&gt;
    // ----------------------------------&lt;br /&gt;
&lt;br /&gt;
    // ... Rest deiner Initialisierung (Interrupt_Initialize, Timer, etc.) ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Erwartetes Ergebnis:**&lt;br /&gt;
Auf dem Bildschirm sollte erscheinen:&lt;br /&gt;
`Starte Exception-Blindtest...`&lt;br /&gt;
`Error: Exception &#039;SynchError2&#039; ausgelöst`&lt;br /&gt;
Und das Programm bleibt stehen (dank `Stop()` im `ExceptionHandler`).&lt;br /&gt;
&lt;br /&gt;
**Wenn das funktioniert, hast du den schwersten Teil bereits geschafft:** Der Wechsel nach EL1, das Setzen von `vbar_el1` und die korrekte Ausrichtung der Vektortabelle sind verifiziert! Du kannst den `svc #0`-Test danach wieder entfernen.&lt;br /&gt;
&lt;br /&gt;
## 7. Der IRQ-Handler: Den Zustand retten und wiederherstellen&lt;br /&gt;
&lt;br /&gt;
Wenn ein echter Hardware-Interrupt (z. B. vom Timer) kommt, springt die CPU zu `GenericIRQHandler`. Da der Interrupt *jederzeit* passieren kann, weiß der Handler nicht, welche Register gerade benutzt wurden. &lt;br /&gt;
&lt;br /&gt;
Die goldene Regel lautet: **Rette alles, was du veränderst, und stelle es exakt so wieder her.**&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// In vector.S (vereinfachte Darstellung des GenericIRQHandler)&lt;br /&gt;
.globl GenericIRQHandler&lt;br /&gt;
GenericIRQHandler:&lt;br /&gt;
    // 1. Frame-Pointer und Link-Register retten&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 2. Rücksprungadresse (elr_el1) und Status (spsr_el1) retten&lt;br /&gt;
    mrs x29, elr_el1&lt;br /&gt;
    mrs x30, spsr_el1&lt;br /&gt;
    stp x29, x30, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 3. FIQs erlauben (falls sie gesperrt waren), IRQs bleiben durch Eintritt automatisch gesperrt&lt;br /&gt;
    msr DAIFClr, #1&lt;br /&gt;
    &lt;br /&gt;
    // 4. ALLE General Purpose Register (x0-x28) auf den Stack sichern&lt;br /&gt;
    stp x27, x28, [sp, #-16]!&lt;br /&gt;
    // ... (weitere stp Befehle bis x0) ...&lt;br /&gt;
    str x0, [sp, #-16]!&lt;br /&gt;
    &lt;br /&gt;
    // 5. ALLE NEON/FP-Register (q0-q31) sichern (wichtig für C-Code mit Floats!)&lt;br /&gt;
    stp q30, q31, [sp, #-32]!&lt;br /&gt;
    // ... (weitere stp Befehle bis q0) ...&lt;br /&gt;
    &lt;br /&gt;
    // 6. Jetzt ist der Stack sauber. Wir können sicher C-Code aufrufen!&lt;br /&gt;
    bl InterruptHandler&lt;br /&gt;
    &lt;br /&gt;
    // 7. Epilog: Alles in EXAKT umgekehrter Reihenfolge wiederherstellen!&lt;br /&gt;
    ldp q0, q1, [sp], #32&lt;br /&gt;
    // ... (bis q30, q31)&lt;br /&gt;
    &lt;br /&gt;
    ldr x0, [sp], #16&lt;br /&gt;
    ldp x1, x2, [sp], #16&lt;br /&gt;
    // ... (bis x27, x28)&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    msr elr_el1, x29&lt;br /&gt;
    msr spsr_el1, x30&lt;br /&gt;
    &lt;br /&gt;
    ldp x29, x30, [sp], #16&lt;br /&gt;
    &lt;br /&gt;
    // 8. Zurück zum unterbrochenen Code&lt;br /&gt;
    eret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
## 8. Zusammenfassung&lt;br /&gt;
&lt;br /&gt;
1. **Nur ein Kern:** Wir schicken Core 1-3 in den Schlaf (`WFE`), um Chaos zu vermeiden.&lt;br /&gt;
2. **EL1 ist Pflicht:** Wir wechseln von EL2 nach EL1, um Kernel-Rechte zu haben.&lt;br /&gt;
3. **Vektortabelle:** Sie muss auf 2 KB ausgerichtet sein und zeigt der CPU den Weg zu den Handlern.&lt;br /&gt;
4. **Blindtest:** Ein `svc #0` ist der perfekte, sichere Weg, um zu prüfen, ob Exceptions korrekt abgefangen werden, bevor man Hardware-IRQs konfiguriert.&lt;br /&gt;
5. **State Saving:** Der Assembly-Handler muss *jedes* Register sichern, bevor er C-Code (`InterruptHandler`) aufruft, und es danach exakt wiederherstellen.&lt;br /&gt;
&lt;br /&gt;
## Ausblick&lt;br /&gt;
&lt;br /&gt;
Nun, da das Fundament steht und wir wissen, dass unsere Exception-Tabelle funktioniert, können wir uns im nächsten Kapitel dem **Generic Interrupt Controller (GIC-400)** und dem **System Timer** widmen. Dort werden wir lernen, wie wir einen echten Hardware-Interrupt (IRQ 27) aktivieren, um unseren ersten periodischen &amp;quot;Rotor&amp;quot; auf dem Bildschirm zu animieren!&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
### Warum diese Überarbeitung besser ist:&lt;br /&gt;
1. **Der `svc #0` Test ist jetzt ein klares, abgehaktes &amp;quot;Meilenstein&amp;quot;-Kapitel.** Das gibt dem Leser ein direktes Erfolgserlebnis.&lt;br /&gt;
2. **Die Rolle von `SynchError` wird als Debugging-Werkzeug hervorgehoben**, genau wie von dir gewünscht. Das nimmt dem &amp;quot;Fehler&amp;quot; den Schrecken und macht ihn zum Freund des Entwicklers.&lt;br /&gt;
3. **Der Assembly-Code im Handler** wurde an deinen tatsächlichen `vector.S` Code angepasst (inklusive Erwähnung der NEON-Register `q0-q31`, die in deinem Code vorkommen und für Bare-Metal C mit Floats/Stack-Alignment absolut kritisch sind).&lt;br /&gt;
4. Die Struktur folgt einer logischen &amp;quot;Vom Einfachen zum Komplexen&amp;quot;-Didaktik.&lt;br /&gt;
&lt;br /&gt;
Passt dieser Text so für dein Tutorial, oder möchtest du an einer bestimmten Stelle noch tiefer ins Detail gehen (z. B. genau erklären, was `eret` im Hintergrund macht)?&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | [[Systeminformationen (PI4)|&amp;lt; Zurück (Systeminformationen)]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Beispiel Timer-Interrupt (PI4)|Weiter (Beispiel Timer-Interrupt) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=LINKS&amp;diff=1333</id>
		<title>LINKS</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=LINKS&amp;diff=1333"/>
		<updated>2026-07-17T11:52:14Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Bare Metal Projekte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Dokumentation für den Raspberry Pi ==&lt;br /&gt;
=== Raspberry Pi auf elinux.org ===&lt;br /&gt;
https://elinux.org/RPi_Hub&lt;br /&gt;
&lt;br /&gt;
=== Peripherie Unterlagen ===&lt;br /&gt;
https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 1 Models A, A+, B, B+, Raspberry Pi Zero, Raspberry Pi Zero W, und Raspberry Pi Compute Module 1 ====&lt;br /&gt;
* BCM2835 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* VideoCore® IV 3D, Architecture Reference Guide: https://docs.broadcom.com/docs/12358545&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* ARM1176JZF-S: https://developer.arm.com/documentation/ddi0301&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 2 Model B ====&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Cortex-A7 MPcore Processor Reference Manual: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0464f/index.html&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Model B, spätere Raspberry Pi 2 Model B und Raspberry Pi Compute Module 3 ==== &lt;br /&gt;
BCM2837 ist identisch wie BCM2836&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Models A+, B+ und Raspberry Pi Compute Module 3+ ==== &lt;br /&gt;
BCM2837B0 ist identisch wie BCM2837, Änderung der internen Frequenzen.&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 4 Model B, the Raspberry Pi 400, and the Raspberry Pi Compute Module 4 ====&lt;br /&gt;
* BCM2711 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A72: https://developer.arm.com/Processors/Cortex-A72 &lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 5 ====&lt;br /&gt;
* rp1-peripherals: https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt; Information aus: https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Forum ==&lt;br /&gt;
Raspberry Pi Forum: https://www.raspberrypi.org/forums/&lt;br /&gt;
&lt;br /&gt;
== Bare Metal Projekte ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Bare Metal Projekte&lt;br /&gt;
|-&lt;br /&gt;
! Name !! RPI !! Programmiersprache !! Inhalt !! Link !! Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Writing a &amp;quot;bare metal&amp;quot; operating system for Raspberry Pi 4 || Pi4 || C (ARMv8) || UART, Grafik, Bluetooth, Sound, Multicore, Interrupts, SPI Ethernet, TCPIP Webserver|| https://www.rpi4os.com/ or https://github.com/isometimes/rpi4-osdev&amp;lt;nowiki/&amp;gt;|| Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Bare Metal Programming on Raspberry Pi 3 || Pi3 || C (ARMv8) || Multicore, UART1, Mailbox, UART0, Zufallszahlen, Timer, PowerOff, Grafik, Diskfunktionen, Memory  || https://github.com/bztsrc/raspi3-tutorial || ca. 3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| Circle || Pi0-5 || C++ || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://github.com/rsta2/circle || 2 Monate&lt;br /&gt;
|-&lt;br /&gt;
| Ultibo || Pi0-4 || Free Pascal || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://ultibo.org/ || ca. 1 Monat&lt;br /&gt;
|-&lt;br /&gt;
| Raspberry-Pi Bare Metal Tutorial || Pi0-4 || C || C Runtime, Interrupts, UART, Mailbox, Grafik || https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-1/ || ca. 6 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| USPi - A bare metal USB driver for Raspberry Pi 1-3 written in C || Pi0-3 || C || Circle in C || https://github.com/rsta2/uspi || ca. 3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| dwelch67 || Pi0-3 || C || Float, Bootloader, MMU, SPI, UART, VIDEO, ZLIB || https://github.com/dwelch67/raspberrypi || ca. 9 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| ... || Pi1 || Assembler ||  || https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/index.html || ca. 14 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| Learning operating system development using Linux kernel and Raspberry Pi || Pi3 || C || Printf, Interrupt, Memory &amp;lt;/br&amp;gt; Vergleicht Bare Metal und Linux || https://github.com/s-matyukevich/raspberry-pi-os || 6 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|PeterLemon RaspberryPi&lt;br /&gt;
|Pi1-3&lt;br /&gt;
|Assembler&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/PeterLemon/RaspberryPi&lt;br /&gt;
|5 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|pi-baremetal&lt;br /&gt;
|Pi1&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/brianwiddas/pi-baremetal&lt;br /&gt;
|14 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|Raspberry Pi Bare Bones&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|QEMU&lt;br /&gt;
|http://wiki.osdev.org/Raspberry_Pi_Bare_Bones&lt;br /&gt;
|Aktuell&lt;br /&gt;
|-&lt;br /&gt;
|circle-stdlib&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C++&lt;br /&gt;
|Circle StdLib&lt;br /&gt;
|https://github.com/smuehlst/circle-stdlib&lt;br /&gt;
|2 Monate&lt;br /&gt;
|-&lt;br /&gt;
|sean-lawless / computersystems&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/sean-lawless/computersystems/tree/master&lt;br /&gt;
|Aktuell&lt;br /&gt;
|-&lt;br /&gt;
|macoy rpi-system&lt;br /&gt;
|Pi4&lt;br /&gt;
|C&lt;br /&gt;
|Circle in C (nur RPI4)&lt;br /&gt;
|https://macoy.me/code/macoy/rpi-system&lt;br /&gt;
|1 Jahr&lt;br /&gt;
|}&lt;br /&gt;
Stand Juli 2026&lt;br /&gt;
&lt;br /&gt;
== Nützliche Links ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Nützliche Links&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Inhalt !! Link&lt;br /&gt;
|-&lt;br /&gt;
| Introduction to Computer Organization: ARM Assembly Language Using the Raspberry Pi || ARM-Assembler || https://bob.cs.sonoma.edu/IntroCompOrg-RPi/intro-co-rpi.html&lt;br /&gt;
|-&lt;br /&gt;
| Arm GNU Toolchain Downloads || Toolchain || https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads&lt;br /&gt;
|-&lt;br /&gt;
| ARM assembler in Raspberry Pi || ARM-Assembler || https://thinkingeek.com/arm-assembler-raspberry-pi/&lt;br /&gt;
|-&lt;br /&gt;
| A Guide to ARM64 / AArch64 Assembly on Linux with Shellcodes and Cryptography || ARM-Assembler || https://modexp.wordpress.com/2018/10/30/arm64-assembly/&lt;br /&gt;
|-&lt;br /&gt;
| Découvrir l&#039;assembleur ARM sur Raspberry Pi || ARM-Assembler || https://assembleurarmpi.blogspot.com/ or https://github.com/vincentARM/ARMassembly64&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 A64 Quick Reference&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://courses.cs.washington.edu/courses/cse469/18wi/Materials/arm64.pdf&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 Instruction Set Overview&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://www.cs.princeton.edu/courses/archive/fall23/cos217/reading/ArmInstructionSetOverview.pdf&lt;br /&gt;
|-&lt;br /&gt;
|A64 Systemaufrufe&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://arm64.syscall.sh/&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|https://s-matyukevich.github.io/raspberry-pi-os/&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Andere, nicht zugeordnet ==&lt;br /&gt;
Raspberry Pi DMA programming in C https://iosoft.blog/2020/05/25/raspberry-pi-dma-programming/&lt;br /&gt;
&lt;br /&gt;
Mailbox property interface https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=LINKS&amp;diff=1332</id>
		<title>LINKS</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=LINKS&amp;diff=1332"/>
		<updated>2026-07-17T11:32:47Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Bare Metal Projekte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Dokumentation für den Raspberry Pi ==&lt;br /&gt;
=== Raspberry Pi auf elinux.org ===&lt;br /&gt;
https://elinux.org/RPi_Hub&lt;br /&gt;
&lt;br /&gt;
=== Peripherie Unterlagen ===&lt;br /&gt;
https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 1 Models A, A+, B, B+, Raspberry Pi Zero, Raspberry Pi Zero W, und Raspberry Pi Compute Module 1 ====&lt;br /&gt;
* BCM2835 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* VideoCore® IV 3D, Architecture Reference Guide: https://docs.broadcom.com/docs/12358545&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* ARM1176JZF-S: https://developer.arm.com/documentation/ddi0301&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 2 Model B ====&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Cortex-A7 MPcore Processor Reference Manual: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0464f/index.html&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Model B, spätere Raspberry Pi 2 Model B und Raspberry Pi Compute Module 3 ==== &lt;br /&gt;
BCM2837 ist identisch wie BCM2836&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Models A+, B+ und Raspberry Pi Compute Module 3+ ==== &lt;br /&gt;
BCM2837B0 ist identisch wie BCM2837, Änderung der internen Frequenzen.&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 4 Model B, the Raspberry Pi 400, and the Raspberry Pi Compute Module 4 ====&lt;br /&gt;
* BCM2711 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A72: https://developer.arm.com/Processors/Cortex-A72 &lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 5 ====&lt;br /&gt;
* rp1-peripherals: https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt; Information aus: https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Forum ==&lt;br /&gt;
Raspberry Pi Forum: https://www.raspberrypi.org/forums/&lt;br /&gt;
&lt;br /&gt;
== Bare Metal Projekte ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Bare Metal Projekte&lt;br /&gt;
|-&lt;br /&gt;
! Name !! RPI !! Programmiersprache !! Inhalt !! Link !! Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Writing a &amp;quot;bare metal&amp;quot; operating system for Raspberry Pi 4 || Pi4 || C (ARMv8) || UART, Grafik, Bluetooth, Sound, Multicore, Interrupts, SPI Ethernet, TCPIP Webserver|| https://www.rpi4os.com/ or https://github.com/isometimes/rpi4-osdev&amp;lt;nowiki/&amp;gt;|| Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Bare Metal Programming on Raspberry Pi 3 || Pi3 || C (ARMv8) || Multicore, UART1, Mailbox, UART0, Zufallszahlen, Timer, PowerOff, Grafik, Diskfunktionen, Memory  || https://github.com/bztsrc/raspi3-tutorial || ca. 3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| Circle || Pi0-5 || C++ || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://github.com/rsta2/circle || 2 Monate&lt;br /&gt;
|-&lt;br /&gt;
| Ultibo || Pi0-4 || Free Pascal || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://ultibo.org/ || ca. 1 Monat&lt;br /&gt;
|-&lt;br /&gt;
| Raspberry-Pi Bare Metal Tutorial || Pi0-4 || C || C Runtime, Interrupts, UART, Mailbox, Grafik || https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-1/ || ca. 4 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| USPi - A bare metal USB driver for Raspberry Pi 1-3 written in C || Pi0-3 || C || Circle in C || https://github.com/rsta2/uspi || ca. 10 Monate&lt;br /&gt;
|-&lt;br /&gt;
| dwelch67 || Pi0-3 || C || Float, Bootloader, MMU, SPI, UART, VIDEO, ZLIB || https://github.com/dwelch67/raspberrypi || ca. 6 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| ... || Pi1 || Assembler ||  || https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/index.html || ca. 12 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| Learning operating system development using Linux kernel and Raspberry Pi || Pi3 || C || Printf, Interrupt, Memory &amp;lt;/br&amp;gt; Vergleicht Bare Metal und Linux || https://github.com/s-matyukevich/raspberry-pi-os || 3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|PeterLemon RaspberryPi&lt;br /&gt;
|Pi1-3&lt;br /&gt;
|Assembler&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/PeterLemon/RaspberryPi&lt;br /&gt;
|3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|pi-baremetal&lt;br /&gt;
|Pi1&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/brianwiddas/pi-baremetal&lt;br /&gt;
|12 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|Raspberry Pi Bare Bones&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|QEMU&lt;br /&gt;
|http://wiki.osdev.org/Raspberry_Pi_Bare_Bones&lt;br /&gt;
|ca. 1,5 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|circle-stdlib&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C++&lt;br /&gt;
|Circle StdLib&lt;br /&gt;
|https://github.com/smuehlst/circle-stdlib&lt;br /&gt;
|3 Monate&lt;br /&gt;
|-&lt;br /&gt;
|sean-lawless / computersystems&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/sean-lawless/computersystems/tree/master&lt;br /&gt;
|Aktuell&lt;br /&gt;
|-&lt;br /&gt;
|macoy rpi-system&lt;br /&gt;
|Pi4&lt;br /&gt;
|C&lt;br /&gt;
|Circle in C (nur RPI4)&lt;br /&gt;
|https://macoy.me/code/macoy/rpi-system&lt;br /&gt;
|Aktuell&lt;br /&gt;
|}&lt;br /&gt;
Stand Januar 2024&lt;br /&gt;
&lt;br /&gt;
== Nützliche Links ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Nützliche Links&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Inhalt !! Link&lt;br /&gt;
|-&lt;br /&gt;
| Introduction to Computer Organization: ARM Assembly Language Using the Raspberry Pi || ARM-Assembler || https://bob.cs.sonoma.edu/IntroCompOrg-RPi/intro-co-rpi.html&lt;br /&gt;
|-&lt;br /&gt;
| Arm GNU Toolchain Downloads || Toolchain || https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads&lt;br /&gt;
|-&lt;br /&gt;
| ARM assembler in Raspberry Pi || ARM-Assembler || https://thinkingeek.com/arm-assembler-raspberry-pi/&lt;br /&gt;
|-&lt;br /&gt;
| A Guide to ARM64 / AArch64 Assembly on Linux with Shellcodes and Cryptography || ARM-Assembler || https://modexp.wordpress.com/2018/10/30/arm64-assembly/&lt;br /&gt;
|-&lt;br /&gt;
| Découvrir l&#039;assembleur ARM sur Raspberry Pi || ARM-Assembler || https://assembleurarmpi.blogspot.com/ or https://github.com/vincentARM/ARMassembly64&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 A64 Quick Reference&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://courses.cs.washington.edu/courses/cse469/18wi/Materials/arm64.pdf&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 Instruction Set Overview&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://www.cs.princeton.edu/courses/archive/fall23/cos217/reading/ArmInstructionSetOverview.pdf&lt;br /&gt;
|-&lt;br /&gt;
|A64 Systemaufrufe&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://arm64.syscall.sh/&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|https://s-matyukevich.github.io/raspberry-pi-os/&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Andere, nicht zugeordnet ==&lt;br /&gt;
Raspberry Pi DMA programming in C https://iosoft.blog/2020/05/25/raspberry-pi-dma-programming/&lt;br /&gt;
&lt;br /&gt;
Mailbox property interface https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=LINKS&amp;diff=1331</id>
		<title>LINKS</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=LINKS&amp;diff=1331"/>
		<updated>2026-07-17T11:32:01Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Bare Metal Projekte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Dokumentation für den Raspberry Pi ==&lt;br /&gt;
=== Raspberry Pi auf elinux.org ===&lt;br /&gt;
https://elinux.org/RPi_Hub&lt;br /&gt;
&lt;br /&gt;
=== Peripherie Unterlagen ===&lt;br /&gt;
https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 1 Models A, A+, B, B+, Raspberry Pi Zero, Raspberry Pi Zero W, und Raspberry Pi Compute Module 1 ====&lt;br /&gt;
* BCM2835 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* VideoCore® IV 3D, Architecture Reference Guide: https://docs.broadcom.com/docs/12358545&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* ARM1176JZF-S: https://developer.arm.com/documentation/ddi0301&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 2 Model B ====&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Cortex-A7 MPcore Processor Reference Manual: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0464f/index.html&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Model B, spätere Raspberry Pi 2 Model B und Raspberry Pi Compute Module 3 ==== &lt;br /&gt;
BCM2837 ist identisch wie BCM2836&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Models A+, B+ und Raspberry Pi Compute Module 3+ ==== &lt;br /&gt;
BCM2837B0 ist identisch wie BCM2837, Änderung der internen Frequenzen.&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 4 Model B, the Raspberry Pi 400, and the Raspberry Pi Compute Module 4 ====&lt;br /&gt;
* BCM2711 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A72: https://developer.arm.com/Processors/Cortex-A72 &lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 5 ====&lt;br /&gt;
* rp1-peripherals: https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt; Information aus: https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Forum ==&lt;br /&gt;
Raspberry Pi Forum: https://www.raspberrypi.org/forums/&lt;br /&gt;
&lt;br /&gt;
== Bare Metal Projekte ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Bare Metal Projekte&lt;br /&gt;
|-&lt;br /&gt;
! Name !! RPI !! Programmiersprache !! Inhalt !! Link !! Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Writing a &amp;quot;bare metal&amp;quot; operating system for Raspberry Pi 4 || Pi4 || C (ARMv8) || UART, Grafik, Bluetooth, Sound, Multicore, Interrupts, SPI Ethernet, TCPIP Webserver|| https://www.rpi4os.com/ or https://github.com/isometimes/rpi4-osdev&amp;lt;nowiki/&amp;gt;|| Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Bare Metal Programming on Raspberry Pi 3 || Pi3 || C (ARMv8) || Multicore, UART1, Mailbox, UART0, Zufallszahlen, Timer, PowerOff, Grafik, Diskfunktionen, Memory  || https://github.com/bztsrc/raspi3-tutorial || ca. 3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| Circle || Pi0-5 || C++ || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://github.com/rsta2/circle || 1 Monat&lt;br /&gt;
|-&lt;br /&gt;
| Ultibo || Pi0-4 || Free Pascal || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://ultibo.org/ || ca. 1 Monat&lt;br /&gt;
|-&lt;br /&gt;
| Raspberry-Pi Bare Metal Tutorial || Pi0-4 || C || C Runtime, Interrupts, UART, Mailbox, Grafik || https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-1/ || ca. 4 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| USPi - A bare metal USB driver for Raspberry Pi 1-3 written in C || Pi0-3 || C || Circle in C || https://github.com/rsta2/uspi || ca. 10 Monate&lt;br /&gt;
|-&lt;br /&gt;
| dwelch67 || Pi0-3 || C || Float, Bootloader, MMU, SPI, UART, VIDEO, ZLIB || https://github.com/dwelch67/raspberrypi || ca. 6 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| ... || Pi1 || Assembler ||  || https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/index.html || ca. 12 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| Learning operating system development using Linux kernel and Raspberry Pi || Pi3 || C || Printf, Interrupt, Memory &amp;lt;/br&amp;gt; Vergleicht Bare Metal und Linux || https://github.com/s-matyukevich/raspberry-pi-os || 3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|PeterLemon RaspberryPi&lt;br /&gt;
|Pi1-3&lt;br /&gt;
|Assembler&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/PeterLemon/RaspberryPi&lt;br /&gt;
|3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|pi-baremetal&lt;br /&gt;
|Pi1&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/brianwiddas/pi-baremetal&lt;br /&gt;
|12 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|Raspberry Pi Bare Bones&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|QEMU&lt;br /&gt;
|http://wiki.osdev.org/Raspberry_Pi_Bare_Bones&lt;br /&gt;
|ca. 1,5 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|circle-stdlib&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C++&lt;br /&gt;
|Circle StdLib&lt;br /&gt;
|https://github.com/smuehlst/circle-stdlib&lt;br /&gt;
|3 Monate&lt;br /&gt;
|-&lt;br /&gt;
|sean-lawless / computersystems&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/sean-lawless/computersystems/tree/master&lt;br /&gt;
|Aktuell&lt;br /&gt;
|-&lt;br /&gt;
|macoy rpi-system&lt;br /&gt;
|Pi4&lt;br /&gt;
|C&lt;br /&gt;
|Circle in C (nur RPI4)&lt;br /&gt;
|https://macoy.me/code/macoy/rpi-system&lt;br /&gt;
|Aktuell&lt;br /&gt;
|}&lt;br /&gt;
Stand Januar 2024&lt;br /&gt;
&lt;br /&gt;
== Nützliche Links ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Nützliche Links&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Inhalt !! Link&lt;br /&gt;
|-&lt;br /&gt;
| Introduction to Computer Organization: ARM Assembly Language Using the Raspberry Pi || ARM-Assembler || https://bob.cs.sonoma.edu/IntroCompOrg-RPi/intro-co-rpi.html&lt;br /&gt;
|-&lt;br /&gt;
| Arm GNU Toolchain Downloads || Toolchain || https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads&lt;br /&gt;
|-&lt;br /&gt;
| ARM assembler in Raspberry Pi || ARM-Assembler || https://thinkingeek.com/arm-assembler-raspberry-pi/&lt;br /&gt;
|-&lt;br /&gt;
| A Guide to ARM64 / AArch64 Assembly on Linux with Shellcodes and Cryptography || ARM-Assembler || https://modexp.wordpress.com/2018/10/30/arm64-assembly/&lt;br /&gt;
|-&lt;br /&gt;
| Découvrir l&#039;assembleur ARM sur Raspberry Pi || ARM-Assembler || https://assembleurarmpi.blogspot.com/ or https://github.com/vincentARM/ARMassembly64&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 A64 Quick Reference&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://courses.cs.washington.edu/courses/cse469/18wi/Materials/arm64.pdf&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 Instruction Set Overview&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://www.cs.princeton.edu/courses/archive/fall23/cos217/reading/ArmInstructionSetOverview.pdf&lt;br /&gt;
|-&lt;br /&gt;
|A64 Systemaufrufe&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://arm64.syscall.sh/&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|https://s-matyukevich.github.io/raspberry-pi-os/&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Andere, nicht zugeordnet ==&lt;br /&gt;
Raspberry Pi DMA programming in C https://iosoft.blog/2020/05/25/raspberry-pi-dma-programming/&lt;br /&gt;
&lt;br /&gt;
Mailbox property interface https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=LINKS&amp;diff=1330</id>
		<title>LINKS</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=LINKS&amp;diff=1330"/>
		<updated>2026-07-17T11:27:23Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Bare Metal Projekte */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Dokumentation für den Raspberry Pi ==&lt;br /&gt;
=== Raspberry Pi auf elinux.org ===&lt;br /&gt;
https://elinux.org/RPi_Hub&lt;br /&gt;
&lt;br /&gt;
=== Peripherie Unterlagen ===&lt;br /&gt;
https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 1 Models A, A+, B, B+, Raspberry Pi Zero, Raspberry Pi Zero W, und Raspberry Pi Compute Module 1 ====&lt;br /&gt;
* BCM2835 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2835/bcm2835-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* VideoCore® IV 3D, Architecture Reference Guide: https://docs.broadcom.com/docs/12358545&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* ARM1176JZF-S: https://developer.arm.com/documentation/ddi0301&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 2 Model B ====&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Cortex-A7 MPcore Processor Reference Manual: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0464f/index.html&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Model B, spätere Raspberry Pi 2 Model B und Raspberry Pi Compute Module 3 ==== &lt;br /&gt;
BCM2837 ist identisch wie BCM2836&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 3 Models A+, B+ und Raspberry Pi Compute Module 3+ ==== &lt;br /&gt;
BCM2837B0 ist identisch wie BCM2837, Änderung der internen Frequenzen.&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* BCM2836 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2836/bcm2836-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A53: https://developer.arm.com/documentation/ddi0500/latest/&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 4 Model B, the Raspberry Pi 400, and the Raspberry Pi Compute Module 4 ====&lt;br /&gt;
* BCM2711 ARM Peripherals: https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
* Arm Cortex-A72: https://developer.arm.com/Processors/Cortex-A72 &lt;br /&gt;
&lt;br /&gt;
==== Raspberry Pi 5 ====&lt;br /&gt;
* rp1-peripherals: https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt; Information aus: https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Forum ==&lt;br /&gt;
Raspberry Pi Forum: https://www.raspberrypi.org/forums/&lt;br /&gt;
&lt;br /&gt;
== Bare Metal Projekte ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Bare Metal Projekte&lt;br /&gt;
|-&lt;br /&gt;
! Name !! RPI !! Programmiersprache !! Inhalt !! Link !! Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Writing a &amp;quot;bare metal&amp;quot; operating system for Raspberry Pi 4 || Pi4 || C (ARMv8) || UART, Grafik, Bluetooth, Sound, Multicore, Interrupts, SPI Ethernet, TCPIP Webserver|| https://www.rpi4os.com/ or https://github.com/isometimes/rpi4-osdev&amp;lt;nowiki/&amp;gt;|| Aktuell&lt;br /&gt;
|-&lt;br /&gt;
| Bare Metal Programming on Raspberry Pi 3 || Pi3 || C (ARMv8) || Multicore, UART1, Mailbox, UART0, Zufallszahlen, Timer, PowerOff, Grafik, Diskfunktionen, Memory  || https://github.com/bztsrc/raspi3-tutorial || 1 Jahr&lt;br /&gt;
|-&lt;br /&gt;
| Circle || Pi0-5 || C++ || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://github.com/rsta2/circle || 1 Monat&lt;br /&gt;
|-&lt;br /&gt;
| Ultibo || Pi0-4 || Free Pascal || Multicore, Interrupt (IRQ, FIQ), UART, GPIO, Timer, DMA, PWM, HDMI-Ton, WLAN, Ethernet, USB (HUB, HID, Speichergeräte, MIDI, Drucker), FAT, FatFs, Grafik || https://ultibo.org/ || ca. 1 Monat&lt;br /&gt;
|-&lt;br /&gt;
| Raspberry-Pi Bare Metal Tutorial || Pi0-4 || C || C Runtime, Interrupts, UART, Mailbox, Grafik || https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-1/ || ca. 4 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| USPi - A bare metal USB driver for Raspberry Pi 1-3 written in C || Pi0-3 || C || Circle in C || https://github.com/rsta2/uspi || ca. 10 Monate&lt;br /&gt;
|-&lt;br /&gt;
| dwelch67 || Pi0-3 || C || Float, Bootloader, MMU, SPI, UART, VIDEO, ZLIB || https://github.com/dwelch67/raspberrypi || ca. 6 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| ... || Pi1 || Assembler ||  || https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/index.html || ca. 12 Jahre&lt;br /&gt;
|-&lt;br /&gt;
| Learning operating system development using Linux kernel and Raspberry Pi || Pi3 || C || Printf, Interrupt, Memory &amp;lt;/br&amp;gt; Vergleicht Bare Metal und Linux || https://github.com/s-matyukevich/raspberry-pi-os || 3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|PeterLemon RaspberryPi&lt;br /&gt;
|Pi1-3&lt;br /&gt;
|Assembler&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/PeterLemon/RaspberryPi&lt;br /&gt;
|3 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|pi-baremetal&lt;br /&gt;
|Pi1&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/brianwiddas/pi-baremetal&lt;br /&gt;
|12 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|Raspberry Pi Bare Bones&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|QEMU&lt;br /&gt;
|http://wiki.osdev.org/Raspberry_Pi_Bare_Bones&lt;br /&gt;
|ca. 1,5 Jahre&lt;br /&gt;
|-&lt;br /&gt;
|circle-stdlib&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C++&lt;br /&gt;
|Circle StdLib&lt;br /&gt;
|https://github.com/smuehlst/circle-stdlib&lt;br /&gt;
|3 Monate&lt;br /&gt;
|-&lt;br /&gt;
|sean-lawless / computersystems&lt;br /&gt;
|Pi1-4&lt;br /&gt;
|C&lt;br /&gt;
|&lt;br /&gt;
|https://github.com/sean-lawless/computersystems/tree/master&lt;br /&gt;
|Aktuell&lt;br /&gt;
|-&lt;br /&gt;
|macoy rpi-system&lt;br /&gt;
|Pi4&lt;br /&gt;
|C&lt;br /&gt;
|Circle in C (nur RPI4)&lt;br /&gt;
|https://macoy.me/code/macoy/rpi-system&lt;br /&gt;
|Aktuell&lt;br /&gt;
|}&lt;br /&gt;
Stand Januar 2024&lt;br /&gt;
&lt;br /&gt;
== Nützliche Links ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Nützliche Links&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Inhalt !! Link&lt;br /&gt;
|-&lt;br /&gt;
| Introduction to Computer Organization: ARM Assembly Language Using the Raspberry Pi || ARM-Assembler || https://bob.cs.sonoma.edu/IntroCompOrg-RPi/intro-co-rpi.html&lt;br /&gt;
|-&lt;br /&gt;
| Arm GNU Toolchain Downloads || Toolchain || https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads&lt;br /&gt;
|-&lt;br /&gt;
| ARM assembler in Raspberry Pi || ARM-Assembler || https://thinkingeek.com/arm-assembler-raspberry-pi/&lt;br /&gt;
|-&lt;br /&gt;
| A Guide to ARM64 / AArch64 Assembly on Linux with Shellcodes and Cryptography || ARM-Assembler || https://modexp.wordpress.com/2018/10/30/arm64-assembly/&lt;br /&gt;
|-&lt;br /&gt;
| Découvrir l&#039;assembleur ARM sur Raspberry Pi || ARM-Assembler || https://assembleurarmpi.blogspot.com/ or https://github.com/vincentARM/ARMassembly64&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 A64 Quick Reference&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://courses.cs.washington.edu/courses/cse469/18wi/Materials/arm64.pdf&lt;br /&gt;
|-&lt;br /&gt;
|ARMv8 Instruction Set Overview&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://www.cs.princeton.edu/courses/archive/fall23/cos217/reading/ArmInstructionSetOverview.pdf&lt;br /&gt;
|-&lt;br /&gt;
|A64 Systemaufrufe&lt;br /&gt;
|ARM-Assembler&lt;br /&gt;
|https://arm64.syscall.sh/&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|https://s-matyukevich.github.io/raspberry-pi-os/&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Andere, nicht zugeordnet ==&lt;br /&gt;
Raspberry Pi DMA programming in C https://iosoft.blog/2020/05/25/raspberry-pi-dma-programming/&lt;br /&gt;
&lt;br /&gt;
Mailbox property interface https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Speicherverwaltung_(PI4)&amp;diff=1329</id>
		<title>Speicherverwaltung (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Speicherverwaltung_(PI4)&amp;diff=1329"/>
		<updated>2026-07-17T06:00:33Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die Speicherverwaltung (Memory Management) ist das fundamentale Fundament jedes Betriebssystems. Sie entscheidet darüber, ob das OS stabil wie ein Fels in der Brandung läuft oder sich im reinsten Chaos (Page Faults, Speicherlecks, zerschossene Datenstrukturen) verabschiedet.&lt;br /&gt;
&lt;br /&gt;
Hier ist eine strukturierte Übersicht darüber, was allgemein beachtet werden muss, wie sich der Betrieb mit und ohne MMU unterscheidet und warum kohärente Bereiche eine absolute Sonderrolle einnehmen.&lt;br /&gt;
&lt;br /&gt;
== Allgemeine Aspekte: Was gibt es zu beachten? ==&lt;br /&gt;
&lt;br /&gt;
Bei der Entwicklung eines Kernels musst der Speicher in zwei völlig verschiedenen Dimensionen verwaltet werden:&lt;br /&gt;
&lt;br /&gt;
* Der Page Allocator (Seiten-Ebene):&lt;br /&gt;
Verwaltet grobe Speicherblöcke (meist 4 KB oder 64 KB). Er entscheidet, welche physischen Speicherseiten frei oder belegt sind. Hierbei ist Schnelligkeit Trumpf. Oft wird dafür eine einfache Freiliste (Free-List) oder ein Buddy-Allocator verwendet.&lt;br /&gt;
&lt;br /&gt;
* Der Heap Allocator (Byte-Ebene):&lt;br /&gt;
Teilt die groben Seiten in winzige, beliebige Häppchen für das Programm auf (das klassische `malloc`/`free`).&lt;br /&gt;
&lt;br /&gt;
* Das Problem der Fragmentierung:&lt;br /&gt;
** Externe Fragmentierung: Es ist zwar genug Speicher frei, aber er ist in so viele winzige Lücken zerstückelt, dass kein großer zusammenhängender Block mehr allokiert werden kann.&lt;br /&gt;
** Interne Fragmentierung: Ein Allokator gibt dir einen größeren Block, als du eigentlich angefordert hast (z. B. 64 Bytes für einen 10-Byte-String), wodurch der Rest ungenutzt verpufft.&lt;br /&gt;
&lt;br /&gt;
*Cache-Alignment (Ausrichtung):&lt;br /&gt;
CPUs lesen Daten am liebsten in &amp;quot;Cache-Lines&amp;quot; (meist 64 Bytes). Liegt eine Datenstruktur ungünstig über eine Cache-Line-Grenze hinweg, muss die CPU zwei statt einem Zugriff ausführen. DMA-Puffer *müssen* zwingend auf Cache-Line-Grenzen ausgerichtet sein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Der MMU-Betrieb: Physisch vs. Virtuell ==&lt;br /&gt;
&lt;br /&gt;
Die **MMU (Memory Management Unit)** ist der Hardware-Übersetzer deiner CPU. Sie entscheidet grundlegend darüber, wie dein System Speicher sieht.&lt;br /&gt;
&lt;br /&gt;
=== Ohne MMU (Physische Adressierung / &amp;quot;Bare Metal&amp;quot;) ===&lt;br /&gt;
&lt;br /&gt;
Wie es läuft: Jede Adresse, die der Code anspricht, entspricht exakt der Leitung auf der Platine (Physische Adresse). Schreibt das Programm an `0x20000000`, landet es genau dort im echten RAM.&lt;br /&gt;
&lt;br /&gt;
Vorteile: Extrem schnell (keine Übersetzungs-Latenz) und einfach zu debuggen.&lt;br /&gt;
&lt;br /&gt;
Nachteile: Keine Sicherheit. Jedes Programm kann den Kernel-Speicher überschreiben. Zudem müssen alle Programme im selben physischen Speicherbereich koexistieren, was die Fragmentierung extrem verschlimmert.&lt;br /&gt;
&lt;br /&gt;
=== Mit MMU (Virtuelle Adressierung) ===&lt;br /&gt;
&lt;br /&gt;
Wie es läuft: Der Code arbeitet nur noch mit *virtuellen* Adressen. Die MMU übersetzt diese über mehrstufige Seitentabellen (Page Tables) transparent in physische Adressen.&lt;br /&gt;
&lt;br /&gt;
Die Superkräfte der MMU:&lt;br /&gt;
&lt;br /&gt;
Speicherschutz (Access Control): Man kann Speicherseiten als *Read-Only* (für Code) oder *Execute-Never (XN)* (für Daten, um Schadcode-Ausführung zu verhindern) markieren.&lt;br /&gt;
&lt;br /&gt;
Virtueller Adressraum: Jedes User-Programm kann denken, es besitze den gesamten Speicher (z. B. ab Adresse `0x0`). Die MMU mappt das im Hintergrund auf völlig verschiedene physische Bereiche im RAM.&lt;br /&gt;
&lt;br /&gt;
Caching-Attribute: Man kann im MAIR-Register (Memory Attribute Indirection Register) der CPU festlegen, welche Speicherbereiche rasant gecacht werden dürfen und welche direkt in die Hardware schreiben müssen.&lt;br /&gt;
&lt;br /&gt;
=== Was ist an Coherent-Bereichen besonders? ===&lt;br /&gt;
&lt;br /&gt;
Um zu verstehen, was &#039;&#039;&#039;Coherent (kohärente) Bereiche&#039;&#039;&#039; besonders macht, müssen wir uns das Problem moderner CPU-Caches ansehen.&lt;br /&gt;
&lt;br /&gt;
CPUs sind um ein Vielfaches schneller als der physische RAM. Deshalb laden sie häufig genutzte Daten in den ultraschnellen L1/L2-Cache. Wenn die CPU eine Variable ändert, ändert sie diese oft erst nur im Cache (*Write-Back*) und schreibt sie erst viel später in den echten RAM.&lt;br /&gt;
&lt;br /&gt;
  CPU Core &amp;lt;---&amp;gt; [ CPU-Cache ] (geänderte Daten)&lt;br /&gt;
                       |&lt;br /&gt;
                       x  &amp;lt;--- Hardware sieht die Änderung nicht!&lt;br /&gt;
                       v&lt;br /&gt;
  Hardware &amp;lt;---&amp;gt; [ Physischer RAM ] (alte Daten)&lt;br /&gt;
&lt;br /&gt;
=== Das Problem mit DMA (Direct Memory Access) ===&lt;br /&gt;
&lt;br /&gt;
Hardware-Peripherie (wie der PCIe-USB-Controller, die GPU oder eine Netzwerkkarte) hat &#039;&#039;&#039;keinen Zugriff auf die CPU-Caches&#039;&#039;&#039;. Sie liest und schreibt immer direkt im physischen RAM.&lt;br /&gt;
&lt;br /&gt;
* Schreibt die CPU Daten für den USB-Stick in einen normalen Speicherbereich, liegen sie im Cache. Der USB-Controller startet den DMA-Transfer, liest aber die veralteten Daten aus dem echten RAM. **Ergebnis: Datenkorruption.**&lt;br /&gt;
&lt;br /&gt;
### Die Lösung: Der Coherent-Bereich&lt;br /&gt;
&lt;br /&gt;
Ein kohärenter Speicherbereich (oft auch *Non-Cacheable* oder *Device-Memory* genannt) löst dieses Problem radikal, indem er das Caching komplett abschaltet.&lt;br /&gt;
&lt;br /&gt;
* **Kein Caching:** Jeder Lese- und Schreibzugriff der CPU geht unter Umgehung aller Caches direkt in den physischen RAM.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* **Sofortige Sichtbarkeit:** Sobald die CPU ein Byte dorthin schreibt, kann eine Hardware-Komponente es sofort per DMA lesen (und umgekehrt).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* **Der Preis:** Da die CPU nicht mehr auf den Cache zugreifen kann, sind Zugriffe auf kohärenten Speicher deutlich langsamer. Deshalb nutzt man sie ausschließlich für kleine Kommunikations-Puffer (wie Mailbox-Strukturen oder DMA-Deskriptoren) und niemals für Berechnungen oder normalen Programmcode.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
Möchtest du als Nächstes tiefer in die konkrete Einrichtung der MMU-Seitentabellen für deinen Kernel einsteigen, um diese Bereiche sauber voneinander abzugrenzen?&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1328</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1328"/>
		<updated>2026-07-17T05:37:16Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Raspberry Pi 4 C (64-Bit) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;C und ARM-Assemblerprogrammierung mit dem Raspberry&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[Datei:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © by 2020-2025&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vorwort ===&lt;br /&gt;
&lt;br /&gt;
Herzlich willkommen auf unserer Seite zur Bare-Metal-Programmierung für den Raspberry Pi 4 und Pi 5! Diese Webseite richtet sich sowohl an Anfänger als auch an fortgeschrittene Programmierer, die tief in die Welt der hardwarenahen Programmierung eintauchen möchten.&lt;br /&gt;
&lt;br /&gt;
Ursprünglich sollte diese Seite sich ausschließlich mit Assembler beschäftigen, wurde jedoch im Laufe der Zeit um C erweitert. Daher finden Sie hier zunächst grundlegende Informationen zu Assembler. Wenn Ihr Interesse in der C-Programmierung liegt und Sie mit Windows arbeiten, empfehlen wir Ihnen mit dem Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen (Konsole)]]&#039;&#039;&#039; zu beginnen. Für Linux-Nutzer ist das Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen unter Linux]]&#039;&#039;&#039; der richtige Startpunkt.&lt;br /&gt;
&lt;br /&gt;
Im Kapitel &#039;&#039;&#039;Raspberry Pi 4 Assembler 32-Bit&#039;&#039;&#039; beschreiben wir die Assembler-Programmierung für den Raspberry Pi 4. Dieses Kapitel hat zwar bereits einige Jahre auf dem Buckel, bleibt aber weiterhin relevant und wertvoll. Hier finden Sie detaillierte Erklärungen der ARM-32-Bit-Befehle mit anschaulichen Beschreibungen und Beispielen, was besonders Anfängern den Einstieg erleichtern soll.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 4 C (64-Bit)&#039;&#039;&#039; entstand im Laufe unserer Arbeit am Raspberry Pi 5 und bietet eine umfassende Beschreibung der 64-Bit-C-Programmierung für den Raspberry Pi 4. Dies wird auch später die USB-Programmierung einschließen, eine Funktion, die wir später auf den Raspberry Pi 5 übertragen werden. So haben alle Projekte einen gemeinsamen Ausgangspunkt.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 5&#039;&#039;&#039; ist der ideale Startpunkt für die Programmierung des Raspberry Pi 5 in Assembler und C. Es führt Sie von der Installation des Hostsystems (Windows/Linux) bis zur abschließenden Programmierung in C und Assembler.&lt;br /&gt;
&lt;br /&gt;
Im Anhang finden Sie viele nützliche Informationen, wie eine Beschreibung der &#039;&#039;&#039;GNU Compiler Collection&#039;&#039;&#039;, eine Auflistung des &#039;&#039;&#039;ARM-Befehlssatzes&#039;&#039;&#039; (im Aufbau), verschiedene &#039;&#039;&#039;Raspberry Pi Modelle&#039;&#039;&#039; und einige hilfreiche &#039;&#039;&#039;Links&#039;&#039;&#039; zu anderen Projekten. Ein aktuelles Projekt zum Thema &#039;&#039;&#039;Debugging&#039;&#039;&#039; ist ebenfalls dort zu finden.&lt;br /&gt;
&lt;br /&gt;
Wir hoffen, dass Sie auf unserer Seite viel Freude haben und sich gut zurechtfinden! Bei Fragen oder Kommentaren schicken Sie uns gerne eine E-Mail an assem@satyria.de. Wir freuen uns über Ihre Rückmeldungen und Anregungen.&lt;br /&gt;
&lt;br /&gt;
Viel Spaß beim Programmieren!&lt;br /&gt;
&lt;br /&gt;
== Sponsor ==&lt;br /&gt;
Wenn ihr uns etwas unterstützen möchtet, schaut einfach mal hier vorbei:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Logo hexenlaedle.png|frameless|200x200px|link=https://www.hexenlaedle.de]]&lt;br /&gt;
&lt;br /&gt;
== Inhalt ==&lt;br /&gt;
*[[Vorwort]]&lt;br /&gt;
*[[Grundlegendes zu Assembler]]&lt;br /&gt;
**[[Grundlegendes zu Assembler#Der ARM-Assembler|Der ARM-Assembler]]&lt;br /&gt;
*[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
*[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5]]&lt;br /&gt;
**[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5#Warum C und Assembler?|Warum C und Assembler?]]&lt;br /&gt;
*[[Programm auf dem Raspberry Pi 5 ausführen]]&lt;br /&gt;
*[[Arbeiten mit Make und Linker-Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Unser erstes Programm (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung]]&lt;br /&gt;
*[[Grafik (PI5)]]&lt;br /&gt;
*[[Chars (PI5)]]&lt;br /&gt;
*Das Terminal (PI5)&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI5)]]&lt;br /&gt;
*[[Grafik in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)]]&lt;br /&gt;
*[[Das Terminal in C (PI5)]]&lt;br /&gt;
*[[printf in BareMetal (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Programmierumgebung erstellen (32-Bit)]]&lt;br /&gt;
*[[Systemprogrammierung / Bare Metal]]&lt;br /&gt;
*[[Das erste Programm]]&lt;br /&gt;
*[[General Purpose I/O]]&lt;br /&gt;
*[[System Timer]]&lt;br /&gt;
*[[UART]]&lt;br /&gt;
*[[Die Anzeige]]&lt;br /&gt;
*[[Zeichenfunktionen]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 C (64-Bit) ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI4)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI4)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI4)]]&lt;br /&gt;
*[[Grafik in C (PI4)]]&lt;br /&gt;
*[[Chars in C (PI4)]]&lt;br /&gt;
*[[Das Terminal in C (PI4)]]&lt;br /&gt;
*[[Printf in BareMetal (PI4)]]&lt;br /&gt;
*[[Systeminformationen (PI4)]] (noch kein Inhalt)&lt;br /&gt;
*[[Interrupts (PI4)]]&lt;br /&gt;
**[[Beispiel Timer-Interrupt (PI4)]]&lt;br /&gt;
*[[Interrupt Teil 2 (PI4)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--*[[USB (PI4)]]--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ARM64 Assembler ==&lt;br /&gt;
*[[Programmieren mit ARM64 Assembler]]&lt;br /&gt;
&lt;br /&gt;
== Zusätzliches Material ==&lt;br /&gt;
*[[Links der verwendeten Software]]&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
*[[GNU Compiler Collection]]&lt;br /&gt;
*[[ARM-Befehlssatz]]&lt;br /&gt;
**[[Grundlegende Befehle]]&lt;br /&gt;
&amp;lt;!--**[[ARMv8-Übersicht]]--&amp;gt;&lt;br /&gt;
*[[Raspberry PI Modelle]]&lt;br /&gt;
**[[Basisadressen der Modelle]]&lt;br /&gt;
*[[LINKS]]&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging (JTAG, RPI 4)]]&lt;br /&gt;
&amp;lt;!--*[[Bare-Metal Debugging]]--&amp;gt;&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4]]&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5]]&lt;br /&gt;
----&lt;br /&gt;
*[[Die wichtigsten Linux-Terminal-Befehle]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[English]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=USB_(PI4)&amp;diff=1327</id>
		<title>USB (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=USB_(PI4)&amp;diff=1327"/>
		<updated>2026-07-17T05:36:41Z</updated>

		<summary type="html">&lt;p&gt;Satyria: Die Seite wurde neu angelegt: „Informationen:  usbconfigparser.c: Wichtig hier zu wissen: Warum wird hier &amp;quot;Audio&amp;quot; erwähnt? Bei Audio-Geräten, wie USB-Headset gibt es im USB-Standard eine Besonderheit: Ein normaler Endpoint-Deskriptor (DESCRIPTOR_ENDPOINT) hat eine feste Länge von 9 Bytes (sizeof(TUSBEndpointDescriptor)). Bei USB Audio Class 1.0 Geräten enthält der Endpoint-Deskriptor jedoch zusätzliche Audiodaten (z.B. für Synchronisation oder Refresh-Raten) und ist daher 11 Byt…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Informationen:&lt;br /&gt;
&lt;br /&gt;
usbconfigparser.c:&lt;br /&gt;
Wichtig hier zu wissen:&lt;br /&gt;
Warum wird hier &amp;quot;Audio&amp;quot; erwähnt?&lt;br /&gt;
Bei Audio-Geräten, wie USB-Headset gibt es im USB-Standard eine Besonderheit:&lt;br /&gt;
Ein normaler Endpoint-Deskriptor (DESCRIPTOR_ENDPOINT) hat eine feste Länge von 9 Bytes (sizeof(TUSBEndpointDescriptor)).&lt;br /&gt;
Bei USB Audio Class 1.0 Geräten enthält der Endpoint-Deskriptor jedoch zusätzliche Audiodaten (z.B. für Synchronisation&lt;br /&gt;
oder Refresh-Raten) und ist daher 11 Bytes groß.&lt;br /&gt;
&lt;br /&gt;
Wenn der Parser auf ein Interface stößt, das zur USB-Audioklasse gehört:&lt;br /&gt;
&lt;br /&gt;
  bInAudio10Interface = pCurrentPosition-&amp;gt;Interface.bInterfaceClass == 0x01&lt;br /&gt;
                      &amp;amp;&amp;amp; pCurrentPosition-&amp;gt;Interface.bInterfaceProtocol != 0x20;&lt;br /&gt;
&lt;br /&gt;
...merkt er sich das über die Flag bInAudio10Interface.&lt;br /&gt;
Sobald er danach den dazugehörigen Endpoint-Deskriptor analysiert:&lt;br /&gt;
&lt;br /&gt;
  if (bInAudio10Interface)&lt;br /&gt;
  {&lt;br /&gt;
    // Audio class 1.0 EP descriptors have additional fields.&lt;br /&gt;
    ucAlternateLen = sizeof (TUSBAudioEndpointDescriptor);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
...erlaubt er als alternative, gültige Länge auch die 11 Bytes der Audio-Spezifikation. Ohne diese Unterscheidung&lt;br /&gt;
würde der Parser den Deskriptor fälschlicherweise als fehlerhaft (beschädigt) einstufen und das Gerät ablehnen.&lt;br /&gt;
&lt;br /&gt;
Warum ist das hier wichtig:&lt;br /&gt;
Sicherheit vor Abstürzen: Sollte ein Benutzer jemals versehentlich ein USB-Headset oder ein USB-Mikrofon in den&lt;br /&gt;
Raspberry Pi stecken, sorgt dieser Code dafür, dass der USB-Stack das Gerät sauber als „nicht unterstützt“ ignoriert.&lt;br /&gt;
Sollte dies fehlen, würde der Parser bei einem solchen Gerät einen Invalid Configuration Descriptor-Fehler werfen und&lt;br /&gt;
den gesamten USB-Port oder -Stack blockieren.&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1326</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1326"/>
		<updated>2026-07-17T05:35:53Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Raspberry Pi 4 C (64-Bit) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;C und ARM-Assemblerprogrammierung mit dem Raspberry&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[Datei:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © by 2020-2025&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vorwort ===&lt;br /&gt;
&lt;br /&gt;
Herzlich willkommen auf unserer Seite zur Bare-Metal-Programmierung für den Raspberry Pi 4 und Pi 5! Diese Webseite richtet sich sowohl an Anfänger als auch an fortgeschrittene Programmierer, die tief in die Welt der hardwarenahen Programmierung eintauchen möchten.&lt;br /&gt;
&lt;br /&gt;
Ursprünglich sollte diese Seite sich ausschließlich mit Assembler beschäftigen, wurde jedoch im Laufe der Zeit um C erweitert. Daher finden Sie hier zunächst grundlegende Informationen zu Assembler. Wenn Ihr Interesse in der C-Programmierung liegt und Sie mit Windows arbeiten, empfehlen wir Ihnen mit dem Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen (Konsole)]]&#039;&#039;&#039; zu beginnen. Für Linux-Nutzer ist das Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen unter Linux]]&#039;&#039;&#039; der richtige Startpunkt.&lt;br /&gt;
&lt;br /&gt;
Im Kapitel &#039;&#039;&#039;Raspberry Pi 4 Assembler 32-Bit&#039;&#039;&#039; beschreiben wir die Assembler-Programmierung für den Raspberry Pi 4. Dieses Kapitel hat zwar bereits einige Jahre auf dem Buckel, bleibt aber weiterhin relevant und wertvoll. Hier finden Sie detaillierte Erklärungen der ARM-32-Bit-Befehle mit anschaulichen Beschreibungen und Beispielen, was besonders Anfängern den Einstieg erleichtern soll.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 4 C (64-Bit)&#039;&#039;&#039; entstand im Laufe unserer Arbeit am Raspberry Pi 5 und bietet eine umfassende Beschreibung der 64-Bit-C-Programmierung für den Raspberry Pi 4. Dies wird auch später die USB-Programmierung einschließen, eine Funktion, die wir später auf den Raspberry Pi 5 übertragen werden. So haben alle Projekte einen gemeinsamen Ausgangspunkt.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 5&#039;&#039;&#039; ist der ideale Startpunkt für die Programmierung des Raspberry Pi 5 in Assembler und C. Es führt Sie von der Installation des Hostsystems (Windows/Linux) bis zur abschließenden Programmierung in C und Assembler.&lt;br /&gt;
&lt;br /&gt;
Im Anhang finden Sie viele nützliche Informationen, wie eine Beschreibung der &#039;&#039;&#039;GNU Compiler Collection&#039;&#039;&#039;, eine Auflistung des &#039;&#039;&#039;ARM-Befehlssatzes&#039;&#039;&#039; (im Aufbau), verschiedene &#039;&#039;&#039;Raspberry Pi Modelle&#039;&#039;&#039; und einige hilfreiche &#039;&#039;&#039;Links&#039;&#039;&#039; zu anderen Projekten. Ein aktuelles Projekt zum Thema &#039;&#039;&#039;Debugging&#039;&#039;&#039; ist ebenfalls dort zu finden.&lt;br /&gt;
&lt;br /&gt;
Wir hoffen, dass Sie auf unserer Seite viel Freude haben und sich gut zurechtfinden! Bei Fragen oder Kommentaren schicken Sie uns gerne eine E-Mail an assem@satyria.de. Wir freuen uns über Ihre Rückmeldungen und Anregungen.&lt;br /&gt;
&lt;br /&gt;
Viel Spaß beim Programmieren!&lt;br /&gt;
&lt;br /&gt;
== Sponsor ==&lt;br /&gt;
Wenn ihr uns etwas unterstützen möchtet, schaut einfach mal hier vorbei:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Logo hexenlaedle.png|frameless|200x200px|link=https://www.hexenlaedle.de]]&lt;br /&gt;
&lt;br /&gt;
== Inhalt ==&lt;br /&gt;
*[[Vorwort]]&lt;br /&gt;
*[[Grundlegendes zu Assembler]]&lt;br /&gt;
**[[Grundlegendes zu Assembler#Der ARM-Assembler|Der ARM-Assembler]]&lt;br /&gt;
*[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
*[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5]]&lt;br /&gt;
**[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5#Warum C und Assembler?|Warum C und Assembler?]]&lt;br /&gt;
*[[Programm auf dem Raspberry Pi 5 ausführen]]&lt;br /&gt;
*[[Arbeiten mit Make und Linker-Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Unser erstes Programm (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung]]&lt;br /&gt;
*[[Grafik (PI5)]]&lt;br /&gt;
*[[Chars (PI5)]]&lt;br /&gt;
*Das Terminal (PI5)&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI5)]]&lt;br /&gt;
*[[Grafik in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)]]&lt;br /&gt;
*[[Das Terminal in C (PI5)]]&lt;br /&gt;
*[[printf in BareMetal (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Programmierumgebung erstellen (32-Bit)]]&lt;br /&gt;
*[[Systemprogrammierung / Bare Metal]]&lt;br /&gt;
*[[Das erste Programm]]&lt;br /&gt;
*[[General Purpose I/O]]&lt;br /&gt;
*[[System Timer]]&lt;br /&gt;
*[[UART]]&lt;br /&gt;
*[[Die Anzeige]]&lt;br /&gt;
*[[Zeichenfunktionen]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 C (64-Bit) ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI4)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI4)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI4)]]&lt;br /&gt;
*[[Grafik in C (PI4)]]&lt;br /&gt;
*[[Chars in C (PI4)]]&lt;br /&gt;
*[[Das Terminal in C (PI4)]]&lt;br /&gt;
*[[Printf in BareMetal (PI4)]]&lt;br /&gt;
*[[Systeminformationen (PI4)]] (noch kein Inhalt)&lt;br /&gt;
*[[Interrupts (PI4)]]&lt;br /&gt;
**[[Beispiel Timer-Interrupt (PI4)]]&lt;br /&gt;
*[[Interrupt Teil 2 (PI4)]]&lt;br /&gt;
&lt;br /&gt;
*[[USB (PI4)]]&lt;br /&gt;
&lt;br /&gt;
== ARM64 Assembler ==&lt;br /&gt;
*[[Programmieren mit ARM64 Assembler]]&lt;br /&gt;
&lt;br /&gt;
== Zusätzliches Material ==&lt;br /&gt;
*[[Links der verwendeten Software]]&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
*[[GNU Compiler Collection]]&lt;br /&gt;
*[[ARM-Befehlssatz]]&lt;br /&gt;
**[[Grundlegende Befehle]]&lt;br /&gt;
&amp;lt;!--**[[ARMv8-Übersicht]]--&amp;gt;&lt;br /&gt;
*[[Raspberry PI Modelle]]&lt;br /&gt;
**[[Basisadressen der Modelle]]&lt;br /&gt;
*[[LINKS]]&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging (JTAG, RPI 4)]]&lt;br /&gt;
&amp;lt;!--*[[Bare-Metal Debugging]]--&amp;gt;&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4]]&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5]]&lt;br /&gt;
----&lt;br /&gt;
*[[Die wichtigsten Linux-Terminal-Befehle]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[English]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1325</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1325"/>
		<updated>2026-06-24T08:51:43Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Umgebungsvariable „Path“ anpassen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt und speichern diese ins Verzeichnis &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
C:\Program Files (x86)\GnuWin32\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 4 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1324</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1324"/>
		<updated>2026-06-24T08:51:09Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Umgebungsvariable „Path“ anpassen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt und speichern diese ins Verzeichnis &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 4 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1323</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1323"/>
		<updated>2026-06-24T08:50:51Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Umgebungsvariable „Path“ anpassen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt und speichern diese ins Verzeichnis &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
C:\Program Files (x86)\GnuWin32\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 4 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1322</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1322"/>
		<updated>2026-06-24T08:46:34Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* OpenOCD einrichten und konfigurieren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt und speichern diese ins Verzeichnis &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 4 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1321</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1321"/>
		<updated>2026-06-05T13:55:09Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Zusammenfassung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 4 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1320</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1320"/>
		<updated>2026-06-05T13:54:36Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die Debugger-Ansichten nutzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A72 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1319</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1319"/>
		<updated>2026-06-05T13:53:15Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und Debugging in der GUI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
=== Hardware verbinden ===&lt;br /&gt;
&lt;br /&gt;
==== Verdrahtung ====&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1318</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1318"/>
		<updated>2026-06-05T13:52:07Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 4 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 4 muss wissen, dass wir ein Bare-Metal-Programm via JTAG debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2711-rpi-4-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bootcode.bin&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;fixup4.dat&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;start4.elf&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1317</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1317"/>
		<updated>2026-06-05T13:42:39Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Linkerscript und Makefile erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 4 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 4.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 4 (&#039;&#039;&#039;-mcpu=cortex-a72&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1316</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1316"/>
		<updated>2026-06-05T13:40:26Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die .vscode Konfigurationsdateien anlegen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 4 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1315</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1315"/>
		<updated>2026-06-05T13:39:49Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Die .vscode Konfigurationsdateien anlegen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 4)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1314</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1314"/>
		<updated>2026-06-05T13:38:21Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und dem FT232H Modul verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zusätzlich benötigen wir ein Target-Profil für den Broadcom-Chip BCM2711 des Raspberry Pi 4. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2711.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
# SPDX-License-Identifier: GPL-2.0-or-later&lt;br /&gt;
&lt;br /&gt;
# The Broadcom BCM2711 used in Raspberry Pi 4&lt;br /&gt;
# No documentation was found on Broadcom website&lt;br /&gt;
&lt;br /&gt;
# Partial information is available on the Raspberry Pi website:&lt;br /&gt;
#  https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
	set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
	set  _CHIPNAME bcm2711&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
	set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
	set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
	set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
	set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
	set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
	set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
jtag newtap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# these addresses are obtained from the ROM table via &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}&lt;br /&gt;
set _CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
	set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
	set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
	cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
	target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
	set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
	eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 4) - FT232H&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel8.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/ft232h-jtag.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2711.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;adapter speed 3000&amp;quot;,&lt;br /&gt;
                &amp;quot;init&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor wait_halt 2000&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;showDevDebugOutput&amp;quot;: &amp;quot;raw&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI4)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x1F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The LED program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_on (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPSET = GPIO_GPSET0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPSET = GPSET + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPSET,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_off (void)&lt;br /&gt;
{&lt;br /&gt;
  u32 LED_Pin = 42;&lt;br /&gt;
  SetGPIOFunction(LED_Pin,GPIO_output);&lt;br /&gt;
&lt;br /&gt;
  u32 GPCLR = GPIO_GPCLR0; &lt;br /&gt;
  if (LED_Pin&amp;gt;31)&lt;br /&gt;
  {&lt;br /&gt;
    GPCLR = GPCLR + 4;&lt;br /&gt;
    LED_Pin = LED_Pin - 32;&lt;br /&gt;
  }&lt;br /&gt;
  write32(GPCLR,1&amp;lt;&amp;lt;LED_Pin);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function)&lt;br /&gt;
{&lt;br /&gt;
  u32 GPSEL = GPIO_GPFSEL0;&lt;br /&gt;
  while (Pin &amp;gt;= 10)&lt;br /&gt;
  {&lt;br /&gt;
    Pin = Pin -10;&lt;br /&gt;
    GPSEL = GPSEL + 4;&lt;br /&gt;
  }&lt;br /&gt;
  Pin = Pin *3;&lt;br /&gt;
  Function = Function &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  u32 mask = 0b111 &amp;lt;&amp;lt; Pin;&lt;br /&gt;
  mask =~mask; //Bitweise negieren&lt;br /&gt;
&lt;br /&gt;
  u32 sel=read32(GPSEL);&lt;br /&gt;
  sel=sel&amp;amp;mask;&lt;br /&gt;
  sel=sel|Function;&lt;br /&gt;
  write32(GPSEL,sel);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE 0xFE000000&lt;br /&gt;
&lt;br /&gt;
// General Purpose I/O (GPIO)                 &lt;br /&gt;
#define GPIO_BASE RPI_BASE + 0x200000               &lt;br /&gt;
#define GPIO_GPFSEL0                    GPIO_BASE + 0x00 // GPIO Function Select 0        &lt;br /&gt;
#define GPIO_GPFSEL1                    GPIO_BASE + 0x04 // GPIO Function Select 1        &lt;br /&gt;
#define GPIO_GPFSEL2                    GPIO_BASE + 0x08 // GPIO Function Select 2        &lt;br /&gt;
#define GPIO_GPFSEL3                    GPIO_BASE + 0x0c // GPIO Function Select 3        &lt;br /&gt;
#define GPIO_GPFSEL4                    GPIO_BASE + 0x10 // GPIO Function Select 4        &lt;br /&gt;
#define GPIO_GPFSEL5                    GPIO_BASE + 0x14 // GPIO Function Select 5        &lt;br /&gt;
#define GPIO_GPSET0                     GPIO_BASE + 0x1c // GPIO Pin Output Set 0       &lt;br /&gt;
#define GPIO_GPSET1                     GPIO_BASE + 0x20 // GPIO Pin Output Set 1       &lt;br /&gt;
#define GPIO_GPCLR0                     GPIO_BASE + 0x28 // GPIO Pin Output Clear 0       &lt;br /&gt;
#define GPIO_GPCLR1                     GPIO_BASE + 0x2c // GPIO Pin Output Clear 1       &lt;br /&gt;
#define GPIO_GPLEV0                     GPIO_BASE + 0x34 // GPIO Pin Level 0        &lt;br /&gt;
#define GPIO_GPLEV1                     GPIO_BASE + 0x38 // GPIO Pin Level 1        &lt;br /&gt;
#define GPIO_GPEDS0                     GPIO_BASE + 0x40 // GPIO Pin Event Detect Status 0      &lt;br /&gt;
#define GPIO_GPEDS1                     GPIO_BASE + 0x44 // GPIO Pin Event Detect Status 1      &lt;br /&gt;
#define GPIO_GPREN0                     GPIO_BASE + 0x4c // GPIO Pin Rising Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPREN1                     GPIO_BASE + 0x50 // GPIO Pin Rising Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPFEN0                     GPIO_BASE + 0x58 // GPIO Pin Falling Edge Detect Enable 0     &lt;br /&gt;
#define GPIO_GPFEN1                     GPIO_BASE + 0x5c // GPIO Pin Falling Edge Detect Enable 1     &lt;br /&gt;
#define GPIO_GPHEN0                     GPIO_BASE + 0x64 // GPIO Pin High Detect Enable 0      &lt;br /&gt;
#define GPIO_GPHEN1                     GPIO_BASE + 0x68 // GPIO Pin High Detect Enable 1      &lt;br /&gt;
#define GPIO_GPLEN0                     GPIO_BASE + 0x70 // GPIO Pin Low Detect Enable 0      &lt;br /&gt;
#define GPIO_GPLEN1                     GPIO_BASE + 0x74 // GPIO Pin Low Detect Enable 1      &lt;br /&gt;
#define GPIO_GPAREN0                    GPIO_BASE + 0x7c // GPIO Pin Async. Rising Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAREN1                    GPIO_BASE + 0x80 // GPIO Pin Async. Rising Edge Detect 1     &lt;br /&gt;
#define GPIO_GPAFEN0                    GPIO_BASE + 0x88 // GPIO Pin Async. Falling Edge Detect 0     &lt;br /&gt;
#define GPIO_GPAFEN1                    GPIO_BASE + 0x8c // GPIO Pin Async. Falling Edge Detect 1     &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG0    GPIO_BASE + 0xe4 // GPIO Pull-up / Pull-down Register 0      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG1    GPIO_BASE + 0xe8 // GPIO Pull-up / Pull-down Register 1      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG2    GPIO_BASE + 0xec // GPIO Pull-up / Pull-down Register 2      &lt;br /&gt;
#define GPIO_GPIO_PUP_PDN_CNTRL_REG3    GPIO_BASE + 0xf0 // GPIO Pull-up / Pull-down Register 3      &lt;br /&gt;
// Information from BCM2835 ARM Peropherals                 &lt;br /&gt;
#define GPIO_GPPUD                      GPIO_BASE + 0x94 // GPIO Pin Pull-up/down Enable        &lt;br /&gt;
#define GPIO_GPPUDCLK0                  GPIO_BASE + 0x98 // GPIO Pin Pull-up/down Enable Clock 0      &lt;br /&gt;
#define GPIO_GPPUDCLK1                  GPIO_BASE + 0x9c // GPIO Pin Pull-up/down Enable Clock 1      &lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE          0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START  0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE   (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END    (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK  (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define GPIO_input  0b000      //Input&lt;br /&gt;
#define GPIO_output 0b001      //Output&lt;br /&gt;
#define GPIO_alt0   0b100      //alternate function 0&lt;br /&gt;
#define GPIO_alt1   0b101      //alternate function 1&lt;br /&gt;
#define GPIO_alt2   0b110      //alternate function 2&lt;br /&gt;
#define GPIO_alt3   0b111      //alternate function 3&lt;br /&gt;
#define GPIO_alt4   0b011      //alternate function 4&lt;br /&gt;
#define GPIO_alt5   0b010      //alternate function 5&lt;br /&gt;
&lt;br /&gt;
#define GPPUD_OFF    0b00&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
void SetGPIOFunction(u32 Pin, u32 Function);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char   u8;&lt;br /&gt;
typedef unsigned short  u16;&lt;br /&gt;
typedef unsigned int    u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char     s8;&lt;br /&gt;
typedef signed short    s16;&lt;br /&gt;
typedef signed int      s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   u64;&lt;br /&gt;
typedef signed long     s64;&lt;br /&gt;
&lt;br /&gt;
typedef long            intptr;&lt;br /&gt;
typedef unsigned long   uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long   size_t;&lt;br /&gt;
typedef long            ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char            boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)  __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE     0&lt;br /&gt;
#define TRUE      1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u32 a, u32 b);&lt;br /&gt;
u32 read32(u32 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
	.init : {&lt;br /&gt;
		*(.init)&lt;br /&gt;
	}&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
	.init_array : {&lt;br /&gt;
		__init_start = .;&lt;br /&gt;
		KEEP(*(.init_array*))&lt;br /&gt;
		__init_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.ARM.exidx : {&lt;br /&gt;
		__exidx_start = .;&lt;br /&gt;
		*(.ARM.exidx*)&lt;br /&gt;
		__exidx_end = .;&lt;br /&gt;
	}&lt;br /&gt;
	.eh_frame : {&lt;br /&gt;
		*(.eh_frame*)&lt;br /&gt;
	}&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 4 in den JTAG-Modus um. Dadurch wird die Kommunikation mit dem FT232H Modul überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 4.&lt;br /&gt;
* Schließen Sie das FT232H Modul per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 4 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel8.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel8.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 4 über das FT232H Modul auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 4 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Wichtiger Hinweis zum Reboot ===&lt;br /&gt;
Da es nicht möglich ist, ein Reset an den Raspberry Pi 4 zu schicken, muss vor jedem Neukompilieren der Raspberry Pi 4 neu gestartet werden. &lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1313</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_(64-Bit)_auf_dem_Raspberry_Pi_4&amp;diff=1313"/>
		<updated>2026-06-05T13:08:33Z</updated>

		<summary type="html">&lt;p&gt;Satyria: Die Seite wurde neu angelegt: „Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.  Um direkt mit dem Raspberry Pi 5 zu kommunizieren u…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1312</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Hauptseite&amp;diff=1312"/>
		<updated>2026-06-05T13:07:54Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Anhang */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;strong&amp;gt;C und ARM-Assemblerprogrammierung mit dem Raspberry&amp;lt;/strong&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
[[Datei:EinhornOrg.png|frameless|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Matthias Steiner&lt;br /&gt;
&lt;br /&gt;
Satyria Press © by 2020-2025&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Vorwort ===&lt;br /&gt;
&lt;br /&gt;
Herzlich willkommen auf unserer Seite zur Bare-Metal-Programmierung für den Raspberry Pi 4 und Pi 5! Diese Webseite richtet sich sowohl an Anfänger als auch an fortgeschrittene Programmierer, die tief in die Welt der hardwarenahen Programmierung eintauchen möchten.&lt;br /&gt;
&lt;br /&gt;
Ursprünglich sollte diese Seite sich ausschließlich mit Assembler beschäftigen, wurde jedoch im Laufe der Zeit um C erweitert. Daher finden Sie hier zunächst grundlegende Informationen zu Assembler. Wenn Ihr Interesse in der C-Programmierung liegt und Sie mit Windows arbeiten, empfehlen wir Ihnen mit dem Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen (Konsole)]]&#039;&#039;&#039; zu beginnen. Für Linux-Nutzer ist das Kapitel &#039;&#039;&#039;[[Programmierumgebung erstellen unter Linux]]&#039;&#039;&#039; der richtige Startpunkt.&lt;br /&gt;
&lt;br /&gt;
Im Kapitel &#039;&#039;&#039;Raspberry Pi 4 Assembler 32-Bit&#039;&#039;&#039; beschreiben wir die Assembler-Programmierung für den Raspberry Pi 4. Dieses Kapitel hat zwar bereits einige Jahre auf dem Buckel, bleibt aber weiterhin relevant und wertvoll. Hier finden Sie detaillierte Erklärungen der ARM-32-Bit-Befehle mit anschaulichen Beschreibungen und Beispielen, was besonders Anfängern den Einstieg erleichtern soll.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 4 C (64-Bit)&#039;&#039;&#039; entstand im Laufe unserer Arbeit am Raspberry Pi 5 und bietet eine umfassende Beschreibung der 64-Bit-C-Programmierung für den Raspberry Pi 4. Dies wird auch später die USB-Programmierung einschließen, eine Funktion, die wir später auf den Raspberry Pi 5 übertragen werden. So haben alle Projekte einen gemeinsamen Ausgangspunkt.&lt;br /&gt;
&lt;br /&gt;
Das Kapitel &#039;&#039;&#039;Raspberry Pi 5&#039;&#039;&#039; ist der ideale Startpunkt für die Programmierung des Raspberry Pi 5 in Assembler und C. Es führt Sie von der Installation des Hostsystems (Windows/Linux) bis zur abschließenden Programmierung in C und Assembler.&lt;br /&gt;
&lt;br /&gt;
Im Anhang finden Sie viele nützliche Informationen, wie eine Beschreibung der &#039;&#039;&#039;GNU Compiler Collection&#039;&#039;&#039;, eine Auflistung des &#039;&#039;&#039;ARM-Befehlssatzes&#039;&#039;&#039; (im Aufbau), verschiedene &#039;&#039;&#039;Raspberry Pi Modelle&#039;&#039;&#039; und einige hilfreiche &#039;&#039;&#039;Links&#039;&#039;&#039; zu anderen Projekten. Ein aktuelles Projekt zum Thema &#039;&#039;&#039;Debugging&#039;&#039;&#039; ist ebenfalls dort zu finden.&lt;br /&gt;
&lt;br /&gt;
Wir hoffen, dass Sie auf unserer Seite viel Freude haben und sich gut zurechtfinden! Bei Fragen oder Kommentaren schicken Sie uns gerne eine E-Mail an assem@satyria.de. Wir freuen uns über Ihre Rückmeldungen und Anregungen.&lt;br /&gt;
&lt;br /&gt;
Viel Spaß beim Programmieren!&lt;br /&gt;
&lt;br /&gt;
== Sponsor ==&lt;br /&gt;
Wenn ihr uns etwas unterstützen möchtet, schaut einfach mal hier vorbei:&lt;br /&gt;
&lt;br /&gt;
[[Datei:Logo hexenlaedle.png|frameless|200x200px|link=https://www.hexenlaedle.de]]&lt;br /&gt;
&lt;br /&gt;
== Inhalt ==&lt;br /&gt;
*[[Vorwort]]&lt;br /&gt;
*[[Grundlegendes zu Assembler]]&lt;br /&gt;
**[[Grundlegendes zu Assembler#Der ARM-Assembler|Der ARM-Assembler]]&lt;br /&gt;
*[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 ==&lt;br /&gt;
*[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
*[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5]]&lt;br /&gt;
**[[Einführung in C- und Assemblerprogrammierung mit dem Raspberry Pi 5#Warum C und Assembler?|Warum C und Assembler?]]&lt;br /&gt;
*[[Programm auf dem Raspberry Pi 5 ausführen]]&lt;br /&gt;
*[[Arbeiten mit Make und Linker-Script]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 Assembler==&lt;br /&gt;
*[[Unser erstes Programm (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung]]&lt;br /&gt;
*[[Grafik (PI5)]]&lt;br /&gt;
*[[Chars (PI5)]]&lt;br /&gt;
*Das Terminal (PI5)&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 5 C ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI5)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI5)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI5)]]&lt;br /&gt;
*[[Grafik in C (PI5)]]&lt;br /&gt;
*[[Chars in C (PI5)]]&lt;br /&gt;
*[[Das Terminal in C (PI5)]]&lt;br /&gt;
*[[printf in BareMetal (PI5)]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 Assembler 32-Bit ==&lt;br /&gt;
*[[Programmierumgebung erstellen (32-Bit)]]&lt;br /&gt;
*[[Systemprogrammierung / Bare Metal]]&lt;br /&gt;
*[[Das erste Programm]]&lt;br /&gt;
*[[General Purpose I/O]]&lt;br /&gt;
*[[System Timer]]&lt;br /&gt;
*[[UART]]&lt;br /&gt;
*[[Die Anzeige]]&lt;br /&gt;
*[[Zeichenfunktionen]]&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi 4 C (64-Bit) ==&lt;br /&gt;
*[[Unser erstes Programm in C (PI4)]]&lt;br /&gt;
*[[Lass die LED leuchten in C (PI4)]]&lt;br /&gt;
*[[Fehlerbehandlung in C (PI4)]]&lt;br /&gt;
*[[Grafik in C (PI4)]]&lt;br /&gt;
*[[Chars in C (PI4)]]&lt;br /&gt;
*[[Das Terminal in C (PI4)]]&lt;br /&gt;
*[[Printf in BareMetal (PI4)]]&lt;br /&gt;
*[[Systeminformationen (PI4)]] (noch kein Inhalt)&lt;br /&gt;
*[[Interrupts (PI4)]]&lt;br /&gt;
**[[Beispiel Timer-Interrupt (PI4)]]&lt;br /&gt;
*[[Interrupt Teil 2 (PI4)]]&lt;br /&gt;
&lt;br /&gt;
== ARM64 Assembler ==&lt;br /&gt;
*[[Programmieren mit ARM64 Assembler]]&lt;br /&gt;
&lt;br /&gt;
== Zusätzliches Material ==&lt;br /&gt;
*[[Links der verwendeten Software]]&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
*[[GNU Compiler Collection]]&lt;br /&gt;
*[[ARM-Befehlssatz]]&lt;br /&gt;
**[[Grundlegende Befehle]]&lt;br /&gt;
&amp;lt;!--**[[ARMv8-Übersicht]]--&amp;gt;&lt;br /&gt;
*[[Raspberry PI Modelle]]&lt;br /&gt;
**[[Basisadressen der Modelle]]&lt;br /&gt;
*[[LINKS]]&lt;br /&gt;
* Debugging&lt;br /&gt;
**[[Bare-Metal Debugging (JTAG, RPI 4)]]&lt;br /&gt;
&amp;lt;!--*[[Bare-Metal Debugging]]--&amp;gt;&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal (64-Bit) auf dem Raspberry Pi 4]]&lt;br /&gt;
**[[Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5]]&lt;br /&gt;
----&lt;br /&gt;
*[[Die wichtigsten Linux-Terminal-Befehle]]&lt;br /&gt;
&lt;br /&gt;
== Translations/Traducciones ==&lt;br /&gt;
*[[English]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1311</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1311"/>
		<updated>2026-06-05T13:06:17Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Konfigurationsdatei erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1310</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1310"/>
		<updated>2026-06-05T13:04:55Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Konfigurationsdatei erstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter speed 3000&lt;br /&gt;
&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
ftdi layout_init 0x0078 0x017b&lt;br /&gt;
&lt;br /&gt;
ftdi_tdo_sample_edge falling&lt;br /&gt;
ftdi layout_signal nTRST -ndata 0x0010 -noe 0x0040&lt;br /&gt;
ftdi layout_signal nSRST -ndata 0x0020 -noe 0x0040&lt;br /&gt;
&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1309</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1309"/>
		<updated>2026-06-05T13:00:48Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Verdrahtung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO22 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO23 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1308</id>
		<title>Bare-Metal Debugging (JTAG, RPI 4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Bare-Metal_Debugging_(JTAG,_RPI_4)&amp;diff=1308"/>
		<updated>2026-06-05T12:58:25Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Bei der Entwicklung von Software wird oft ein Debugger benötigt, um Fehler zu finden und zu beheben. Ein gängiger Debugger ist GDB. Solange das Programm aus einer Umgebung wie Linux oder Windows startbar ist, kann dieser Debugger direkt verwendet werden. Bei der Bare-Metal-Entwicklung sind die Dinge jedoch etwas schwieriger, da hier die gewohnte Betriebssystemumgebung fehlt. In solchen Fällen sind Emulatoren oder spezielle Hardwarelösungen nötig.&lt;br /&gt;
&lt;br /&gt;
Da Emulatoren meist nicht zu 100 % die Hardware widerspiegeln können, verwenden wir das Hostsystem, welches direkt mit dem Raspberry Pi 4 (RPI4) kommuniziert. Das Programm wird auf dem Raspberry Pi direkt ausgeführt. Über das Hostsystem können wir jedoch den Code direkt manipulieren und sehen das Ergebnis unmittelbar auf der Hardware.&lt;br /&gt;
&lt;br /&gt;
Der Raspberry Pi unterstützt für solche Kommunikation das JTAG-Protokoll. Leider können Hostsysteme dieses Protokoll selten direkt bereitstellen, weshalb wir auf zusätzliche Hardware angewiesen sind. Eine günstige Option ist das &amp;quot;CJMCU FT232H Modul&amp;quot; (https://amzn.eu/d/hb8tKuA), welches ich für diese Anleitung verwendet habe.&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten ==&lt;br /&gt;
Das FT232H Modul wurde zwar mit Stiftleisten geliefert, aber diese mussten zunächst auf das Modul aufgelötet werden. Anschließend konnte mit Kabelbrücken die Verbindung mit dem Raspberry Pi 4 hergestellt werden.&lt;br /&gt;
=== Verdrahtung ===&lt;br /&gt;
Die Verdrahtung zwischen dem FT232H Modul und dem Raspberry Pi 4 erfolgt wie folgt:&lt;br /&gt;
[[Datei:Verbindung.png|mini]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Verdrahtung&lt;br /&gt;
|-&lt;br /&gt;
! FT232H !! colspan=&amp;quot;2&amp;quot;|Raspi 4&lt;br /&gt;
|-&lt;br /&gt;
! Name !!  GPIO !! PIN&lt;br /&gt;
|-&lt;br /&gt;
| AD0 || GPIO25 || 22&lt;br /&gt;
|-&lt;br /&gt;
| AD1 || GPIO26 || 37&lt;br /&gt;
|-&lt;br /&gt;
| AD2 || GPIO24 || 18&lt;br /&gt;
|-&lt;br /&gt;
| AD3 || GPIO27 || 13&lt;br /&gt;
|-&lt;br /&gt;
| AD4 || GPIO27 || 15&lt;br /&gt;
|-&lt;br /&gt;
| AD7 || GPIO27 || 16&lt;br /&gt;
|-&lt;br /&gt;
| GND || GND || 6 (9,14,20,25,30,34,39)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Windows ==&lt;br /&gt;
Unter Windows verwenden wir die MSYS2-Umgebung. Diese bietet eine Unix-ähnliche Umgebung unter Windows und ist nützlich für viele Entwicklungsaufgaben.&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Falls noch nicht geschehen, installieren wir MSYS2 nach folgender Anleitung [[Programmierumgebung erstellen (Konsole)]] und richten die Programmierumgebung ein, indem wir die folgenden Befehle ausführen:&lt;br /&gt;
&lt;br /&gt;
* Starte MSYS2 MSYS.&lt;br /&gt;
* Gib die folgenden Befehle ein, um OpenOCD zu installieren und die Umgebungsvariablen zu setzen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
pacman -S mingw64/mingw-w64-x86_64-openocd&lt;br /&gt;
echo &#039;export PATH=&amp;quot;/mingw64/bin:$PATH&amp;quot;&#039; &amp;gt;&amp;gt; ~/.bashrc&lt;br /&gt;
source ~/.bashrc&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;C:\msys64\mingw64\share\openocd\scripts\interface\ftdi&amp;quot; ab, wenn MSYS2 nach den Vorgaben installiert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Treiberinstallation ===&lt;br /&gt;
OpenOCD möchte auf das Gerät über libusb zugreifen. In der Regel bietet Windows diesen Treiber nicht, sodass wir hier noch einen Treiber installieren müssen. Dazu verwenden wir &amp;quot;Zadig&amp;quot;, welches von https://zadig.akeo.ie/ heruntergeladen werden kann.&lt;br /&gt;
&lt;br /&gt;
# Installiere Zadig.&lt;br /&gt;
# Starte Zadig und aktiviere im Menü &amp;quot;Options&amp;quot; die Option &amp;quot;List All Devices&amp;quot;.&lt;br /&gt;
# Wähle &amp;quot;Single RS232-HS&amp;quot; aus der Geräteliste.&lt;br /&gt;
# Als Treiber wähle &amp;quot;libusbK&amp;quot; aus und klicke auf &amp;quot;Replace Driver&amp;quot;.&lt;br /&gt;
Damit ist Windows bereit, mit dem Modul über OpenOCD zu kommunizieren.&lt;br /&gt;
&lt;br /&gt;
== Verwendung von Linux ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Einrichtung ===&lt;br /&gt;
Zunächst benötigen wir OpenOCD:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install openocd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Konfigurationsdatei erstellen ===&lt;br /&gt;
Leider fehlt uns eine Konfigurationsdatei, die JTAG und FT232H beschreibt. Daher erstellen wir eine Datei namens ft232h-jtag.cfg mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
adapter driver ftdi&lt;br /&gt;
&lt;br /&gt;
ftdi vid_pid 0x0403 0x6014&lt;br /&gt;
&lt;br /&gt;
# Setze das Layout für JTAG-Signale (TDI, TDO, TMS, TCK, nTRST, nSRST)&lt;br /&gt;
# Diese Pins sind für JTAG erforderlich&lt;br /&gt;
ftdi layout_init 0x0000 0x000b&lt;br /&gt;
ftdi layout_signal nTRST -data 0x0020 -oe 0x0020&lt;br /&gt;
ftdi layout_signal nSRST -data 0x0010 -oe 0x0010&lt;br /&gt;
ftdi layout_signal TDI -data 0x0011 -oe 0x0011&lt;br /&gt;
ftdi layout_signal TDO -data 0x0012 -oe 0x0012&lt;br /&gt;
ftdi layout_signal TMS -data 0x0013 -oe 0x0013&lt;br /&gt;
ftdi layout_signal TCK -data 0x0014 -oe 0x0014&lt;br /&gt;
&lt;br /&gt;
# Wählen Sie den JTAG-Modus aus&lt;br /&gt;
transport select jtag&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Datei legen wir ins Verzeichnis &amp;quot;/usr/share/openocd/scripts/interface/ftdi&amp;quot; ab.&lt;br /&gt;
&lt;br /&gt;
== Raspberry Pi vorbereiten ==&lt;br /&gt;
Nachdem unser Betriebssystem auf dem Raspberry Pi eingerichtet ist, müssen wir den Raspberry Pi 4 für Bare-Metal-Programmierung vorbereiten. Dazu schreiben wir ein kleines Programm, das nur eine Endlosschleife erzeugt, aber den Raspberry Pi startet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The boot program for RPI4&lt;br /&gt;
// 07.03.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  b _start          // endless loop&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir verwenden ein Makefile, um das Programm zu kompilieren und zu linken (siehe [[Unser erstes Programm in C (PI4)#Kompilieren des Programms mit Make]]). Den erzeugten &amp;quot;kernel8.img&amp;quot; kopieren wir auf die SD-Karte. Beachte, dass auch folgende Dateien im Root-Verzeichnis der SD-Karte liegen müssen:&lt;br /&gt;
* bcm2711-rpi-4-b.dtb&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* fixup4.dat&lt;br /&gt;
* start4.elf&lt;br /&gt;
&lt;br /&gt;
Nun müssen wir dem Raspberry Pi auch mitteilen, dass er auf JTAG reagiert. Dazu erstellen wir eine config.txt mit folgendem Inhalt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
gpio=22-27=np&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Datei legen wir ebenfalls in das Root-Verzeichnis der SD-Karte.&lt;br /&gt;
&lt;br /&gt;
== Starten von OpenOCD und Verwendung von GDB == &lt;br /&gt;
=== Verbindung herstellen ===&lt;br /&gt;
Verbinde zunächst das FT232H-Modul per USB mit dem Hostsystem. Auf dem Modul sollte nun eine LED leuchten, welche anzeigt, dass das Gerät betriebsbereit ist. Starte den Raspberry Pi. Wenn du ihn an einem Monitor angeschlossen hast, wird auf dem Bildschirm das Regenbogenbild angezeigt, was bedeutet, dass das System gestartet wurde.&lt;br /&gt;
&lt;br /&gt;
Starte nun &amp;quot;MSYS2 MSYS&amp;quot; oder unter Linux ein Terminal:&lt;br /&gt;
[[Datei:OpenOcd.png|mini]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
openocd -f interface/ftdi/ft232h-jtag.cfg -f target/bcm2711.cfg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wenn alles korrekt eingerichtet ist, sollte OpenOCD alle vier CPUs des Raspberry Pi anzeigen. Es zeigt außerdem, dass die Ports 3333, 3334, 3335 und 3336 für die GDB-Kommunikation bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
=== Kernel-Upload und Debugging ===&lt;br /&gt;
Um sicherzugehen, öffnen wir ein weiteres &amp;quot;MSYS2 MSYS&amp;quot;-Fenster oder zweites Terminal unter Linux. Hier wechseln wir in das Verzeichnis, welches den zu testenden Code enthält. Falls der Code noch nicht kompiliert ist, muss dies jetzt erledigt werden.&lt;br /&gt;
&lt;br /&gt;
Verwende für diese Versuche den kernel8.elf:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dies stellt eine Verbindung zur CPU0 her.&lt;br /&gt;
&lt;br /&gt;
Lade nun den Kernel auf den Raspberry Pi hoch:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
load kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Kernel nun startet, verwenden wir die folgenden Befehle:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
set $pc = 0x80000&lt;br /&gt;
continue&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohne Kernel-Upload ===&lt;br /&gt;
&lt;br /&gt;
Wenn bereits auf dem Raspberry PI 4 der Kernel läuft, verwende folgendes:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
aarch64-none-elf-gdb kernel8.elf&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Debugger dann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;gdb&amp;quot;&amp;gt;&lt;br /&gt;
target extended-remote :3333&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1307</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1307"/>
		<updated>2026-06-05T10:53:39Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1306</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1306"/>
		<updated>2026-06-05T10:30:47Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen: https://www.satyria.de/arm/sources/RPI4/C/RPI4_ImageVSCode.zip&lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1305</id>
		<title>Professionelle GUI mit Debugging für Bare-Metal auf dem Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professionelle_GUI_mit_Debugging_f%C3%BCr_Bare-Metal_auf_dem_Raspberry_Pi_5&amp;diff=1305"/>
		<updated>2026-06-05T10:29:30Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Hardware vorbereiten und SD-Karte einrichten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Viele Entwickler wünschen sich eine professionelle Möglichkeit, die Bare-Metal-Entwicklung direkt in einer grafischen Benutzeroberfläche (GUI) zu realisieren. In dieser Anleitung zeige ich Ihnen, wie Sie eine solche Entwicklungsumgebung aufsetzen können. Als IDE (Integrated Development Environment) nutzen wir Visual Studio Code von Microsoft, das unter der Open-Source-Lizenz MIT veröffentlicht ist.&lt;br /&gt;
&lt;br /&gt;
Um direkt mit dem Raspberry Pi 5 zu kommunizieren und Programme zu debuggen, wird zusätzlich eine &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039; benötigt. Diese wird über USB mit einem Windows-Rechner und über die Debug-Pins mit dem Raspberry Pi 5 verbunden.&lt;br /&gt;
&lt;br /&gt;
== Software-Voraussetzungen ==&lt;br /&gt;
Wie eingangs erwähnt, nutzen wir als GUI Visual Studio Code. Die Software kann kostenlos von der offiziellen Website heruntergeladen werden:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Zusätzlich benötigen wir die offizielle Toolchain von ARM, um Code für die AArch64-Architektur des Raspberry Pi 5 kompilieren zu können. Diese steht hier zum Download bereit:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Zum Zeitpunkt der Erstellung dieses Tutorials wurde die Version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; verwendet. Da die Toolchain kontinuierlich weiterentwickelt wird, kann sich die Versionsnummer bei Ihrem Download leicht unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Für die Verbindung zwischen dem PC und der Debug Probe verwenden wir OpenOCD (Open On-Chip Debugger). Eine vorkompilierte Windows-Version wird über das xPack-Projekt bereitgestellt:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direkter Download-Link der verwendeten Version: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Zuletzt benötigen wir noch das Build-Werkzeug &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, welches wir im nächsten Schritt direkt über die Windows-Konsole installieren.&lt;br /&gt;
&lt;br /&gt;
== Installation und Einrichtung ==&lt;br /&gt;
=== Toolchain einrichten ===&lt;br /&gt;
Da Windows gelegentlich Probleme mit sehr langen Pfadnamen haben kann, benennen wir das heruntergeladene Archiv der Toolchain zunächst in &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Entpacken Sie das ZIP-Archiv vollständig.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie ein neues Verzeichnis direkt auf Ihrem Systemlaufwerk: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie den gesamten Inhalt des entpackten Toolchain-Ordners in dieses Verzeichnis, sodass die Ordnerstruktur direkt mit &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039; beginnt.&lt;br /&gt;
&lt;br /&gt;
=== OpenOCD einrichten und konfigurieren ===&lt;br /&gt;
Entpacken Sie das OpenOCD-ZIP-Archiv.&lt;br /&gt;
&lt;br /&gt;
Im entpackten Ordner finden Sie das Unterverzeichnis &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (bzw. Ihre aktuellere Version). Benennen Sie diesen Ordner einfach in &#039;&#039;&#039;openocd&#039;&#039;&#039; um.&lt;br /&gt;
&lt;br /&gt;
Verschieben Sie den gesamten Ordner &#039;&#039;&#039;openocd&#039;&#039;&#039; nach &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, sodass der Pfad &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039; entsteht.&lt;br /&gt;
&lt;br /&gt;
Überprüfen Sie anschließend, ob die Datei &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; existiert. Diese wird in der Regel standardmäßig mitgeliefert. Sollte sie fehlen, erstellen Sie diese Datei neu mit folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Da in älteren oder Standard-OpenOCD-Releases ein passendes Target-Profil für den neuen Broadcom-Chip BCM2712 des Raspberry Pi 5 fehlt, müssen wir dieses manuell anlegen. Erstellen Sie dazu eine neue Datei unter dem Pfad &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; und fügen Sie folgenden Inhalt ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP für direkten Zugriff&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# Diese Adressen wurden aus der ROM-Table via &#039;dap info 0&#039;-Befehl ausgelesen&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Standard-Target ist cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== GNU Make installieren ===&lt;br /&gt;
Unter Windows stellt Microsoft ein Paket für &#039;&#039;&#039;GNU Make&#039;&#039;&#039; bereit, das sich komfortabel über den integrierten Paketmanager installieren lässt. Öffnen Sie ein Terminal (Eingabeaufforderung oder PowerShell) und geben Sie folgenden Befehl ein:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Das Programm wird standardmäßig in das Verzeichnis &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039; installiert. Damit &#039;&#039;&#039;Make&#039;&#039;&#039; reibungslos mit unseren anderen Tools zusammenarbeitet, kopieren Sie den gesamten Inhalt dieses bin-Ordners in unser zuvor angelegtes Verzeichnis &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Umgebungsvariable „Path“ anpassen ===&lt;br /&gt;
Damit Windows und Visual Studio Code die installierten Tools (Compiler, Make und OpenOCD) systemweit über die Konsole finden können, müssen wir die ausführbaren Dateien dem Systempfad hinzufügen.&lt;br /&gt;
&lt;br /&gt;
Drücken Sie die &#039;&#039;&#039;Windows-Taste&#039;&#039;&#039; und tippen Sie im Suchfeld „&#039;&#039;Umgebungsvariable&#039;&#039;“ ein.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie den Punkt „&#039;&#039;Systemumgebungsvariablen bearbeiten&#039;&#039;“ und klicken Sie im nächsten Fenster unten auf den Button „&#039;&#039;Umgebungsvariablen...&#039;&#039;“.&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Suchen Sie im Bereich „&#039;&#039;Benutzervariablen&#039;&#039;“ oder „&#039;&#039;Systemvariablen&#039;&#039;“ nach dem Eintrag &#039;&#039;&#039;Path&#039;&#039;&#039; (oder PATH) und wählen Sie Bearbeiten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Fügen Sie der Liste die folgenden zwei Pfade als separate, neue Zeilen hinzu:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bestätigen Sie alle geöffneten Fenster mit &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die grundlegende Toolchain ist damit erfolgreich installiert und eingerichtet. Im nächsten Teil widmen wir uns der Konfiguration von Visual Studio Code für das eigentliche Bare-Metal-Projekt.&lt;br /&gt;
&lt;br /&gt;
=== Visual Studio Code einrichten ===&lt;br /&gt;
Nachdem die grundlegende Toolchain installiert ist, richten wir nun die Entwicklungsumgebung in Visual Studio Code ein.&lt;br /&gt;
&lt;br /&gt;
Zunächst installiere Visual Studio Code komplett aus dem vorhergehenden Download.&lt;br /&gt;
  &lt;br /&gt;
==== VS Code Erweiterungen (Extensions) installieren ====&lt;br /&gt;
Öffnen Sie Visual Studio Code. Um die Bare-Metal-Entwicklung so komfortabel wie möglich zu gestalten, installieren wir zunächst einige essenzielle Erweiterungen. Klicken Sie dazu auf das Erweiterungs-Icon auf der linken Menüleiste (oder drücken Sie STRG + UMSCHALT + X) und suchen Sie nach folgenden Extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (von Microsoft): Bietet Syntax-Highlighting und Code-Vervollständigung (IntelliSense) für C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Bringt zusätzliche nützliche Werkzeuge für die C-Entwicklung mit.&lt;br /&gt;
* Cortex-Debug (von marus25): Die Schlüssel-Erweiterung, die es uns ermöglicht, via OpenOCD direkt auf dem Raspberry Pi 5 zu debuggen.&lt;br /&gt;
* Arm Assembly (von dan-c-underwood): Bietet exzellente Unterstützung und Highlighting für ARM-Assembler-Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Sprache der Benutzeroberfläche auf Deutsch umstellen ====&lt;br /&gt;
Falls Ihr VS Code englischsprachig ist und Sie zum Beispiel die deutsche Oberfläche bevorzugen:&lt;br /&gt;
&lt;br /&gt;
Drücken Sie &#039;&#039;&#039;STRG + UMSCHALT + P&#039;&#039;&#039;, um die Befehlspalette zu öffnen. Tippen Sie &#039;&#039;Configure Display Language&#039;&#039; ein und drücken Sie &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Wählen Sie „Deutsch“ aus (falls nicht aufgelistet, können Sie es darüber direkt nachinstallieren). &lt;br /&gt;
&lt;br /&gt;
Starten Sie VS Code neu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Projektverzeichnis erstellen und öffnen ====&lt;br /&gt;
Erstellen Sie nun ein Verzeichnis auf Ihrer Festplatte, in dem Ihr Bare-Metal-Projekt leben soll. In diesem Beispiel verwenden wir den Pfad &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Wählen Sie in VS Code &#039;&#039;Datei -&amp;gt; Ordner öffnen...&#039;&#039; und wählen Sie das Verzeichnis &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039; aus. Bestätigen Sie die Sicherheitsabfrage, ob Sie den Autoren des Ordners vertrauen, mit „&#039;&#039;Ja, ich vertraue den Autoren&#039;&#039;“.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Die .vscode Konfigurationsdateien anlegen ====&lt;br /&gt;
&lt;br /&gt;
Damit VS Code weiß, welchen Compiler es nutzen soll und wie der Debugger gestartet wird, legen wir ein Konfigurationsverzeichnis an. Erstellen Sie im Hauptverzeichnis Ihres Projekts einen neuen Ordner mit dem exakten Namen &#039;&#039;&#039;.vscode&#039;&#039;&#039; (den Punkt am Anfang nicht vergessen!). Klicken Sie mit der rechten Maustaste auf den neu erstellten Ordner &#039;&#039;&#039;.vscode&#039;&#039;&#039; und legen Sie nacheinander die folgenden drei Dateien an.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie jeweils den entsprechenden JSON-Code hinein:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: Diese Datei konfiguriert die Code-Vervollständigung (IntelliSense), damit VS Code die ARM-spezifischen Befehle und Header versteht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: Diese Datei steuert den Debugger (Cortex-Debug). Sie sorgt dafür, dass Ihr Code via OpenOCD auf den Raspberry Pi 5 geladen und der Prozessor exakt an der Startadresse gestoppt wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Überschreibt den standardmäßigen &amp;quot;reset halt&amp;quot; Befehl von VS-Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Befehle, die direkt nach dem Verbinden ausgeführt werden&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Zwingt den Prozessor direkt auf die Startadresse&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Kompilieren&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: Hier wird der automatisierte Build-Prozess definiert. Bevor der Debugger startet, wird über diese Task automatisch make aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Kompilieren&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie alle Konfigurationen ab, indem Sie im Menü auf &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; klicken.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Hinweis zu vermeintlichen Fehlermeldungen ====&lt;br /&gt;
Nach dem Speichern der &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039; fällt Ihnen im Datei-Explorer oder im Reiter „Probleme“ (unten in VS Code) vermutlich eine kleine rote „&#039;&#039;&#039;2&#039;&#039;&#039;“ auf. Dies deutet auf aktive Warnungen hin. Wenn Sie auf die Meldung klicken, sehen Sie den Grund: VS Code beschwert sich, dass die beiden Verzeichnis-Pfade &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; und &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; nicht existieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Keine Sorge: Das ist völlig korrekt! Da wir ein leeres Projekt gestartet haben, gibt es diese Ordner einfach noch nicht. Sobald Sie im weiteren Verlauf Ihres Projekts die Ordner &#039;&#039;&#039;src&#039;&#039;&#039; und &#039;&#039;&#039;include&#039;&#039;&#039; anlegen, verschwindet diese Warnung von selbst.&lt;br /&gt;
&lt;br /&gt;
Tipp: Sollte VS Code neu erstellte Ordner einmal nicht sofort registrieren, können Sie die Entwicklungsumgebung schnell aktualisieren. Drücken Sie dazu &#039;&#039;&#039;F1&#039;&#039;&#039;, tippen Sie &#039;&#039;Reload Window&#039;&#039; ein und bestätigen Sie mit &#039;&#039;&#039;Enter&#039;&#039;&#039;. Das lädt die Oberfläche neu, ohne Ihre Arbeit zu unterbrechen.&lt;br /&gt;
&lt;br /&gt;
== Sourcecode, Linkerscript und Makefile ==&lt;br /&gt;
=== Das Quellcode-Verzeichnis (src/) ===&lt;br /&gt;
Um unser Setup praktisch zu testen, verwenden wir ein minimalistisches LED-Blinkbeispiel in C und Assembler ([[Lass die LED leuchten in C (PI5)]]). Dieses Projekt nutzt die Aufteilung in verschiedene Dateien, damit Sie die Querverweise im Code und den Komfort einer professionellen GUI direkt erleben können.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Hauptverzeichnis Ihres Projekts (z. B. &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;) einen neuen Ordner namens &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Wichtiger Hinweis für Assembler-Dateien: Achten Sie darauf, dass Assembler-Dateien, die den C-Präprozessor nutzen (wie das Einbinden von Header-Dateien via &#039;&#039;&#039;#include&#039;&#039;&#039;), zwingend die Dateiendung mit einem großen „&#039;&#039;&#039;S&#039;&#039;&#039;“ (&#039;&#039;&#039;.S&#039;&#039;&#039;) haben müssen. Ein kleines „s“ führt dazu, dass der Compiler die Includes ignoriert.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Ordner &#039;&#039;&#039;src/&#039;&#039;&#039; die folgenden sechs Dateien:&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Stellt sicher, dass der Linker dies an den Anfang des Kernel-Images setzt&lt;br /&gt;
.globl _start   // Hier beginnt die Ausführung&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Stack-Pointer initialisieren&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Bit 9 auf 0 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Bit 9 auf 1 setzen&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; zyklen; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Leere Schleife zur Verzögerung&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis&#039;&#039;&#039;: Sobald Sie diese Dateien gespeichert haben, wird Ihnen VS Code im „Probleme“-Tab diverse Fehler anzeigen. Der Code ist mit roten, gewellten Linien unterlegt. Das ist völlig normal, da dem Compiler aktuell die Header-Dateien (.h) fehlen und er die Verweise nicht auflösen kann.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Das Include-Verzeichnis (include/) ===&lt;br /&gt;
Um die Fehler zu beheben, legen wir nun die Header-Dateien an. Erstellen Sie dazu im Hauptverzeichnis Ihres Projekts einen neuen Ordner namens &#039;&#039;&#039;include&#039;&#039;&#039;. Legen Sie darin die folgenden sechs Dateien ab:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO-Definitionen für den Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Startadresse des Hauptprogramms&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 zyklen);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Header-Dateien im include-Ordner abgespeichert sind, verschwinden die roten Linien in VS Code automatisch. Die GUI hat die Bezüge erfolgreich erkannt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Linkerscript und Makefile erstellen ===&lt;br /&gt;
&lt;br /&gt;
Damit aus dem Quellcode ein ausführbares Bare-Metal-Image für den Raspberry Pi 5 gebaut werden kann, benötigen wir ein &#039;&#039;Linkerscript&#039;&#039; und die Steuerungsdatei für &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Beide Dateien werden direkt im Hauptverzeichnis (Root) Ihres Projekts angelegt.&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
Dieses Skript definiert die genaue Anordnung der Code-Segmente im Arbeitsspeicher des Raspberry Pi 5.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
Das &#039;&#039;&#039;Makefile&#039;&#039;&#039; automatisiert den Aufruf des Compilers und Linkers. Beachten Sie, dass hier explizit die Architektur des Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;) angesprochen wird.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtiger Hinweis&#039;&#039;&#039; zu Makefiles: Verwende keine Leerzeichen um Einrückungen zu erstellen. Dies mag &#039;&#039;&#039;make&#039;&#039;&#039; nicht. Verwende stattdessen immer ein &#039;&#039;&#039;TAB&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Vergessen Sie am Ende nicht, alle geöffneten Dateien im VS-Code-Menü über &#039;&#039;Datei -&amp;gt; Alles speichern&#039;&#039; zu sichern. Das Projekt ist nun vollständig konfiguriert und bereit für den ersten Build- und Debugging-Lauf!&lt;br /&gt;
&lt;br /&gt;
== Hardware vorbereiten und Debugging in der GUI ==&lt;br /&gt;
=== Hardware vorbereiten und SD-Karte einrichten ===&lt;br /&gt;
Da wir den Code direkt auf der echten Hardware testen, müssen wir die SD-Karte des Raspberry Pi 5 vorbereiten. Das Betriebssystem (EEPROM/Firmware) des Pi 5 muss wissen, dass wir ein Bare-Metal-Programm via JTAG/SWD debuggen möchten.&lt;br /&gt;
&lt;br /&gt;
Formatieren Sie eine MicroSD-Karte auf FAT32 und kopieren Sie folgende drei Dateien in das Hauptverzeichnis (Root) der Karte:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (Diese Datei wird gleich beim ersten Kompilieren erzeugt).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (Der originale Device-Tree-Blob aus der offiziellen Raspberry Pi Firmware)&lt;br /&gt;
&#039;&#039;&#039;.config.txt&#039;&#039;&#039; (Die Konfigurationsdatei für die Firmware).&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie die &#039;&#039;&#039;config.txt&#039;&#039;&#039; mit exakt folgendem Inhalt:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 Was bewirkt diese Konfiguration?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; setzt die Startadresse im RAM fest, an die unser Kernel geladen wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; schaltet die GPIO-Pins des Raspberry Pi 5 in den JTAG-/SWD-Modus um. Dadurch wird die Kommunikation mit der Raspberry Pi Debug Probe überhaupt erst möglich.&lt;br /&gt;
&lt;br /&gt;
Hier kannst du den kompletten Inhalt runterladen:  &lt;br /&gt;
&lt;br /&gt;
Hardware verbinden:&lt;br /&gt;
* Stecken Sie die vorbereitete SD-Karte in den Raspberry Pi 5.&lt;br /&gt;
* Verbinden Sie die Raspberry Pi Debug Probe über das mitgelieferte 3-Pol-UART/Debug-Kabel mit dem dedizierten Debug-Port des Raspberry Pi 5 (befindet sich zwischen den Micro-HDMI-Ports).&lt;br /&gt;
* Schließen Sie die Debug Probe per USB-Kabel an Ihren Windows-Rechner an.&lt;br /&gt;
* Schalten Sie den Raspberry Pi 5 ein (Stromversorgung anschließen).&lt;br /&gt;
&lt;br /&gt;
Durch das aktivierte JTAG-Interface wartet der Prozessor nun an der Startadresse auf die Verbindung und Befehle des Debuggers.&lt;br /&gt;
&lt;br /&gt;
=== Programm kompilieren und Debugger starten ===&lt;br /&gt;
Dank unserer Vorbereitungen in Visual Studio Code können wir den gesamten Build- und Flash-Prozess mit Tastenkombinationen steuern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Kompilieren&#039;&#039;: Drücken Sie die Tastenkombination &#039;&#039;&#039;STRG + UMSCHALT + B&#039;&#039;&#039;. VS Code führt nun im Hintergrund das Makefile aus (&#039;&#039;&#039;make all&#039;&#039;&#039;). Im Projektverzeichnis entstehen dadurch die Dateien &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (für den Debugger inklusive Symbolen) und &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (das reine Binärformat).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schritt 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Debugger starten&#039;&#039;: Drücken Sie die Taste &#039;&#039;&#039;F5&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Nun passiert Folgendes vollautomatisch:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD baut die Verbindung zum Raspberry Pi 5 über die Debug Probe auf.&lt;br /&gt;
* Der GDB-Debugger wird gestartet.&lt;br /&gt;
* Das neu kompilierte Programm wird direkt in den RAM des Raspberry Pi 5 geladen (load).&lt;br /&gt;
* Der Programmzähler des Prozessors wird auf die Startadresse 0x80000 gezwungen.&lt;br /&gt;
&lt;br /&gt;
Da wir in der &#039;&#039;&#039;launch.json&#039;&#039;&#039; festgelegt haben, dass der Debugger beim Start anhalten soll, stoppt die Ausführung exakt am ersten Befehl. In unserem Sourcecode springt der Cursor direkt in die Datei &#039;&#039;&#039;boot.S&#039;&#039;&#039; zum Label &#039;&#039;&#039;_start:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Bedienung des Debuggers in VS Code ===&lt;br /&gt;
Sobald der Debugger aktiv ist, verändert sich die Benutzeroberfläche von VS Code. Am oberen Bildschirmrand erscheint eine schwebende Debug-Steuerungsleiste.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Die Steuerelemente der GUI:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Steuerelemente&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Aktion !! Tastenkürzel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Hält das laufende Programm sofort an der aktuellen Stelle an.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Fortsetzen || F5 || Lässt das Programm normal weiterlaufen (bis zum nächsten Breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Einzelschritt (Step Over) || F10 || Führt die aktuelle Zeile aus. Springt bei Funktionen nicht in die Funktion hinein.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Einzelschritt (Step Into) || F11 || Springt direkt in eine Funktion hinein, um sie Zeile für Zeile zu prüfen.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Herausspringen (Step Out) || UMSCHALT + F11 || Führt den Rest der aktuellen Funktion aus und stoppt direkt nach der Rückkehr.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Neustart || STRG + UMSCHALT + F5 || Lädt das Programm neu auf den Pi und startet den Debug-Vorgang von vorne.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stoppen || UMSCHALT +F5 || Beendet die Debug-Sitzung und schließt die Verbindung zum Pi.5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Die Debugger-Ansichten nutzen ====&lt;br /&gt;
Der größte Vorteil einer professionellen GUI gegenüber der GDB-Kommandozeile ist die visuelle Aufbereitung aller Prozessor-Informationen auf der linken Seite des Bildschirms:&lt;br /&gt;
&lt;br /&gt;
* Variablen-Fenster (Variables): Hier werden Ihnen lokale und globale Variablen automatisch angezeigt. Sie sehen sofort, welchen Wert sie aktuell besitzen. Sie können Werte hier während des Haltepunkts sogar per Doppelklick manipulieren, um Testfälle zu simulieren!&lt;br /&gt;
&lt;br /&gt;
* Überwachungs-Fenster (Watch): Wenn Sie bestimmte Variablen oder Registeradressen permanent im Auge behalten wollen, können Sie diese hier hinzufügen.&lt;br /&gt;
&lt;br /&gt;
* Aufrufliste (Call Stack): Zeigt Ihnen genau, durch welche Funktionen das Programm gelaufen ist, um an den aktuellen Punkt zu gelangen.&lt;br /&gt;
&lt;br /&gt;
* Register-Fenster (Cortex-Debug): Ein Highlight für Bare-Metal-Entwickler. Hier sehen Sie die CPU-Register des ARM Cortex-A76 Cores (X0 bis X30, SP, PC, etc.) im Echtzeit-Zugriff. Ändert sich ein Registerwert nach einem Einzelschritt, wird dieser farblich hervorgehoben.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Breakpoints (Haltepunkte) per Mausklick setzen ====&lt;br /&gt;
Das Eintippen von Speicheradressen in GDB gehört der Vergangenheit an. In VS Code bewegen Sie einfach die Maus links neben die Zeilennummern im Sourcecode (z. B. in der kernel.c in der Zeile &#039;&#039;LED_on();&#039;&#039;). Ein schwach roter Punkt erscheint. Mit einem einfachen Linksklick aktivieren Sie den Breakpoint (er wird kräftig rot). Wenn Sie nun &#039;&#039;&#039;F5&#039;&#039;&#039; (Fortsetzen) drücken, läuft das Programm so lange, bis es genau diese Zeile erreicht, und friert die CPU ein. Ein weiterer Klick auf den Punkt entfernt den Haltepunkt wieder.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
Damit haben Sie eine vollwertige, professionelle Entwicklungsumgebung mit Hardware-Debugging für den Raspberry Pi 5 eingerichtet!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1304</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1304"/>
		<updated>2026-06-05T10:25:36Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Kompilieren und Ausführen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, welches im Root-Verzeichnis des Projektes abgelegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben. Dieses wird, wie das Makefile in das Root-Verzeichnis abgelegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Bevor wir kompilieren, prüfe deine Verzeichnisstruktur:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
C:.&lt;br /&gt;
|   linker.ld&lt;br /&gt;
|   makefile&lt;br /&gt;
|       &lt;br /&gt;
\---src&lt;br /&gt;
        boot.S&lt;br /&gt;
        main.c &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1303</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1303"/>
		<updated>2026-06-05T10:21:01Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Linker-Script (linker.ld) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, welches im Root-Verzeichnis des Projektes abgelegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben. Dieses wird, wie das Makefile in das Root-Verzeichnis abgelegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1302</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1302"/>
		<updated>2026-06-05T10:19:55Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Kompilieren des Programms mit Make */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, welches im Root-Verzeichnis des Projektes abgelegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1301</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1301"/>
		<updated>2026-06-05T10:18:34Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Erstellung des Sourcecodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt. Erstelle zunächst das Verzeichnis &amp;quot;src&amp;quot;, in der wir unseren Sourcecode ablegen werden.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, angepasst für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1300</id>
		<title>Unser erstes Programm in C (PI4)</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Unser_erstes_Programm_in_C_(PI4)&amp;diff=1300"/>
		<updated>2026-06-05T10:17:03Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Kompilieren des Programms mit Make */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
In diesem Kurs werden wir die Grundlagen der Programmierung des Raspberry Pi 4 erlernen. Ähnlich wie bei der Programmierung des Raspberry Pi 5, gibt es einige Unterschiede, hauptsächlich bedingt durch die Unterstützung von USB und die umfangreichere Dokumentation für den Raspberry Pi 4. Für diesen Kurs habe ich Material vom Raspberry Pi 5 angepasst, um die Programmierung des Raspberry Pi 4 zu erläutern.&lt;br /&gt;
&lt;br /&gt;
Ein Terminal wurde programmiert, das sich sehr gut zum Debuggen eignet. Dies werden wir in unserem Kurs ebenfalls verwenden.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
== Ziel unseres ersten Programms ==&lt;br /&gt;
Unser erstes Programm wird eine einfache Endlosschleife enthalten. Dies dient als Basis für weitere Experimente und Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
== Erstellung des Sourcecodes ==&lt;br /&gt;
Leider kommen wir bei Bare-Metal-Programmierung in C nicht um Assembler herum. Auch wenn das Assemblerprogramm zunächst nicht viel tut, wird es später beispielsweise für die Interrupt-Programmierung benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Assembler-Code (boot.S) ===&lt;br /&gt;
&lt;br /&gt;
Öffnen Sie ein Textprogramm und schreiben Sie folgendes erstes Assemblerprogramm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;GAS&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .init      // Ensure the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start       // Generates a global label&lt;br /&gt;
_start:             // The label _start (entry address)&lt;br /&gt;
&lt;br /&gt;
  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)&lt;br /&gt;
  b main            // Branch to &amp;quot;main&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie die Datei im Standardverzeichnis unter Windows: C:\msys64\home\xxx (wobei xxx in der Regel Ihr Benutzername ist). Unter Linux können Sie die Datei im Home-Verzeichnis speichern. Geben Sie der Datei den Namen boot.S. Die Endung .S kennzeichnet die Datei als Assembler-Sourcecode.&lt;br /&gt;
&lt;br /&gt;
=== C-Code (main.c) ===&lt;br /&gt;
&lt;br /&gt;
Nun erstellen wir eine main.c Datei für unser erstes Programm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
//&lt;br /&gt;
// main.c&lt;br /&gt;
// The first program for RPI4&lt;br /&gt;
// 20.02.2025 www.satyria.de&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
  while (1){}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier wird eine einfache Endlosschleife erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren des Programms mit Make ==&lt;br /&gt;
Um unser Programm zu kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwenden Sie hierzu die entsprechenden Anleitungen. Diese funktionieren auch für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (Konsole)]]&lt;br /&gt;
&lt;br /&gt;
[[Programmierumgebung erstellen (64-Bit)]]&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie dann ein Makefile, angepasst für den Raspberry Pi 4:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel8.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel8.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
# --- Native Windows Umgebung (CMD / PowerShell) ---&lt;br /&gt;
# @cls&lt;br /&gt;
	@if exist kernel8.elf del /q /f kernel8.elf&lt;br /&gt;
	@if exist kernel8.img del /q /f kernel8.img&lt;br /&gt;
	@if exist kernel8.map del /q /f kernel8.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
else&lt;br /&gt;
# --- MSYS2 / Unix-ähnliche Umgebung ---&lt;br /&gt;
	/bin/rm -f kernel8.elf kernel8.map src/*.o *.img &amp;gt; /dev/null 2&amp;gt; /dev/null || true&lt;br /&gt;
endif&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
ifeq ($(MSYSTEM),)&lt;br /&gt;
	@cls&lt;br /&gt;
else&lt;br /&gt;
	/bin/clear&lt;br /&gt;
endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen Makefile.&lt;br /&gt;
&lt;br /&gt;
Wenn Sie mehr über den Inhalt erfahren möchten, schauen Sie unter &amp;quot;[[Arbeiten mit Make und Linker-Script]]&amp;quot; nach.&lt;br /&gt;
&lt;br /&gt;
== Linker-Script (linker.ld) ==&lt;br /&gt;
Erstellen Sie das folgende Linker-Script, welches wir unter [[Arbeiten mit Make und Linker-Script]] beschrieben haben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data*)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Speichern Sie diese Datei unter dem Namen linker.ld.&lt;br /&gt;
&lt;br /&gt;
== Kompilieren und Ausführen ==&lt;br /&gt;
Nun können wir unser erstes Programm kompilieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;shell&amp;quot;&amp;gt;&lt;br /&gt;
make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit unser Kernel funktionsfähig ist, muss der Kernel auf eine SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:&lt;br /&gt;
&lt;br /&gt;
* bootcode.bin&lt;br /&gt;
* start.elf&lt;br /&gt;
* config.txt&lt;br /&gt;
* kernel8.img (Ihr kompiliertes Programm)&lt;br /&gt;
&lt;br /&gt;
Den Source-Code können Sie als ZIP-Datei [https://www.satyria.de/arm/sources/RPI4/C/1.zip hier] herunterladen.&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%;&lt;br /&gt;
| style=&amp;quot;width: 33%;&amp;quot; | &lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:center;&amp;quot; | [[Hauptseite|&amp;lt; Hauptseite &amp;gt;]]&lt;br /&gt;
| style=&amp;quot;width: 33%; text-align:right;&amp;quot; | [[Lass die LED leuchten in C (PI4)|Weiter (Lass die LED leuchten in C (PI4)) &amp;gt;]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1299</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1299"/>
		<updated>2026-06-03T16:33:55Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Preparing the Hardware and Setting Up the SD Card */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Creating the Linker Script and Makefile ===&lt;br /&gt;
&lt;br /&gt;
In order to build an executable bare-metal image for the Raspberry Pi 5 from the source code, we need a &#039;&#039;linker script&#039;&#039; and the control file for &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Both files are created directly in the main directory (root) of your project.&lt;br /&gt;
&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
This script defines the exact layout of the code segments in the Raspberry Pi 5&#039;s memory.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
The &#039;&#039;&#039;Makefile&#039;&#039;&#039; automates the invocation of the compiler and linker. Note that it explicitly targets the architecture of the Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039; regarding Makefiles: Do not use spaces for indentation. &#039;&#039;&#039;make&#039;&#039;&#039; will not accept this. Always use a &#039;&#039;&#039;TAB&#039;&#039;&#039; character instead.&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to save all open files via &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the VS Code menu. The project is now fully configured and ready for its first build and debugging run!&lt;br /&gt;
&lt;br /&gt;
== Preparing the Hardware and Debugging in the GUI ==&lt;br /&gt;
=== Preparing the Hardware and Setting Up the SD Card ===&lt;br /&gt;
Since we are testing the code directly on the real hardware, we need to prepare the Raspberry Pi 5&#039;s SD card. The operating system (EEPROM/firmware) of the Pi 5 needs to know that we want to debug a bare-metal program via JTAG/SWD.&lt;br /&gt;
&lt;br /&gt;
Format a MicroSD card as FAT32 and copy the following three files into the root directory of the card:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (This file will be generated during the first compilation).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (The original device tree blob from the official Raspberry Pi firmware)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (The configuration file for the firmware).&lt;br /&gt;
&lt;br /&gt;
Create the &#039;&#039;&#039;config.txt&#039;&#039;&#039; with the exact following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 What does this configuration do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; sets the start address in RAM where our kernel will be loaded.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; switches the Raspberry Pi 5&#039;s GPIO pins into JTAG/SWD mode. This is what makes communication with the Raspberry Pi Debug Probe possible in the first place.&lt;br /&gt;
&lt;br /&gt;
Connecting the hardware:&lt;br /&gt;
* Insert the prepared SD card into the Raspberry Pi 5.&lt;br /&gt;
* Connect the Raspberry Pi Debug Probe to the dedicated debug port of the Raspberry Pi 5 (located between the Micro-HDMI ports) using the supplied 3-pin UART/debug cable.&lt;br /&gt;
* Connect the Debug Probe to your Windows PC via a USB cable.&lt;br /&gt;
* Power on the Raspberry Pi 5 (connect the power supply).&lt;br /&gt;
&lt;br /&gt;
With the JTAG interface enabled, the processor now waits at the start address for the debugger to connect and issue commands.&lt;br /&gt;
&lt;br /&gt;
=== Compiling the Program and Starting the Debugger ===&lt;br /&gt;
Thanks to our preparations in Visual Studio Code, we can control the entire build and flash process using keyboard shortcuts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Compile&#039;&#039;: Press the keyboard shortcut &#039;&#039;&#039;CTRL + SHIFT + B&#039;&#039;&#039;. VS Code will now run the Makefile (&#039;&#039;&#039;make all&#039;&#039;&#039;) in the background. This will generate the files &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (for the debugger, including symbols) and &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (the raw binary format) in the project directory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Start Debugger&#039;&#039;: Press the &#039;&#039;&#039;F5&#039;&#039;&#039; key. &lt;br /&gt;
&lt;br /&gt;
The following now happens fully automatically:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD establishes the connection to the Raspberry Pi 5 via the Debug Probe.&lt;br /&gt;
* The GDB debugger is started.&lt;br /&gt;
* The newly compiled program is loaded directly into the Raspberry Pi 5&#039;s RAM (load).&lt;br /&gt;
* The processor&#039;s program counter is forced to the start address 0x80000.&lt;br /&gt;
&lt;br /&gt;
Since we specified in the &#039;&#039;&#039;launch.json&#039;&#039;&#039; that the debugger should halt on startup, execution stops exactly at the first instruction. In our source code, the cursor jumps directly to the &#039;&#039;&#039;boot.S&#039;&#039;&#039; file at the &#039;&#039;&#039;_start:&#039;&#039;&#039; label.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Operating the Debugger in VS Code ===&lt;br /&gt;
As soon as the debugger is active, the VS Code user interface changes. A floating debug control bar appears at the top of the screen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The GUI controls:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Controls&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Action !! Keyboard Shortcut !! Description&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Immediately halts the running program at the current location.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Continue || F5 || Resumes normal program execution (until the next breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Step Over || F10 || Executes the current line. Does not step into functions.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Step Into || F11 || Steps directly into a function to examine it line by line.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Step Out || SHIFT + F11 || Executes the rest of the current function and stops immediately after returning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Restart || CTRL + SHIFT + F5 || Reloads the program onto the Pi and starts the debugging process from the beginning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stop || SHIFT + F5 || Ends the debug session and closes the connection to the Pi 5.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Using the Debugger Views ====&lt;br /&gt;
The biggest advantage of a professional GUI over the GDB command line is the visual presentation of all processor information on the left side of the screen:&lt;br /&gt;
&lt;br /&gt;
* Variables Window: Local and global variables are automatically displayed here. You can immediately see their current values. You can even manipulate these values with a double-click while halted to simulate test cases!&lt;br /&gt;
&lt;br /&gt;
* Watch Window: If you want to keep a permanent eye on specific variables or register addresses, you can add them here.&lt;br /&gt;
&lt;br /&gt;
* Call Stack: Shows you exactly which functions the program has gone through to reach the current point.&lt;br /&gt;
&lt;br /&gt;
* Registers Window (Cortex-Debug): A highlight for bare-metal developers. Here you can see the CPU registers of the ARM Cortex-A76 core (X0 to X30, SP, PC, etc.) with real-time access. If a register value changes after a single step, it is highlighted in color.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Setting Breakpoints with a Mouse Click ====&lt;br /&gt;
Typing memory addresses into GDB is a thing of the past. In VS Code, simply move your mouse to the left of the line numbers in the source code (e.g., in kernel.c on the line &#039;&#039;LED_on();&#039;&#039;). A faint red dot will appear. With a simple left-click, you activate the breakpoint (it turns solid red). If you now press &#039;&#039;&#039;F5&#039;&#039;&#039; (Continue), the program runs until it reaches exactly this line and freezes the CPU. Another click on the dot removes the breakpoint again.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
You have now set up a fully-fledged, professional development environment with hardware debugging for the Raspberry Pi 5!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1298</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1298"/>
		<updated>2026-06-03T16:32:44Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Creating the Linker Script and Makefile ===&lt;br /&gt;
&lt;br /&gt;
In order to build an executable bare-metal image for the Raspberry Pi 5 from the source code, we need a &#039;&#039;linker script&#039;&#039; and the control file for &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Both files are created directly in the main directory (root) of your project.&lt;br /&gt;
&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
This script defines the exact layout of the code segments in the Raspberry Pi 5&#039;s memory.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
The &#039;&#039;&#039;Makefile&#039;&#039;&#039; automates the invocation of the compiler and linker. Note that it explicitly targets the architecture of the Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039; regarding Makefiles: Do not use spaces for indentation. &#039;&#039;&#039;make&#039;&#039;&#039; will not accept this. Always use a &#039;&#039;&#039;TAB&#039;&#039;&#039; character instead.&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to save all open files via &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the VS Code menu. The project is now fully configured and ready for its first build and debugging run!&lt;br /&gt;
&lt;br /&gt;
== Preparing the Hardware and Debugging in the GUI ==&lt;br /&gt;
=== Preparing the Hardware and Setting Up the SD Card ===&lt;br /&gt;
Since we are testing the code directly on the real hardware, we need to prepare the Raspberry Pi 5&#039;s SD card. The operating system (EEPROM/firmware) of the Pi 5 needs to know that we want to debug a bare-metal program via JTAG/SWD.&lt;br /&gt;
&lt;br /&gt;
Format a MicroSD card as FAT32 and copy the following three files into the root directory of the card:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (This file will be generated during the first compilation).&lt;br /&gt;
&#039;&#039;&#039;bcm2712-rpi-5-b.dtb&#039;&#039;&#039; (The original device tree blob from the official Raspberry Pi firmware)&lt;br /&gt;
&#039;&#039;&#039;config.txt&#039;&#039;&#039; (The configuration file for the firmware).&lt;br /&gt;
&lt;br /&gt;
Create the &#039;&#039;&#039;config.txt&#039;&#039;&#039; with the exact following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
arm_64bit=1&lt;br /&gt;
kernel_address=0x80000&lt;br /&gt;
enable_jtag_gpio=1&lt;br /&gt;
kernel=kernel_2712.img&lt;br /&gt;
framebuffer_depth=32&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
💡 What does this configuration do?&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;kernel_address=0x80000&#039;&#039;&#039; sets the start address in RAM where our kernel will be loaded.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;enable_jtag_gpio=1&#039;&#039;&#039; switches the Raspberry Pi 5&#039;s GPIO pins into JTAG/SWD mode. This is what makes communication with the Raspberry Pi Debug Probe possible in the first place.&lt;br /&gt;
&lt;br /&gt;
Connecting the hardware:&lt;br /&gt;
* Insert the prepared SD card into the Raspberry Pi 5.&lt;br /&gt;
* Connect the Raspberry Pi Debug Probe to the dedicated debug port of the Raspberry Pi 5 (located between the Micro-HDMI ports) using the supplied 3-pin UART/debug cable.&lt;br /&gt;
* Connect the Debug Probe to your Windows PC via a USB cable.&lt;br /&gt;
* Power on the Raspberry Pi 5 (connect the power supply).&lt;br /&gt;
&lt;br /&gt;
With the JTAG interface enabled, the processor now waits at the start address for the debugger to connect and issue commands.&lt;br /&gt;
&lt;br /&gt;
=== Compiling the Program and Starting the Debugger ===&lt;br /&gt;
Thanks to our preparations in Visual Studio Code, we can control the entire build and flash process using keyboard shortcuts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Compile&#039;&#039;: Press the keyboard shortcut &#039;&#039;&#039;CTRL + SHIFT + B&#039;&#039;&#039;. VS Code will now run the Makefile (&#039;&#039;&#039;make all&#039;&#039;&#039;) in the background. This will generate the files &#039;&#039;&#039;kernel_2712.elf&#039;&#039;&#039; (for the debugger, including symbols) and &#039;&#039;&#039;kernel_2712.img&#039;&#039;&#039; (the raw binary format) in the project directory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Start Debugger&#039;&#039;: Press the &#039;&#039;&#039;F5&#039;&#039;&#039; key. &lt;br /&gt;
&lt;br /&gt;
The following now happens fully automatically:&lt;br /&gt;
&lt;br /&gt;
* OpenOCD establishes the connection to the Raspberry Pi 5 via the Debug Probe.&lt;br /&gt;
* The GDB debugger is started.&lt;br /&gt;
* The newly compiled program is loaded directly into the Raspberry Pi 5&#039;s RAM (load).&lt;br /&gt;
* The processor&#039;s program counter is forced to the start address 0x80000.&lt;br /&gt;
&lt;br /&gt;
Since we specified in the &#039;&#039;&#039;launch.json&#039;&#039;&#039; that the debugger should halt on startup, execution stops exactly at the first instruction. In our source code, the cursor jumps directly to the &#039;&#039;&#039;boot.S&#039;&#039;&#039; file at the &#039;&#039;&#039;_start:&#039;&#039;&#039; label.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debug1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
=== Operating the Debugger in VS Code ===&lt;br /&gt;
As soon as the debugger is active, the VS Code user interface changes. A floating debug control bar appears at the top of the screen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:ObDebug.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The GUI controls:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Controls&lt;br /&gt;
|-&lt;br /&gt;
! Icon / Symbol !! Action !! Keyboard Shortcut !! Description&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Pause.png|20px|link=]] || Pause || F6 || Immediately halts the running program at the current location.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Weiter.png|20px|link=]] || Continue || F5 || Resumes normal program execution (until the next breakpoint).&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepOver.png|20px|link=]] || Step Over || F10 || Executes the current line. Does not step into functions.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug EinzelschrittStepInto.png|20px|link=]] || Step Into || F11 || Steps directly into a function to examine it line by line.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Herausspringen.png|20px|link=]]  || Step Out || SHIFT + F11 || Executes the rest of the current function and stops immediately after returning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Neustart.png|20px|link=]] || Restart || CTRL + SHIFT + F5 || Reloads the program onto the Pi and starts the debugging process from the beginning.&lt;br /&gt;
|-&lt;br /&gt;
| [[Datei:Debug Stop.png|20px|link=]] || Stop || SHIFT + F5 || Ends the debug session and closes the connection to the Pi 5.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Using the Debugger Views ====&lt;br /&gt;
The biggest advantage of a professional GUI over the GDB command line is the visual presentation of all processor information on the left side of the screen:&lt;br /&gt;
&lt;br /&gt;
* Variables Window: Local and global variables are automatically displayed here. You can immediately see their current values. You can even manipulate these values with a double-click while halted to simulate test cases!&lt;br /&gt;
&lt;br /&gt;
* Watch Window: If you want to keep a permanent eye on specific variables or register addresses, you can add them here.&lt;br /&gt;
&lt;br /&gt;
* Call Stack: Shows you exactly which functions the program has gone through to reach the current point.&lt;br /&gt;
&lt;br /&gt;
* Registers Window (Cortex-Debug): A highlight for bare-metal developers. Here you can see the CPU registers of the ARM Cortex-A76 core (X0 to X30, SP, PC, etc.) with real-time access. If a register value changes after a single step, it is highlighted in color.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Debuger Ansicht.png|300x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Setting Breakpoints with a Mouse Click ====&lt;br /&gt;
Typing memory addresses into GDB is a thing of the past. In VS Code, simply move your mouse to the left of the line numbers in the source code (e.g., in kernel.c on the line &#039;&#039;LED_on();&#039;&#039;). A faint red dot will appear. With a simple left-click, you activate the breakpoint (it turns solid red). If you now press &#039;&#039;&#039;F5&#039;&#039;&#039; (Continue), the program runs until it reaches exactly this line and freezes the CPU. Another click on the dot removes the breakpoint again.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Breakpoint1.png|200x200px]]&lt;br /&gt;
[[Datei:Breakpoint2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
You have now set up a fully-fledged, professional development environment with hardware debugging for the Raspberry Pi 5!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1297</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1297"/>
		<updated>2026-06-03T16:28:29Z</updated>

		<summary type="html">&lt;p&gt;Satyria: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== Creating the Linker Script and Makefile ===&lt;br /&gt;
&lt;br /&gt;
In order to build an executable bare-metal image for the Raspberry Pi 5 from the source code, we need a &#039;&#039;linker script&#039;&#039; and the control file for &#039;&#039;&#039;GNU Make&#039;&#039;&#039;. Both files are created directly in the main directory (root) of your project.&lt;br /&gt;
&lt;br /&gt;
==== linker.ld ====&lt;br /&gt;
This script defines the exact layout of the code segments in the Raspberry Pi 5&#039;s memory.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    .init : {&lt;br /&gt;
        *(.init)&lt;br /&gt;
    }&lt;br /&gt;
    .text : {&lt;br /&gt;
        *(.text)&lt;br /&gt;
        *(.text.*)&lt;br /&gt;
        _etext = .;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .rodata : {&lt;br /&gt;
        *(.rodata)&lt;br /&gt;
        *(.rodata.*)&lt;br /&gt;
    }&lt;br /&gt;
    .init_array : {&lt;br /&gt;
        __init_start = .;&lt;br /&gt;
        KEEP(*(.init_array*))&lt;br /&gt;
        __init_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .ARM.exidx : {&lt;br /&gt;
        __exidx_start = .;&lt;br /&gt;
        *(.ARM.exidx*)&lt;br /&gt;
        __exidx_end = .;&lt;br /&gt;
    }&lt;br /&gt;
    .eh_frame : {&lt;br /&gt;
        *(.eh_frame*)&lt;br /&gt;
    }&lt;br /&gt;
    .data : {&lt;br /&gt;
        *(.data)&lt;br /&gt;
    }&lt;br /&gt;
    .bss : {&lt;br /&gt;
        __bss_start = .;&lt;br /&gt;
        *(.bss)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
        __bss_end = .;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
__bss_size = (__bss_end - __bss_start) &amp;gt;&amp;gt; 3;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Makefile ====&lt;br /&gt;
The &#039;&#039;&#039;Makefile&#039;&#039;&#039; automates the invocation of the compiler and linker. Note that it explicitly targets the architecture of the Raspberry Pi 5 (&#039;&#039;&#039;-mcpu=cortex-a76&#039;&#039;&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;make&amp;quot;&amp;gt;&lt;br /&gt;
CSRCS := $(wildcard src/*.c)&lt;br /&gt;
CPPSRCS := $(wildcard src/*.cpp)&lt;br /&gt;
ASRCS := $(wildcard src/*.S)&lt;br /&gt;
COBJS := $(CSRCS:.c=.o)&lt;br /&gt;
CPPOBJS := $(CPPSRCS:.cpp=.o)&lt;br /&gt;
AOBJS := $(ASRCS:.S=.o)&lt;br /&gt;
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)&lt;br /&gt;
LOADADDR = 0x80000&lt;br /&gt;
&lt;br /&gt;
GCCFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -O0 -ffreestanding \&lt;br /&gt;
           -nostartfiles -nostdlib -nostdinc -g -I ./include&lt;br /&gt;
&lt;br /&gt;
AFLAGS = -mcpu=cortex-a76 -mlittle-endian  -I ./include -O0 -g&lt;br /&gt;
&lt;br /&gt;
CFLAGS = -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \&lt;br /&gt;
         -I ./include -O0 -fno-exceptions &lt;br /&gt;
&lt;br /&gt;
CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -mcpu=cortex-a76 -mlittle-endian -Wall -fsigned-char \&lt;br /&gt;
           -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new&lt;br /&gt;
&lt;br /&gt;
all: clean new kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
%.o: %.S&lt;br /&gt;
	@echo &amp;quot;as $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(AFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.c&lt;br /&gt;
	@echo &amp;quot;gcc $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-gcc $(CFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
%.o: %.cpp&lt;br /&gt;
	@echo &amp;quot;g++ $@&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $&amp;lt; -o $@&lt;br /&gt;
&lt;br /&gt;
kernel_2712.img: $(AllOBJS)&lt;br /&gt;
	@echo &amp;quot;=============================================================================&amp;quot;&lt;br /&gt;
	@echo &amp;quot;Linking...&amp;quot;&lt;br /&gt;
	@aarch64-none-elf-ld -o kernel_2712.elf -Map kernel_2712.map -nostdlib \&lt;br /&gt;
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \&lt;br /&gt;
		-g -T linker.ld $(AllOBJS)&lt;br /&gt;
	aarch64-none-elf-objcopy -O binary kernel_2712.elf kernel_2712.img&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	@if exist kernel_2712.elf del /q /f kernel_2712.elf&lt;br /&gt;
	@if exist kernel_2712.img del /q /f kernel_2712.img&lt;br /&gt;
	@if exist kernel_2712.map del /q /f kernel_2712.map&lt;br /&gt;
	@if exist src\*.o del /q /f src\*.o&lt;br /&gt;
&lt;br /&gt;
new:&lt;br /&gt;
	@cls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039; regarding Makefiles: Do not use spaces for indentation. &#039;&#039;&#039;make&#039;&#039;&#039; will not accept this. Always use a &#039;&#039;&#039;TAB&#039;&#039;&#039; character instead.&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to save all open files via &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the VS Code menu. The project is now fully configured and ready for its first build and debugging run!&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1296</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1296"/>
		<updated>2026-06-03T16:25:31Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Source Code, Linker Script, and Makefile */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1295</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1295"/>
		<updated>2026-06-03T16:23:19Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* The Include Directory (include/) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
#define RPI_BASE 0x107C000000UL&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0 (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
#define MEM_KERNEL_START 0x80000 // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
typedef unsigned char u8;&lt;br /&gt;
typedef unsigned short u16;&lt;br /&gt;
typedef unsigned int u32;&lt;br /&gt;
typedef signed char s8;&lt;br /&gt;
typedef signed short s16;&lt;br /&gt;
typedef signed int s32;&lt;br /&gt;
typedef unsigned long u64;&lt;br /&gt;
typedef signed long s64;&lt;br /&gt;
typedef long intptr;&lt;br /&gt;
typedef unsigned long uintptr;&lt;br /&gt;
typedef unsigned long size_t;&lt;br /&gt;
typedef long ssize_t;&lt;br /&gt;
typedef char boolean;&lt;br /&gt;
#define ALIGN(n) attribute((aligned (n)))&lt;br /&gt;
#define FALSE 0&lt;br /&gt;
#define TRUE 1&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1294</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1294"/>
		<updated>2026-06-03T16:18:23Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Important: Note on Apparent Error Messages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
== Source Code, Linker Script, and Makefile ==&lt;br /&gt;
=== The Source Code Directory (src/) ===&lt;br /&gt;
To practically test our setup, we will use a minimalist LED blinking example in C and Assembly ([[Lass die LED leuchten in C (PI5)]]). This project uses a split into multiple files so that you can directly experience cross-references in the code and the convenience of a professional GUI.&lt;br /&gt;
&lt;br /&gt;
In the main directory of your project (e.g., &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;), create a new folder named &#039;&#039;&#039;src&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
💡 Important note for assembly files: Make sure that assembly files that use the C preprocessor (such as including header files via &#039;&#039;&#039;#include&#039;&#039;&#039;) strictly have the file extension with a capital &amp;quot;&#039;&#039;&#039;S&#039;&#039;&#039;&amp;quot; (&#039;&#039;&#039;.S&#039;&#039;&#039;). A lowercase &amp;quot;s&amp;quot; will cause the compiler to ignore the includes.&lt;br /&gt;
&lt;br /&gt;
Create the following six files in the &#039;&#039;&#039;src/&#039;&#039;&#039; folder:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/boot.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// boot.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;config.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.section .init  // Ensures that the linker places this at the beginning of the kernel image&lt;br /&gt;
.globl _start   // Execution starts here&lt;br /&gt;
&lt;br /&gt;
_start:&lt;br /&gt;
    ldr x0, =MEM_KERNEL_STACK&lt;br /&gt;
    mov sp, x0          // Initialize stack pointer&lt;br /&gt;
    b sysinit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/kernel.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// kernel.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;led.h&amp;quot;&lt;br /&gt;
#include &amp;quot;time.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        LED_off();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
        LED_on();&lt;br /&gt;
        wait(0x3F0000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/led.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;base.h&amp;quot;&lt;br /&gt;
#include &amp;quot;util.h&amp;quot;&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void LED_off(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg &amp;amp;= ~0x200; // Set bit 9 to 0&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void LED_on(void)&lt;br /&gt;
{&lt;br /&gt;
    u32 reg = read32(ARM_GPIO2_DATA0);&lt;br /&gt;
    reg |= 0x200; // Set bit 9 to 1&lt;br /&gt;
    write32(ARM_GPIO2_DATA0, reg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/sysinit.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// sysinit.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.section .text&lt;br /&gt;
.globl sysinit&lt;br /&gt;
&lt;br /&gt;
sysinit:&lt;br /&gt;
    b main&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/time.c&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.c&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles) &lt;br /&gt;
{&lt;br /&gt;
    volatile u32 i;&lt;br /&gt;
    for (i = 0; i &amp;lt; cycles; i++) &lt;br /&gt;
    {&lt;br /&gt;
        // Empty loop for delay&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/util.S&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
// util.S&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
.globl write32&lt;br /&gt;
write32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    str w1, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
.globl read32&lt;br /&gt;
read32:&lt;br /&gt;
    stp x29, x30, [sp, -16]!&lt;br /&gt;
    mov x29, sp&lt;br /&gt;
    ldr w0, [x0]&lt;br /&gt;
    ldp x29, x30, [sp], 16&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: As soon as you save these files, VS Code will display various errors in the &amp;quot;Problems&amp;quot; tab. The code will be underlined with red, wavy lines. This is completely normal because the compiler currently lacks the header files (.h) and cannot resolve the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Source prob.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
=== The Include Directory (include/) ===&lt;br /&gt;
To fix the errors, we will now create the header files. To do this, create a new folder named &#039;&#039;&#039;include&#039;&#039;&#039; in the main directory of your project. Place the following six files inside it:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/base.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// base.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _base_h&lt;br /&gt;
#define _base_h&lt;br /&gt;
&lt;br /&gt;
#define RPI_BASE  0x107C000000UL&lt;br /&gt;
&lt;br /&gt;
// GPIO definitions for the Pi 5&lt;br /&gt;
#define ARM_GPIO2_BASE   (RPI_BASE + 0x1517C00)&lt;br /&gt;
#define ARM_GPIO2_DATA0  (ARM_GPIO2_BASE + 0x04)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/config.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// config.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _config_h&lt;br /&gt;
#define _config_h&lt;br /&gt;
&lt;br /&gt;
#define MEGABYTE 0x100000&lt;br /&gt;
&lt;br /&gt;
#define MEM_KERNEL_START 0x80000          // Start address of the main program&lt;br /&gt;
#define KERNEL_MAX_SIZE  (2 * MEGABYTE)&lt;br /&gt;
#define MEM_KERNEL_END   (MEM_KERNEL_START + KERNEL_MAX_SIZE)&lt;br /&gt;
#define KERNEL_STACK_SIZE 0x20000&lt;br /&gt;
#define MEM_KERNEL_STACK (MEM_KERNEL_END + KERNEL_STACK_SIZE)&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/led.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// led.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_led_h&lt;br /&gt;
#define _ms_led_h&lt;br /&gt;
&lt;br /&gt;
void LED_off(void);&lt;br /&gt;
void LED_on(void);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/time.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// time.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_time_h&lt;br /&gt;
#define _ms_time_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void wait(u32 cycles);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
	<entry>
		<id>https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1293</id>
		<title>Professional GUI with Debugging for Bare-Metal on the Raspberry Pi 5</title>
		<link rel="alternate" type="text/html" href="https://satyria.de/arm/index.php?title=Professional_GUI_with_Debugging_for_Bare-Metal_on_the_Raspberry_Pi_5&amp;diff=1293"/>
		<updated>2026-06-03T16:17:32Z</updated>

		<summary type="html">&lt;p&gt;Satyria: /* Important: Note on Apparent Error Messages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Many developers desire a professional way to perform bare-metal development directly within a graphical user interface (GUI). In this guide, I will show you how to set up such a development environment. As our IDE (Integrated Development Environment), we will use Visual Studio Code from Microsoft, which is released under the open-source MIT license.&lt;br /&gt;
&lt;br /&gt;
To communicate directly with the Raspberry Pi 5 and debug programs, you will additionally need a &#039;&#039;&#039;Raspberry Pi Debug Probe&#039;&#039;&#039;. This is connected to a Windows PC via USB and to the Raspberry Pi 5 via the debug pins.&lt;br /&gt;
&lt;br /&gt;
== Software Requirements ==&lt;br /&gt;
As mentioned earlier, we will use Visual Studio Code as our GUI. The software can be downloaded for free from the official website:&lt;br /&gt;
&lt;br /&gt;
👉 https://code.visualstudio.com/&lt;br /&gt;
&lt;br /&gt;
Additionally, we need the official toolchain from ARM to be able to compile code for the AArch64 architecture of the Raspberry Pi 5. It is available for download here:&lt;br /&gt;
&lt;br /&gt;
👉 [https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ARM GNU Toolchain Downloads]&lt;br /&gt;
&lt;br /&gt;
Note: At the time of writing this tutorial, version &#039;&#039;arm-gnu-toolchain-15.2.rel1-mingw-w64-x86_64-aarch64-none-elf.zip&#039;&#039; was used. Since the toolchain is under continuous development, the version number of your download may vary slightly.&lt;br /&gt;
&lt;br /&gt;
For the connection between the PC and the Debug Probe, we use OpenOCD (Open On-Chip Debugger). A pre-compiled Windows version is provided by the xPack project:&lt;br /&gt;
&lt;br /&gt;
👉 [https://xpack-dev-tools.github.io/openocd-xpack/ xPack OpenOCD Website]&lt;br /&gt;
&lt;br /&gt;
Direct download link for the version used: [https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.12.0-7/xpack-openocd-0.12.0-7-win32-x64.zip xPack OpenOCD v0.12.0-7]&lt;br /&gt;
&lt;br /&gt;
Finally, we also need the build tool &#039;&#039;&#039;GNU Make&#039;&#039;&#039;, which we will install directly via the Windows console in the next step.&lt;br /&gt;
== Installation and Setup ==&lt;br /&gt;
=== Setting Up the Toolchain ===&lt;br /&gt;
Since Windows can occasionally have issues with very long path names, we first rename the downloaded toolchain archive to &#039;&#039;&#039;toolchain.zip&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Extract the ZIP archive completely.&lt;br /&gt;
&lt;br /&gt;
Create a new directory directly on your system drive: &#039;&#039;&#039;C:\tools&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Copy the entire contents of the extracted toolchain folder into this directory, so that the folder structure starts directly with &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up and Configuring OpenOCD ===&lt;br /&gt;
Extract the OpenOCD ZIP archive.&lt;br /&gt;
&lt;br /&gt;
In the extracted folder, you will find the subdirectory &#039;&#039;&#039;xpack-openocd-0.12.0-7&#039;&#039;&#039; (or your more recent version). Simply rename this folder to &#039;&#039;&#039;openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Move the entire &#039;&#039;&#039;openocd&#039;&#039;&#039; folder to &#039;&#039;&#039;C:\tools&#039;&#039;&#039;, so that the path becomes &#039;&#039;&#039;C:\tools\openocd&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Next, check if the file &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\interface\cmsis-dap.cfg&#039;&#039;&#039; exists. This is usually included by default. If it is missing, create this file with the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
adapter driver cmsis-dap&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Since older or standard OpenOCD releases lack a suitable target profile for the new Broadcom BCM2712 chip of the Raspberry Pi 5, we need to create it manually. To do this, create a new file at the path &#039;&#039;&#039;C:\tools\openocd\openocd\scripts\target\bcm2712.cfg&#039;&#039;&#039; and insert the following content:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
if { [info exists CHIPNAME] } {&lt;br /&gt;
        set  _CHIPNAME $CHIPNAME&lt;br /&gt;
} else {&lt;br /&gt;
        set  _CHIPNAME bcm2712&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists CHIPCORES] } {&lt;br /&gt;
        set _cores $CHIPCORES&lt;br /&gt;
} else {&lt;br /&gt;
        set _cores 4&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists USE_SMP] } {&lt;br /&gt;
        set _USE_SMP $USE_SMP&lt;br /&gt;
} else {&lt;br /&gt;
        set _USE_SMP 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if { [info exists DAP_TAPID] } {&lt;br /&gt;
        set _DAP_TAPID $DAP_TAPID&lt;br /&gt;
} else {&lt;br /&gt;
        set _DAP_TAPID 0x4ba00477&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
transport select swd&lt;br /&gt;
&lt;br /&gt;
swd newdap $_CHIPNAME cpu -expected-id $_DAP_TAPID -irlen 4&lt;br /&gt;
adapter speed 4000&lt;br /&gt;
&lt;br /&gt;
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu&lt;br /&gt;
&lt;br /&gt;
# MEM-AP for direct access&lt;br /&gt;
target create $_CHIPNAME.ap mem_ap -dap $_CHIPNAME.dap -ap-num 0&lt;br /&gt;
&lt;br /&gt;
# These addresses were read from the ROM table via the &#039;dap info 0&#039; command&lt;br /&gt;
set _DBGBASE {0x80010000 0x80110000 0x80210000 0x80310000}&lt;br /&gt;
set _CTIBASE {0x80020000 0x80120000 0x80220000 0x80320000}&lt;br /&gt;
&lt;br /&gt;
set _smp_command &amp;quot;target smp&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for { set _core 0 } { $_core &amp;lt; $_cores } { incr _core } {&lt;br /&gt;
        set _CTINAME $_CHIPNAME.cti$_core&lt;br /&gt;
        set _TARGETNAME $_CHIPNAME.cpu$_core&lt;br /&gt;
&lt;br /&gt;
        cti create $_CTINAME -dap $_CHIPNAME.dap -ap-num 0 -baseaddr [lindex $_CTIBASE $_core]&lt;br /&gt;
        target create $_TARGETNAME aarch64 -dap $_CHIPNAME.dap -ap-num 0 -dbgbase [lindex $_DBGBASE $_core] -cti $_CTINAME&lt;br /&gt;
&lt;br /&gt;
        set _smp_command &amp;quot;$_smp_command $_TARGETNAME&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if {$_USE_SMP} {&lt;br /&gt;
        eval $_smp_command&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Default target is cpu0&lt;br /&gt;
targets $_CHIPNAME.cpu0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing GNU Make ===&lt;br /&gt;
On Windows, a package for &#039;&#039;&#039;GNU Make&#039;&#039;&#039; is available, which can be conveniently installed via the integrated package manager. Open a terminal (Command Prompt or PowerShell) and enter the following command:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
winget install GnuWin32.Make&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program is installed by default in the directory &#039;&#039;&#039;C:\Program Files (x86)\GnuWin32\bin&#039;&#039;&#039;. To ensure that &#039;&#039;&#039;Make&#039;&#039;&#039; works smoothly with our other tools, copy the entire contents of this bin folder into our previously created directory &#039;&#039;&#039;C:\tools\bin&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Adjusting the &amp;quot;Path&amp;quot; Environment Variable ===&lt;br /&gt;
In order for Windows and Visual Studio Code to find the installed tools (compiler, Make, and OpenOCD) system-wide via the console, we need to add the executables to the system path.&lt;br /&gt;
&lt;br /&gt;
Press the &#039;&#039;&#039;Windows key&#039;&#039;&#039; and type &amp;quot;&#039;&#039;environment variables&#039;&#039;&amp;quot; in the search field.&lt;br /&gt;
&lt;br /&gt;
Select the option &amp;quot;&#039;&#039;Edit the system environment variables&#039;&#039;&amp;quot; and click the &amp;quot;&#039;&#039;Environment Variables...&#039;&#039;&amp;quot; button at the bottom of the next window.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path1.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
In the &amp;quot;&#039;&#039;User variables&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;System variables&#039;&#039;&amp;quot; section, look for the entry &#039;&#039;&#039;Path&#039;&#039;&#039; (or PATH) and select Edit.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path2.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
Add the following two paths to the list as separate, new lines:&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
C:\tools\bin&lt;br /&gt;
C:\tools\openocd\bin&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Confirm all open windows with &#039;&#039;&#039;OK&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Path3.png|300x300px]]&lt;br /&gt;
&lt;br /&gt;
The basic toolchain is now successfully installed and set up. In the next part, we will focus on configuring Visual Studio Code for the actual bare-metal project.&lt;br /&gt;
&lt;br /&gt;
=== Setting Up Visual Studio Code ===&lt;br /&gt;
Now that the basic toolchain is installed, we will set up the development environment in Visual Studio Code.&lt;br /&gt;
&lt;br /&gt;
First, complete the installation of Visual Studio Code using the installer you downloaded earlier.&lt;br /&gt;
  &lt;br /&gt;
==== Installing VS Code Extensions ====&lt;br /&gt;
Open Visual Studio Code. To make bare-metal development as comfortable as possible, we will first install some essential extensions. Click on the Extensions icon in the left menu bar (or press CTRL + SHIFT + X) and search for the following extensions:&lt;br /&gt;
&lt;br /&gt;
* C/C++ (by Microsoft): Provides syntax highlighting and code completion (IntelliSense) for C/C++.&lt;br /&gt;
* C/C++ Extension Pack (optional): Comes with additional useful tools for C development.&lt;br /&gt;
* Cortex-Debug (by marus25): The key extension that allows us to debug directly on the Raspberry Pi 5 via OpenOCD.&lt;br /&gt;
* Arm Assembly (by dan-c-underwood): Provides excellent support and highlighting for ARM assembly code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Erw1.png|200x200px]]&lt;br /&gt;
[[Datei:Erw2.png|200x200px]]&lt;br /&gt;
[[Datei:Erw3.png|200x200px]]&lt;br /&gt;
[[Datei:Erw4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Optional: Changing the User Interface Language to German ====&lt;br /&gt;
If your VS Code is in English and you prefer the German interface, for example:&lt;br /&gt;
&lt;br /&gt;
Press &#039;&#039;&#039;CTRL + SHIFT + P&#039;&#039;&#039; to open the Command Palette. Type &#039;&#039;Configure Display Language&#039;&#039; and press &#039;&#039;&#039;Enter&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;Deutsch&amp;quot; (German). (If it is not listed, you can install it directly from there). &lt;br /&gt;
&lt;br /&gt;
Restart VS Code.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Lang1.png|200x200px]]&lt;br /&gt;
[[Datei:Lang2.png|200x200px]]&lt;br /&gt;
[[Datei:Lang3.png|200x200px]]&lt;br /&gt;
[[Datei:Lang4.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating and Opening the Project Directory ====&lt;br /&gt;
Now create a directory on your hard drive where your bare-metal project will reside. In this example, we will use the path &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. In VS Code, select &#039;&#039;File -&amp;gt; Open Folder...&#039;&#039; and choose the directory &#039;&#039;&#039;D:\projekt1&#039;&#039;&#039;. Confirm the security prompt asking if you trust the authors of the folder by clicking &amp;quot;&#039;&#039;Yes, I trust the authors&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Proj1.png|200x200px]]&lt;br /&gt;
[[Datei:Proj2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Creating the .vscode Configuration Files ====&lt;br /&gt;
&lt;br /&gt;
To let VS Code know which compiler to use and how to start the debugger, we need to create a configuration directory. In the main directory of your project, create a new folder with the exact name &#039;&#039;&#039;.vscode&#039;&#039;&#039; (don&#039;t forget the dot at the beginning!). Right-click on the newly created &#039;&#039;&#039;.vscode&#039;&#039;&#039; folder and create the following three files one after the other.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Konf1.png|200x200px]]&lt;br /&gt;
[[Datei:Konf2.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
Copy the corresponding JSON code into each of them:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;: This file configures code completion (IntelliSense) so that VS Code understands the ARM-specific commands and headers.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;includePath&amp;quot;: [&lt;br /&gt;
                &amp;quot;${workspaceFolder}/include&amp;quot;,&lt;br /&gt;
                &amp;quot;${workspaceFolder}/src&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;compilerPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gcc.exe&amp;quot;,&lt;br /&gt;
            &amp;quot;cStandard&amp;quot;: &amp;quot;c11&amp;quot;,&lt;br /&gt;
            &amp;quot;cppStandard&amp;quot;: &amp;quot;c++14&amp;quot;,&lt;br /&gt;
            &amp;quot;intelliSenseMode&amp;quot;: &amp;quot;windows-gcc-arm64&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;version&amp;quot;: 4&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;launch.json&#039;&#039;&#039;: This file controls the debugger (Cortex-Debug). It ensures that your code is loaded onto the Raspberry Pi 5 via OpenOCD and that the processor is stopped exactly at the start address.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;0.2.0&amp;quot;,&lt;br /&gt;
    &amp;quot;configurations&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;Bare-Metal Debug (Pi 5)&amp;quot;,&lt;br /&gt;
            &amp;quot;cwd&amp;quot;: &amp;quot;${workspaceFolder}&amp;quot;,&lt;br /&gt;
            &amp;quot;executable&amp;quot;: &amp;quot;${workspaceFolder}/kernel_2712.elf&amp;quot;,&lt;br /&gt;
            &amp;quot;request&amp;quot;: &amp;quot;launch&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;cortex-debug&amp;quot;,&lt;br /&gt;
            &amp;quot;runToEntryPoint&amp;quot;: &amp;quot;0x80000&amp;quot;,&lt;br /&gt;
            &amp;quot;servertype&amp;quot;: &amp;quot;openocd&amp;quot;,&lt;br /&gt;
            &amp;quot;searchDir&amp;quot;: [&lt;br /&gt;
                &amp;quot;C:/tools/openocd/openocd/scripts&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;configFiles&amp;quot;: [&lt;br /&gt;
                &amp;quot;interface/cmsis-dap.cfg&amp;quot;,&lt;br /&gt;
                &amp;quot;target/bcm2712.cfg&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;gdbPath&amp;quot;: &amp;quot;C:/tools/bin/aarch64-none-elf-gdb.exe&amp;quot;, &lt;br /&gt;
            &amp;quot;serverpath&amp;quot;: &amp;quot;C:/tools/openocd/bin/openocd.exe&amp;quot;, &lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;openOCDLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;transport select swd&amp;quot;,&lt;br /&gt;
                &amp;quot;adapter speed 1000&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
&lt;br /&gt;
            // Overrides the default &amp;quot;reset halt&amp;quot; command from VS Code&lt;br /&gt;
            &amp;quot;overrideResetCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            // Commands that are executed directly after connecting&lt;br /&gt;
            &amp;quot;overrideLaunchCommands&amp;quot;: [&lt;br /&gt;
                &amp;quot;monitor halt&amp;quot;,&lt;br /&gt;
                &amp;quot;load&amp;quot;,&lt;br /&gt;
                &amp;quot;monitor reg pc 0x80000&amp;quot; // Forces the processor directly to the start address&lt;br /&gt;
            ],&lt;br /&gt;
            &lt;br /&gt;
            &amp;quot;preLaunchTask&amp;quot;: &amp;quot;Compile&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;tasks.json&#039;&#039;&#039;: This defines the automated build process. Before the debugger starts, this task automatically calls make.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tasks&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;label&amp;quot;: &amp;quot;Compile&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;shell&amp;quot;,&lt;br /&gt;
            &amp;quot;command&amp;quot;: &amp;quot;make&amp;quot;,&lt;br /&gt;
            &amp;quot;args&amp;quot;: [&amp;quot;all&amp;quot;],&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;env&amp;quot;: {&lt;br /&gt;
                    &amp;quot;PATH&amp;quot;: &amp;quot;${env:PATH};C:\\tools\\bin&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;group&amp;quot;: {&lt;br /&gt;
                &amp;quot;kind&amp;quot;: &amp;quot;build&amp;quot;,&lt;br /&gt;
                &amp;quot;isDefault&amp;quot;: true&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;problemMatcher&amp;quot;: [&amp;quot;$gcc&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Save all configurations by clicking &#039;&#039;File -&amp;gt; Save All&#039;&#039; in the menu.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AllesSpeichern.png|200x200px]]&lt;br /&gt;
[[Datei:Konfübersicht.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
==== Important: Note on Apparent Error Messages ====&lt;br /&gt;
After saving the &#039;&#039;&#039;c_cpp_properties.json&#039;&#039;&#039;, you will probably notice a small red &amp;quot;&#039;&#039;&#039;2&#039;&#039;&#039;&amp;quot; in the file explorer or in the &amp;quot;Problems&amp;quot; tab (at the bottom of VS Code). This indicates active warnings. If you click on the message, you will see the reason: VS Code is complaining that the two directory paths &#039;&#039;&#039;${workspaceFolder}/include&#039;&#039;&#039; and &#039;&#039;&#039;${workspaceFolder}/src&#039;&#039;&#039; do not exist.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Prob1.png|200x200px]]&lt;br /&gt;
&lt;br /&gt;
💡Don&#039;t worry: This is completely correct! Since we started with an empty project, these folders simply don&#039;t exist yet. As soon as you create the &#039;&#039;&#039;src&#039;&#039;&#039; and &#039;&#039;&#039;include&#039;&#039;&#039; folders later in your project, this warning will disappear automatically.&lt;br /&gt;
&lt;br /&gt;
Tip: If VS Code does not immediately register newly created folders, you can quickly refresh the development environment. To do this, press &#039;&#039;&#039;F1&#039;&#039;&#039;, type &#039;&#039;Reload Window&#039;&#039;, and confirm with &#039;&#039;&#039;Enter&#039;&#039;&#039;. This reloads the interface without interrupting your work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/types.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// types.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_types_h&lt;br /&gt;
#define _ms_types_h&lt;br /&gt;
&lt;br /&gt;
typedef unsigned char    u8;&lt;br /&gt;
typedef unsigned short   u16;&lt;br /&gt;
typedef unsigned int     u32;&lt;br /&gt;
&lt;br /&gt;
typedef signed char      s8;&lt;br /&gt;
typedef signed short     s16;&lt;br /&gt;
typedef signed int       s32;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    u64;&lt;br /&gt;
typedef signed long      s64;&lt;br /&gt;
&lt;br /&gt;
typedef long             intptr;&lt;br /&gt;
typedef unsigned long    uintptr;&lt;br /&gt;
&lt;br /&gt;
typedef unsigned long    size_t;&lt;br /&gt;
typedef long             ssize_t;&lt;br /&gt;
&lt;br /&gt;
typedef char             boolean;&lt;br /&gt;
&lt;br /&gt;
#define ALIGN(n)    __attribute__((aligned (n)))&lt;br /&gt;
&lt;br /&gt;
#define FALSE       0&lt;br /&gt;
#define TRUE        1&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;include/util.h&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
// util.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#ifndef _ms_util_h&lt;br /&gt;
#define _ms_util_h&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;types.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void write32(u64 a, u32 b);&lt;br /&gt;
u32 read32(u64 a);&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as all header files are saved in the include folder, the red lines in VS Code will disappear automatically. The GUI has successfully recognized the references.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Null prob.png|200x200px]]&lt;/div&gt;</summary>
		<author><name>Satyria</name></author>
	</entry>
</feed>