Kampis Elektroecke

Am Anfang war die LED…

Raspberry Pi

In diesem Artikel geht es darum, eine LED zu aktivieren und sie blinken zu lassen. Für ein besseres Verständnis ist es ratsam das BCM2835 Peripherie Datenblatt zur Hand zu haben, da für die Lösung der Aufgabenstellung einige Registeradressen benötigt werden.
Es kann außerdem nicht schaden, wenn ihr zusätzlich das ARM Befehlsdatenblatt offen habt. In diesem Dokument sind alle verfügbaren ARM Befehlscodes vermerkt.

Ok los geht´s…

Die Ordnerstruktur:

Für die spätere Kompilierung wird ein Makefile benötigt, welches der Cross Compiler verwendet. Alle benötigten Dateien sind in diesem Ordnertemplate zu finden. Das Template kann heruntergeladen und als Ausgangsverzeichnis verwendet werden.
In diesem Ordner findet ist ein Verzeichnis mit dem Namen source zu finden. Hier werden alle Assemblerdateien abgelegt und der Inhalt dieses Verzeichnisses wird dann während des Compilierens durch das Tool make des abgearbeitet und zu einem Kernelimage gelinkt.

Genauere Informationen hierzu stehen in der Datei Makefile, welche wir uns mal anschauen.
Diese Datei kann z. B. mit Programmer´s Notepad geöffnet werden. Wirklich wichtig sind für uns nur die ersten paar Punkte, da ich (noch) nicht so tief in den Aufbau eines Make-Files einsteigen will:

Der erste Punkt gibt den verwendeten Compiler an. In diesem Fall ist es der Compiler arm-none-eabi für eine sogenannte bare metall Programmierung. Als bare Metall Programmierung wird das Programmieren ohne Betriebssystem bezeichnet. Man startet ohne jegliche Software und Schnittstellen und baut sich alles selber zusammen.
Der zweite und der dritte Punkt gibt das Verzeichnis für die kompilierten Objekte bzw. Sourcefiles an. Dabei wird das Verzeichnis des Makefiles als Ausgangspunkt verwendet.
Im vierten Punkt legt man den Namen des erzeugen Kernelimages fest. Der Name muss immer kernel.img heißen!
Die nächsten beiden Punkte geben die Dateinamen des Listingfiles und des Mapfiles an und interessieren uns nicht wirklich.
Und der letzte Punkt gibt den verwendeten Linker an. Der Linker sorgt dafür, dass alle verwendeten Programmteile zu einem ausführbaren Programm zusammen gesetzt werden.

Eine LED einschalten:

Für das erste Programm wird im Programmer´s Notepad eine neue Assemblerdatei angelegt und als main.s gespeichert. Ziel ist es, dass wir eine LED an GPIO 24 aktivieren. 

Jetzt geht der spaßige Teil los!
Dazu wird das Datenblatt der BCM2835 Peripherie zu benötigt. Auf S. 89 beginnt die Sektion über die GPIO, die für die Ansteuerung der GPIO benötigt wird und auf S. 90 befindet sich die Registerübersicht. Zu allererst wird der GPIO über die Register GPFSELn als Ausgang konfiguriert. Wenn man das Datenblatt etwas weiter ließt, sieht man ab S. 92 das die GPIO unterteilt sind. Register 0 deckt die GPIO 0 – 9 ab, Register 1 die GPIO 10 – 19, usw.

Da der GPIO 24 angesteuert werden soll wird das Register GPFSEL2 benötigt. Ok, also wieder zurück scrollen auf die S. 90 für die Registerübersicht. Dort lässt sich entnehmen, dass das Register GPFSEL2 bei der Adresse 0x20000008 anfängt. Das Datenblatt zeigt eine genaue Auflistung der Unterteilung des Registers für GPIO 24. Um den GPIO 24 als Ausgang zu deklarieren, muss also in den Bereich FSEL24 der Wert 0x01 stehen.

Nachdem der GPIO als Ausgang deklariert wurde, kann der Ausgang über das GPSETn-Register gesetzt werden. In diesem Register repräsentiert jedes Bit für einen einzelnen I/O.

Hier ein kleiner Hinweis:
Das Thema Registeradressen ist leider etwas komplizierter. In der Registerübersicht des GPIO-Controllers steht, dass die Startadresse des GPIO-Controllers bei 0x7E200000 liegt. Bei dieser Adresse handelt es sich um eine virtuelle Speicheradresse, die üblicherweise von einem Betriebssystem genutzt und durch eine MMU auf eine physische Adresse gemappt wird. Ohne Betriebssystem ist man an die physikalischen Adressen des Chips gebunden. Eine Übersicht dazu findet sich auf S. 5 des Peripheriedatenblattes:

Da keine MMU genutzt wird, gelten die Adressen aus dem mittleren Adressbereich. Daraus ergibt sich die Adresse 0x20200000 und nicht 0x7E200000 für den GPIO-Controller (das FE wird zu 20).
Nun sind aber alle Komponenten vorhanden. Zeit um aus diesen Erkenntnissen Code zu generieren…

Die ersten drei Zeilen müssen immer gleich bleiben. Sie dienen quasi als Einstigspunkt für den Prozessor:

Im nächsten Schritt wird die Basisadresse bzw. die Anfangsadresse des GPIO Controllers im Register r0 gespeichert. Diese lautet 0x20200000 und ist in der Tabelle auf S. 90 zu finden (das Umrechnen der Adresse nicht vergessen!).

Mit den nächsten beiden Schritten wird der GPIO 24 als Ausgang deklariert. 

Jetzt wird der Wert in das entsprechende GPFSEL-Register geschrieben.

Dieser Befehl kopiert den Wert von Register r1 zu der in Register r0 und um den Wert 8 erhöhte Adresse.

Der nächste Schritt besteht darin, den Ausgang auf High zu setzen. Der Chip verfügt für diesen Fall über ein GPIO Pin Output Set Register (GPSETn). Jedes Register deckt bis zu 32 GPIO ab, wobei jedes Bit für den jeweiligen GPIO steht (Register 0 Bit 0 → GPIO 0, Register 0 Bit 1 → GPIO 1, etc.).
Um den GPIO 24 zu setzen muss also das Bit 24 des Registers GPSET0 gesetzt werden.

Es muss jetzt allerdings der Adressoffset auf 28 und die Anzahl der zu schiebenden Stellen auf 24 angepasst werden.
Als letztes wird noch eine Endlosschleife eingefügt, damit das Raspberry was zu tun hat und keine Fehler produziert:

Die Datei wird nun als main.s im source-Verzeichnis des Verzeichnistemplates abgespeichert. Der Kompiliervorgang kann nun über die Konsole und dem Befehl make gestartet werden:

Wenn der Kompiliervorgang erfolgreich beendet wurde, ist in dem Verzeichnis eine Datei namens kernel.img zu finden. Alles was jetzt noch benötigt wird ist eine SD-Karte mit einem bereits installierten Betriebssystem. Auf dieser SD-Karte wird die vorhandene Datei kernel.img durch die von euch erzeugte Datei kernel.img ersetzt.

Nun kann der Raspberry Pi eingeschaltet werden und kurz nach dem Einschalten leuchtet die LED an GPIO 24 auf.

Die LED blinken lassen:

Im nächsten Schritt soll die LED blinken. Dazu wird das erstellte Programm geringfügig abgeändert und die Endlosschleife erweitert:

Die neue Endlosschleife besteht aus einem Verzögerungsblock, wo der Wert 0x3F0000 in das Register r2 geladen und dann in einer Schleife so lange um 1 reduziert wird, bis der Wert 0x00 erreicht wird:

Sobald die Schleife durchgelaufen ist, wird die LED entweder an oder aus geschaltet. Es resultiert eine Blinkfrequenz von (etwa) 2 Hz. 

Das Projekt gibt es hier zum Download.

→ Zurück zu Raspberry Pi Assembler

24 Kommentare

  1. C:\Users\patrick\Desktop\template\template>make
    arm-none-eabi-ld –no-undefined -Map kernel.map -o build/output.elf -T kernel.l
    d
    process_begin: CreateProcess(NULL, arm-none-eabi-ld –no-undefined -Map kernel.m
    ap -o build/output.elf -T kernel.ld, …) failed.
    make (e=2): Das System kann die angegebene Datei nicht finden.
    make: *** [build/output.elf] Error 2

    was funktioniert denn hier nicht? die led.asm datei ist im source ordner ??
    Danke

      1. Hallo Kampi,
        danke für die rückmeldung, leider kommt jetzt eine andere fehlermeldung…

        C:\Users\patrick\Desktop\template\template>make
        mkdir build
        arm-none-eabi-as -I source/ source/main.s -o build/main.o
        process_begin: CreateProcess(NULL, arm-none-eabi-as -I source/ source/main.s -o
        build/main.o, …) failed.
        make (e=2): Das System kann die angegebene Datei nicht finden.
        make: *** [build/main.o] Error 2

        leider werde ich aus dieser fehlermeldung auch nicht schlau, da hier irgendwie garnicht angegeben ist, welche datei denn nun fehlt….

        Gruß

        1. Hallo Patrick,

          habe es gerade mal getestet.
          Als Software brauchst du:

          http://www.emb4fun.de/archive/gabmt/download/yagarto-tools-20121018-setup.exe

          http://sourceforge.net/projects/yagarto/

          Sowie MiniGW als Compiler

          http://sourceforge.net/projects/mingw/files/MSYS/Base/msys-core/msys-1.0.10/MSYS-1.0.10.exe/download

          Danach öffnest du die Konsole, wechselst in das Verzeichnis und gibst „make“ ein (musst den build-Ordner vorher aber löschen).

          Gruß
          Daniel

  2. Hallo Daniel,

    vielen Dank für die wirklich schnelle Antwort und entschuldige bitte, dass ich schon wieder störe. Aller Anfang ist schwer. Beim Compiilieren erhalte ich folgende Fehlermeldungen:
    C:\ASM>make
    MAKE Version 5.2 Copyright (c) 1987, 1998 Inprise Corp.
    ld –no-undefined -Map kernel.map -o build/output.elf -T kernel.ld
    Der Befehl „ld“ ist entweder falsch geschrieben oder konnte nicht gefunden werden.
    objcopy build/output.elf -O binary kernel.img
    Der Befehl „objcopy“ ist entweder falsch geschrieben oder konnte nicht gefunden werden.
    objdump -d build/output.elf > kernel.list
    Der Befehl „objdump“ ist entweder falsch geschrieben oder konnte nicht gefunden werden.

    Ich habe die 3 Tools mehrmals neu installiert und habe dabei nie eine Fehlermeldung erhalten. Was habe ich falsch gemacht? Im Ordner ASM liegen 3 Dateien und der Ordner source mit der Datei main.s .

      1. Hi Daniel,

        das ging ja extrem schnell – Danke!

        make – version wird beantwortet mit „Incorrect command line argument: – version“ und ca. 20 Optionen zur Syntax.
        Die zweite Antwort lautet:
        arm-none-eabi-gcc: error: unrecognized command line option ‚-version‘
        arm-none-eabi-gcc: fatal error: no input files
        compilation terminated.

        Gruß
        Bernd

          1. Hi Daniel,

            zur 1. Abfrage:
            MAKE Version 5.2 Copyright (c) 1987, 1998 Inprise Corp.
            Incorrect command line argument: –version
            und dann wieder die 20 Optionen
            zur 2. Abfrage:
            arm-none-eabi-gcc (GCC) 4.7.2
            Copyright (C) 2012 Free Software Foundation, Inc.
            This is free software; see the source for copying conditions. There is NO waranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

            Grüße
            Bernd

        1. Bitte die Windows Enviroment Variable PATH anschauen! Damit das „richtige“ make benutzt wird, muss das richtige make Verzeichnis am Anfang stehen.

  3. Hi Daniel,

    meine letzte Frage: An welcher Stelle erkennst Du was? Lasse Dir Zeit! Wichtig ist für mich nur das Ergebnis, dass ich den Cross Compiler nutzen kann.

    MfG
    Bernd

    1. Hallo Bernd,

      ich habe es gerade noch mal getestet.
      Du installierst die Tools (als Admin und mit den Defaultpfaden) und lädst dir mein Beispielprogramm runter.
      Das entpackst du z.B. bei dir auf dem Desktop und löscht die Dateien „kernel.img“, „kernel.list“, „kernel.ld“ und den Inhalt von „build“.
      Anschließend öffnest du eine neue Konsole (wenn du die bei der Installation der Tools offen hattest, musst du die einmal schließen, damit die PATH-Variable neu geladen wird) und wechselst in das Verzeichnis des Beispielprogrammes.
      Dort tippst du dann „make“ ein und dann sollte es funktionieren.

      Probier das mal bitte so und wenn es nicht klappt einfach noch mal melden (wenn du einen Linux Rechner hast, kannst du es auch damit machen…da ist es einfacher als mit Windows…).

      Gruß
      Daniel

      1. Hallo Daniel,

        habe alle 3 Tools nochmals als Admin in die Defaultpfade installiert. Dein Beispielprogramm in meinem Arbeitsordner entpackt, die 3 Kernel-Dateien gelöscht und auch die 2 Dateien im build ordner gelöscht. Die Windowseingabeaufforderung geöffnet, den Pfad zu meinem Arbeitsordner eingestellt und make eingetippt. Das Ergebnis ist:
        MAKE Version 5.2 Copyright (c) 1987,1998 Inprise Corp.
        Fatal: ‚kernel.ld‘ does not exist – don’t know how to make it
        Kopiere ich nun zusätzlich kernel.ld in meinen Arbeitsordner erscheint wieder die Fehlermeldung mit falsch geschrieben oder nicht gefunden.
        Vielleicht liegt’s an meinem System. Habe leider nur Win7 Ultimate 32bit. Oder es liegen noch alte Fragmente von den vorherigen Installationsversuchen im System. Werde heute Abend mal meinen XP-Laptop aktivieren. Es sieht aber eher nach einem Versionsproblem aus oder mir fehlt noch ein Tool.
        Warum geht’s bei Dir und bei mir nicht?

        Grüße
        Bernd

        1. Hallo Bernd,

          tut mir leid das du so viele Probleme hast :(
          Das ist natürlich schwer zu erkennen woran es liegt und ich kann dir auch nicht sagen warum es bei mir funktioniert.
          Aber das sieht mir nach einem Problem mit dem Linkerskript aus.
          Hast du eventuell Leerzeichen oder Sonderzeichen im Dateipfad drin? Weil das kann schon mal gerne Probleme verursachen.

          Wie gesagt….so ein Cross Compiler unter Windows ist ein Krampf schlechthin. Mittlerweile nutze ich ein Ubunu für den Cross Compile Vorgang, da es dafür zahlreiche Anleitungen gibt und es auch einfacher funktioniert wie ich finde…

          Gruß
          Daniel

          1. Hallo Daniel,
            nur zur Rückmeldung:
            Auch unter XP habe ich die gleichen Fehlermeldungen. Nur bei der Installation von MinGW mußte ich ein paar Fragen mehr beantworten. Gerade hab ich nochmals alles deinstalliert und neu angefangen. In der Konsole bei der MinGW Inst. fällt mir ein Abschnitt auf, der vielleicht die Ursache ist:
            c:\msys\1.0\postinstall>..\bin\sh.exe pi.sh
            AllocationBase 0x0, BaseAddress 0x715B0000, RegionSize 0xB0000, State 0x10000
            C:\msys\1.0\bin\sh.exe: *** Couldn’t reserve space for cygwin’s heap, Win32 error 0
            C:\msys\1.0\postinstall>pause
            Drücken Sie eine beliebige Taste . . .

            Mein Arbeitspfag lautet: C:\asm. Der sollte also auch keine Probleme machen. Vielleicht fällt Dir zur Fehlermeldung noch was ein.
            Vielen Dank für Deine Bemühungen

            Gruß
            Bernd

          2. Hey Bernd,

            mmh mir fällt leider auch nichts mehr ein :(
            Was spricht den gegen eine VM (z.B. mit VirtualBox) und einem Ubuntu als OS? Dann kannst du den Cross Compiler unter Linux nutzen…da kann ich dir mehr mit helfen und das funktioniert sehr oft auch viel besser.

            Gruß
            Daniel

  4. Hallo Daniel,

    tolle Seite, habe schon cross compiled und muß noch das led-image am Raspi ausprobieren. Installationen haben super geklappt. Großes Lob für die Beschreibung ein Crossentwicklungssystem unter Windows für ARM zu installieren.

    Zwei kleine Fehler sind mir aufgefallen in Deiner Beschreibung und einen Vorschlag hätte ich noch.
    1. Im Text: müssen wir also in den Bereich FSEL24 den Wert 010 rein schreiben.
    Korrektur: der richtige Wert müsste 001 sein.
    2. Im Text: Um den GPIO 24 zu löschen müssen wir laut dem Datenblatt also Bit 24 des Registers GPCLR1 setzen.
    Korrektur: Registers GPCLR0 setzen

    Vorschlag:
    3. Im Text: Jetzt wird die Datei gespeichert und dann eine Konsole geöffnet. In der Konsole müsst ihr nun in das Verzeichnis des Projektes wechseln:

    Noch einmal Danke für die tolle Anleitung
    bachitoph

    Vorschlag: Mein Template-Verzeichnis habe ich umbenannt und ist bei C:\Users\Daniel\Desktop\Raspberry Pi ARM\LED(1) zu finden. Jetzt wird die Datei in C:\Users\Daniel\Desktop\Raspberry Pi ARM\LED(1)\source als main.s gespeichert und dann eine Konsole geöffnet. In der Konsole müsst ihr nun in das Verzeichnis des Projektes wechseln:

  5. Hallo Daniel,

    ich habe dein Tutorial seit mehr als 3 Stunden offen und versuche das nachzuvollziehen. Meiner Meinung nach sieht alles gut aus. Nur leider kriege ich meinen Raspberry v2 nicht dzau die LED zum leuchten zu bringen.
    Benutze normal Raspian und habe die Kernel7.img und die Kernel datei ersetzt geht das damit?

      1. Hallo Daniel,

        danke für die HIlfe, mit einem Raspberry 1 habe ich es hinbekommen. Und habe schon erste Erweiterungen vorgenommen, an meinem Betriebssystem.

        7asper

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.