如何在树莓派上运行没有操作系统的 C 程序? [英] How to run a C program with no OS on the Raspberry Pi?

查看:28
本文介绍了如何在树莓派上运行没有操作系统的 C 程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想尝试将 Raspberry Pi 用于一些不同的低级嵌入式应用程序.唯一的问题是,与可用的 AVR 和 PIC 微控制器板不同,Raspberry Pi 通常运行一个操作系统(如 Raspbian),该操作系统将 CPU 时间分配给所有正在运行的程序,这使得它对某些实时应用程序不切实际.

我最近了解到,假设您安装了像 GRUB 这样的引导程序,在 x86 上运行 C 程序

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

开始.S

.global _start_开始:mov sp, #0x8000主线悬挂:b挂

main.c

#include /* 这不好.任何严重的事情都应该使用计时器* 由董事会提供.但这使代码更简单.*/#define BUSY_WAIT __asm__ __volatile__("")#define BUSY_WAIT_N 0x100000int 主(无效){uint32_t i;/* 在底层,一切都是通过写入魔术内存地址来完成的.硬件供应商提供的设备树文件(dtb/dts),告诉 Linux 内核这些魔法值.*/易失性 uint32_t * const GPFSEL4 = (uint32_t *)0x3F200010;易失性 uint32_t * const GPFSEL3 = (uint32_t *)0x3F20000C;易失性 uint32_t * const GPSET1 = (uint32_t *)0x3F200020;易失性 uint32_t * const GPCLR1 = (uint32_t *)0x3F20002C;*GPFSEL4 = (*GPFSEL4 & ~(7 << 21)) |(1<<21);*GPFSEL3 = (*GPFSEL3 & ~(7 << 15)) |(1<<15);而 (1) {*GPSSET1 = 1 <<(47 - 32);*GPCLR1 = 1<<(35 - 32);for (i = 0; i 

ldscript

内存{内存:原点 = 0x8000,长度 = 0x10000}部分{.text : { *(.text*) } >内存.bss : { *(.bss*) } >内存}

make.sh

#!/usr/bin/env bash设置 -edev="${1:-/dev/mmcblk0}"部分="${2:-p1}"part_dev="${dev}${part}"mnt='/mnt/rpi'sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi# 生成kernel7.imgarm-none-eabi-as start.S -o start.oarm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.oarm-none-eabi-ld start.o main.o -T ldscript -o main.elf# 从生成的 elf 文件中获取原始程序集.arm-none-eabi-objcopy main.elf -O binary kernel7.img# 获取固件.那些只是魔法斑点,可能是编译过的# 来自一些我们无法访问的 Broadcom 专有 C 代码.wget -O bootcode.bin https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/bootcode.bin?raw=truewget -O start.elf https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/start.elf?raw=true# 准备文件系统.须藤卸载$part_dev"echo 'start=2048, type=c' |须藤 sfdisk "$dev"须藤 mkfs.vfat "$part_dev"须藤 mkdir -p "$mnt"须藤挂载${part_dev}"$mnt"须藤cp kernel7.img bootcode.bin start.elf "$mnt"# 清理.同步须藤 umount "$mnt"

QEMU 友好的裸机示例

闪光灯的问题是在 QEMU 中很难观察 LED:https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of-the-leds-and-gpios-in-a-qemu-emulation-like-t

这里我描述了一些可能感兴趣的裸机 QEMU 设置:如何制作裸机 ARM 程序并在 QEMU 上运行它们? 写入 UART 是从 QEMU 中获取输出的最简单方法.

QEMU 模拟 Raspberry Pi 的效果可以部分推断出:如何使用 QEMU 模拟 Raspberry Pi Raspbian? 因为即使是 Linux 终端也会出现,所以您的裸机很可能也可以工作.

奖金

这是一个 x86 示例:如何在没有操作系统的情况下运行程序?

I'd like to experiment using the Raspberry Pi for some different low level embedded applications. The only problem is that, unlike the AVR and PIC microcontroller boards available, Raspberry Pi typically runs an OS (like Raspbian) that distributes CPU time across all running programs and makes it impractical for certain real time applications.

I've recently learned that, assuming you have a bootloader like GRUB installed, running a C program on x86 (in the form of a kernel) takes very little actual setup, just an assembly program to call the main function and the actual C code.

Is there a way to achieve this with a Raspberry Pi? It'd be a great way to learn about low level ARM programming, and it already has a few complex peripherals to mess around with (USB, Ethernet, etc.)

解决方案

Fully automated minimal bare metal blinker example

Tested on Ubuntu 16.04 host, Raspberry Pi 2.

https://github.com/dwelch67/raspberrypi is the most comprehensive example set I've seen to date (previously mentioned on this now deleted answer), but this is a minimal easy to setup hello world to get you started quickly.

Usage:

  1. Insert SD card on host

  2. Make the image:

    ./make.sh /dev/mmblck0 p1
    

    Where:

    • /dev/mmblck0 is the device of the SD card
    • p1 is the first partition of the device (/dev/mmblck0p1)
  3. Inset SD card on PI

  4. Turn power off and on

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

main.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
}

make.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 friendly bare metal examples

The problem with the blinker is that it is hard to observe LEDs in QEMU: https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of-the-leds-and-gpios-in-a-qemu-emulation-like-t

Here I describe some bare metal QEMU setups that may be of interest: How to make bare metal ARM programs and run them on QEMU? Writing to the UART is the easiest way to get output out from QEMU.

How well QEMU simulates the Raspberry Pi can be partially inferred from: How to emulate Raspberry Pi Raspbian with QEMU? Since even the Linux terminal shows up, it is likely that your baremetal stuff will also work.

Bonus

Here is an x86 example for the curious: How to run a program without an operating system?

这篇关于如何在树莓派上运行没有操作系统的 C 程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