Wie führe ich ein C-Programm ohne Betriebssystem auf dem Raspberry Pi aus?

Lesezeit: 6 Minuten

Benutzeravatar von watswat5
watswat5

Ich würde gerne mit dem Raspberry Pi für einige verschiedene eingebettete Anwendungen auf niedriger Ebene experimentieren. Das einzige Problem ist, dass Raspberry Pi im Gegensatz zu den verfügbaren AVR- und PIC-Mikrocontroller-Boards normalerweise ein Betriebssystem (wie Raspbian) ausführt, das die CPU-Zeit auf alle laufenden Programme verteilt und es für bestimmte Echtzeitanwendungen unpraktisch macht.

Ich habe das kürzlich gelernt, vorausgesetzt, Sie haben einen Bootloader wie GRUB installiert, der ein C-Programm auf x86 ausführt (in Form eines Kernels) erfordert sehr wenig Einrichtung, nur ein Assemblerprogramm zum Aufrufen der Hauptfunktion und des eigentlichen C-Codes.

Gibt es eine Möglichkeit, dies mit einem Raspberry Pi zu erreichen? Es wäre eine großartige Möglichkeit, etwas über die Low-Level-ARM-Programmierung zu lernen, und es hat bereits ein paar komplexe Peripheriegeräte, mit denen man herumspielen kann (USB, Ethernet usw.).

  • Wenn Sie nicht einmal einen minimalen Linux-Kernel verwenden, müssten Sie dann nicht am Ende eine ganze Reihe von Kernel-Funktionen schreiben, nur um mit allen Peripheriegeräten zu interagieren?

    – Wut

    24. April 2015 um 2:51 Uhr

  • Nicht wirklich. Es hängt davon ab, was Sie tun möchten. Wenn Sie nur ein Licht blinken lassen möchten, brauchen Sie wirklich nicht viel Code. Aber mit USB, Ethernet usw. zu sprechen, wäre eine Menge Arbeit.

    – Jo

    24. April 2015 um 2:53 Uhr

  • Können Sie nicht einfach ein paar leichtgewichtige Echtzeitbetriebssysteme ausprobieren, bevor Sie eine Entscheidung treffen?

    – Benutzer3528438

    24. April 2015 um 2:57 Uhr

  • solltest du auschecken raspberrypi.stackexchange.com/questions/1408/… bevor Sie das Betriebssystem löschen. Auf PICs usw. läuft kein Betriebssystem, weil Sie oft keinen Platz dafür haben! auf dem Pi, und es gibt Möglichkeiten, in Echtzeit zu laufen

    – Keith Nicholas

    24. April 2015 um 3:23 Uhr

  • @KeithNicholas, es gibt viele Gründe, kein Betriebssystem auszuführen. Sie können Zyklen nicht genau zählen oder etwas mit Zyklusgenauigkeit entwickeln, wenn Sie das Betriebssystem um Erlaubnis bitten müssen.

    – cbmeeks

    15. August 2017 um 13:03 Uhr

Ciro Santilli Benutzeravatar von OurBigBook.com
Ciro Santilli OurBigBook.com

Vollautomatisches Beispiel für einen minimalen Bare-Metal-Blinker

Getestet auf Ubuntu 16.04-Host, Raspberry Pi 2.

https://github.com/dwelch67/raspberrypi ist das umfassendste Beispielset, das ich bisher gesehen habe (zuvor in dieser jetzt gelöschten Antwort erwähnt), aber dies ist eine minimale, einfach einzurichtende Hallo-Welt, damit Sie schnell loslegen können.

Verwendungszweck:

  1. Legen Sie die SD-Karte in den Host ein

  2. Machen Sie das Bild:

    ./make.sh /dev/mmblck0 p1
    

    Wo:

    • /dev/mmblck0 ist das Gerät der SD-Karte
    • p1 ist die erste Partition des Geräts (/dev/mmblck0p1)
  3. SD-Karte in PI einlegen

  4. Schalten Sie den Strom aus und wieder ein

Geben Sie hier die Bildbeschreibung ein

GitHub-Upstream: https://github.com/cirosantilli/raspberry-pi-bare-metal-blinker/tree/d20f0337189641824b3ad5e4a688aa91e13fd764

start.S

.global _start
_start:
    mov sp, #0x8000
    bl main
hang:
    b hang

Haupt c

#include <stdint.h>

/* This is bad. Anything remotely serious should use timers
 * provided by the board. But this makes the code simpler. */
#define BUSY_WAIT __asm__ __volatile__("")
#define BUSY_WAIT_N 0x100000

