Unser erstes Programm in C (PI4)

Aus C und Assembler mit Raspberry

Die Programmierung des Raspberry Pi 4 wird ähnlich durchgeführt, wie die Programmierung des Raspberry's Pi 5. Ich habe hierzu den den Kurs dort herauskopiert und es entsprechend angepasst. Ich habe es aus den Gründen gemacht, da ich USB unterstützen möchte und es inzwischen mehr Informationen zum RPI4 gibt, als für den RPI5.

Idealerweise hatte ich dort ein Terminal programmiert, welches ich sehr gut zum Debuggen verwenden kann. In diesem Sinne, kann ich den Kurs entsprechend hier auch einführen.


Unser erstes Programm wird zunächst nichts tun. Es wird einfach eine Dauerschleife durchlaufen. Dies dient als Grundbaustein für alle weiteren Versuche. Ich werde erklären, wie ein solches Programm erstellt, kompiliert und ausgeführt wird. Zunächst schauen wir, ob alles funktioniert.

Sourcecode erstellen

Leider kommen wir bei BareMetal mit C nicht an Assembler vorbei. Auch wenn das Assemblerprogramm zur Zeit nicht wirklich viel tut, wird es später zum Beispiel für die Interrupt-Progrogramierung benötigt.

Öffnen Sie ein Textprogramm, um unser erstes Assemblerprogramm zu schreiben:

//
// The first program for RPI4
// 20.02.2025 www.satyria.de
//

.section .init      // Ensure the linker places this at the beginning of the kernel image
.globl _start       // Generates a global label
_start:             // The label _start (entry address)

  mov sp, #0x80000  // Create a stack of 512KB (524288 bytes)
  b main            // Branch to "main"

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.

Da wir ja in C programmieren wollen schreiben wir noch ein kleines main:

//
// main.c
// The first program for RPI4
// 20.02.2025 www.satyria.de
//

int main (void)
{
  while (1){}
}

Hier wird einfach eine Endlosschleife erzeugt.

Kompilieren des Programms mit make

Um nun unser Programm zu Kompilieren, muss zunächst die Programmierumgebung eingerichtet werden. Verwende dazu folgende Anleitungen. Diese funktionieren auch für den RPI4:

Programmierumgebung erstellen (Konsole) Programmierumgebung erstellen (64-Bit)

Wenn wir dies nun erstellt haben, können wir ein Makefile erstellen, welches wir für den RPI4 angepasst haben:

CSRCS := $(wildcard *.c)
CPPSRCS := $(wildcard *.cpp)
ASRCS := $(wildcard *.S)
COBJS := $(CSRCS:.c=.o)
CPPOBJS := $(CPPSRCS:.cpp=.o)
AOBJS := $(ASRCS:.S=.o)
AllOBJS := $(COBJS) $(CPPOBJS) $(AOBJS)
LOADADDR = 0x80000

GCCFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -O0 -ffreestanding \
           -nostartfiles -nostdlib -nostdinc -g -I ./include

AFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian  -I ./include -O0 -g

CFLAGS = -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char -ffreestanding -g \
         -I ./include -O0 -fno-exceptions 

CPPFLAGS = -fno-exceptions -fno-rtti -nostdinc++ -DAARCH=64 -mcpu=cortex-a72 -mlittle-endian -Wall -fsigned-char \
			  -ffreestanding -g -I ./include -O0 -mstrict-align -std=c++14 -Wno-aligned-new

all: clean new kernel8.img

%.o: %.S
	@echo "as $@"
	@aarch64-none-elf-gcc $(AFLAGS) -c $< -o $@

%.o: %.c
	@echo "gcc $@"
	@aarch64-none-elf-gcc $(CFLAGS) -c $< -o $@

%.o: %.cpp
	@echo "g++ $@"
	@aarch64-none-elf-g++ $(CPPFLAGS) -c $< -o $@

kernel8.img: $(AllOBJS)
	@echo "============================================================================="
	@echo "Linking..."
	@aarch64-none-elf-ld -o kernel8.elf -Map kernel8.map -nostdlib \
		--section-start=.init=$(LOADADDR) --no-warn-rwx-segments \
		-g -T linker.ld $(AllOBJS)
	aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img

clean:
	/bin/rm -f kernel8.elf kernel8.map *.o *.img > /dev/null 2> /dev/null || true

new:
	/bin/clear

Speicher diese Datei als "makefile" ab. Wenn du mehr über den Inhalt erfahren möchtest, so schaue unter Arbeiten mit Make und Linker-Script nach.

Was nun noch fehlt, ist ein Linker-Script, welches wir bereits auch unter Arbeiten mit Make und Linker-Script beschrieben haben:

ENTRY(_start)

SECTIONS
{
	.init : {
		*(.init)
	}
	.text : {
		*(.text*)
		_etext = .;
	}
	.rodata : {
		*(.rodata*)
	}
	.init_array : {
		__init_start = .;
		KEEP(*(.init_array*))
		__init_end = .;
	}
	.ARM.exidx : {
		__exidx_start = .;
		*(.ARM.exidx*)
		__exidx_end = .;
	}
	.eh_frame : {
		*(.eh_frame*)
	}
	.data : {
		*(.data*)
	}
	.bss : {
		__bss_start = .;
		*(.bss*)
		*(COMMON)
		__bss_end = .;
	}
}
__bss_size = (__bss_end - __bss_start) >> 3;

Speichere diese Datei als "linker.ld" ab.

Nun können wir unser erstes Programm kompilieren:

make

Damit unser Kernel nun funktionsfähig ist, muss der Kernel auf die SD-Karte, die in FAT32 formatiert ist, kopiert werden. Zusätzlich benötigt der Raspberry Pi 4 folgende Dateien auf der SD-Karte:

LISTE!!!

Du kannst den Source-Code als ZIP-Datei mit folgenden Link downloaden: https://www.satyria.de/arm/sources/RPI4/C/1.zip


< Hauptseite > Weiter (Lass die LED leuchten in C (PI4)) >