int main( void ) {
    uint32_t i;
    /* At the low level, everything is done by writing to magic memory addresses.
    The device tree files (dtb / dts), which are provided by hardware vendors,
    tell the Linux kernel about those magic values. */
    volatile uint32_t * const GPFSEL4 = (uint32_t *)0x3F200010;
    volatile uint32_t * const GPFSEL3 = (uint32_t *)0x3F20000C;
    volatile uint32_t * const GPSET1  = (uint32_t *)0x3F200020;
    volatile uint32_t * const GPCLR1  = (uint32_t *)0x3F20002C;

    *GPFSEL4 = (*GPFSEL4 & ~(7 << 21)) | (1 << 21);
    *GPFSEL3 = (*GPFSEL3 & ~(7 << 15)) | (1 << 15);
    while (1) {
        *GPSET1 = 1 << (47 - 32);
        *GPCLR1 = 1 << (35 - 32);
        for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
        *GPCLR1 = 1 << (47 - 32);
        *GPSET1 = 1 << (35 - 32);
        for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
    }
}

ldscript

MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 0x10000
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}

machen.sh

#!/usr/bin/env bash

set -e

dev="${1:-/dev/mmcblk0}"
part="${2:-p1}"
part_dev="${dev}${part}"
mnt="/mnt/rpi"

sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi

# Generate kernel7.img
arm-none-eabi-as start.S -o start.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.o
arm-none-eabi-ld start.o main.o -T ldscript -o main.elf
# Get the raw assembly out of the generated elf file.
arm-none-eabi-objcopy main.elf -O binary kernel7.img

# Get the firmware. Those are just magic blobs, likely compiled
# from some Broadcom proprietary C code which we cannot access.
wget -O bootcode.bin https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/bootcode.bin?raw=true
wget -O start.elf https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/start.elf?raw=true

# Prepare the filesystem.
sudo umount "$part_dev"
echo 'start=2048, type=c' | sudo sfdisk "$dev"
sudo mkfs.vfat "$part_dev"
sudo mkdir -p "$mnt"
sudo mount "${part_dev}" "$mnt"
sudo cp kernel7.img bootcode.bin start.elf "$mnt"

# Cleanup.
sync
sudo umount "$mnt"

QEMU-freundliche Bare-Metal-Beispiele

Das Problem mit dem Blinker ist, dass es schwierig ist, LEDs in QEMU zu beobachten: https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of-the-leds-and-gpios-in-a-qemu-emulation-like-t

Hier beschreibe ich einige Bare-Metal-QEMU-Setups, die von Interesse sein könnten: Wie erstellt man Bare-Metal-ARM-Programme und führt sie auf QEMU aus? Das Schreiben auf den UART ist der einfachste Weg, um eine Ausgabe von QEMU zu erhalten.

Wie gut QEMU den Raspberry Pi simuliert, lässt sich teilweise aus: How to emulate Raspberry Pi Raspbian with QEMU? Da sogar das Linux-Terminal auftaucht, ist es wahrscheinlich, dass Ihr Baremetal-Zeug auch funktioniert.

Bonus

Hier ist ein x86-Beispiel für Neugierige: Wie führt man ein Programm ohne Betriebssystem aus?

Obwohl Bare Metal auf dem Pi möglich ist, würde ich es vermeiden, da Linux so leichtgewichtig wird und eine ganze Reihe von Dingen für Sie erledigt.

Hier ist ein Tutorial für den Einstieg, wenn Sie noch Bare-Metal-Sachen lernen möchten: http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/

Nach alledem würde ich einfach Ihre bevorzugte eingebettete Linux-Distribution laden (RT-gepatcht könnte je nach Ihren Anforderungen bevorzugt werden) und es als gut bezeichnen.

  • Danke für die Information. Ich werde wahrscheinlich vorerst die Bare-Metal-Programmierung verwenden, da das Projekt keine Zeitbeschränkungen hat, aber ich werde die eingebettete Linux-Distribution im Auge behalten!

    – watswat5

    24. April 2015 um 3:18 Uhr

  • Leider wurde Valvers offline genommen, weil es zu nützlich war. Sie müssen jetzt über den Google-Cache darauf zugreifen :/

    – Eule

    5. März 2019 um 15:03 Uhr

https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ ist ein großartiges Tutorial, und da sie Ihnen sagen werden, dass der beste schnelle und schmutzige Weg, Code auf Bare Metal auszuführen, darin besteht, eine Linux-Distribution zu entführen, kompilieren Sie dazu einfach in kernel.img (mit den entsprechenden Architekturoptionen) und verwenden Sie es es, um das vorhandene in der Linux-Distribution zu ersetzen, nur für diesen Abschnitt des Tutorials, zu dem Sie gehen können:
https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html#pitime

Der Pi ist möglicherweise etwas suboptimal für das, was Sie tun möchten, da das SoC-Design so ist, dass die ARM-CPU ein Bürger zweiter Klasse ist – was bedeutet, dass es einige Reifen gibt, durch die man springen muss, um ein Bare-Metal-Programm darauf laufen zu lassen.

Allerdings könnte man ein bisschen schummeln und das nutzen U-Boot-API um Ihnen Zugriff auf einige der Funktionen zu geben, die U-Boot bietet, aber in der Lage zu sein, Ihre eigenen Funktionen nebenbei hinzuzufügen.

1400410cookie-checkWie führe ich ein C-Programm ohne Betriebssystem auf dem Raspberry Pi aus?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy